From: Paul Mackerras Date: Thu, 31 Dec 2020 04:27:11 +0000 (+1100) Subject: Merge pull request #183 from sthibaul/path-ip-up-down X-Git-Tag: ppp-2.4.9~34 X-Git-Url: https://git.ozlabs.org/?a=commitdiff_plain;h=506e9a3461ec4956215a2477cfde9c7233323173;hp=7afd9fdfe025a5c448d737a23d2e635e9cf6bd2d;p=ppp.git Merge pull request #183 from sthibaul/path-ip-up-down Add option to specify ip-up script --- diff --git a/pppd/eap.c b/pppd/eap.c index 92d29ec..ede5ff0 100644 --- a/pppd/eap.c +++ b/pppd/eap.c @@ -82,6 +82,11 @@ #ifdef USE_EAPTLS #include "eap-tls.h" #endif /* USE_EAPTLS */ +#ifdef CHAPMS +#include "magic.h" +#include "chap_ms.h" +#include "chap-new.h" +#endif /* CHAPMS */ eap_state eap_states[NUM_PPP]; /* EAP state; one for each unit */ #ifdef USE_SRP @@ -687,6 +692,9 @@ eap_figure_next_state(eap_state *esp, int status) } break; +#ifdef CHAPMS + case eapMSCHAPv2Chall: +#endif case eapMD5Chall: if (status != 0) { esp->es_server.ea_state = eapBadAuth; @@ -707,6 +715,162 @@ eap_figure_next_state(eap_state *esp, int status) #endif /* USE_EAPTLS */ } +#if CHAPMS +static int +eap_chapms2_verify_response(int id, char *name, + unsigned char *secret, int secret_len, + unsigned char *challenge, unsigned char *response, + char *message, int message_space) +{ + unsigned char md[MS_CHAP2_RESPONSE_LEN]; + char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; + int challenge_len, response_len; + + challenge_len = *challenge++; /* skip length, is 16 */ + response_len = *response++; + if (response_len != MS_CHAP2_RESPONSE_LEN) + goto bad; /* not even the right length */ + + /* Generate the expected response and our mutual auth. */ + ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name, + (char *)secret, secret_len, md, + (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR); + + /* compare MDs and send the appropriate status */ + /* + * Per RFC 2759, success message must be formatted as + * "S= M=" + * where + * is the Authenticator Response (mutual auth) + * is a text message + * + * However, some versions of Windows (win98 tested) do not know + * about the M= part (required per RFC 2759) and flag + * it as an error (reported incorrectly as an encryption error + * to the user). Since the RFC requires it, and it can be + * useful information, we supply it if the peer is a conforming + * system. Luckily (?), win98 sets the Flags field to 0x04 + * (contrary to RFC requirements) so we can use that to + * distinguish between conforming and non-conforming systems. + * + * Special thanks to Alex Swiridov for + * help debugging this. + */ + if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP], + MS_CHAP2_NTRESP_LEN) == 0) { + if (response[MS_CHAP2_FLAGS]) + slprintf(message, message_space, "S=%s", saresponse); + else + slprintf(message, message_space, "S=%s M=%s", + saresponse, "Access granted"); + return 1; + } + + bad: + /* + * Failure message must be formatted as + * "E=e R=r C=c V=v M=m" + * where + * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE) + * r = retry (we use 1, ok to retry) + * c = challenge to use for next response, we reuse previous + * v = Change Password version supported, we use 0 + * m = text message + * + * The M=m part is only for MS-CHAPv2. Neither win2k nor + * win98 (others untested) display the message to the user anyway. + * They also both ignore the E=e code. + * + * Note that it's safe to reuse the same challenge as we don't + * actually accept another response based on the error message + * (and no clients try to resend a response anyway). + * + * Basically, this whole bit is useless code, even the small + * implementation here is only because of overspecification. + */ + slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", + challenge_len, challenge, "Access denied"); + return 0; +} + +static struct chap_digest_type eap_chapms2_digest = { + CHAP_MICROSOFT_V2, /* code */ + NULL, /* chapms2_generate_challenge, */ + eap_chapms2_verify_response, + NULL, /* chapms2_make_response, */ + NULL, /* chapms2_check_success, */ + NULL, /* chapms_handle_failure, */ +}; + +/* + * eap_chap_verify_response - check whether the peer's response matches + * what we think it should be. Returns 1 if it does (authentication + * succeeded), or 0 if it doesn't. + */ +static int +eap_chap_verify_response(char *name, char *ourname, int id, + struct chap_digest_type *digest, + unsigned char *challenge, unsigned char *response, + char *message, int message_space) +{ + int ok; + unsigned char secret[MAXSECRETLEN]; + int secret_len; + + /* Get the secret that the peer is supposed to know */ + if (!get_secret(0, name, ourname, (char *)secret, &secret_len, 1)) { + error("No CHAP secret found for authenticating %q", name); + return 0; + } + + ok = digest->verify_response(id, name, secret, secret_len, challenge, + response, message, message_space); + memset(secret, 0, sizeof(secret)); + + return ok; +} + +/* + * Format and send an CHAPV2-Success/Failure EAP Request message. + */ +static void +eap_chapms2_send_request(eap_state *esp, u_char id, + u_char opcode, u_char chapid, + char *message, int message_len) +{ + u_char *outp; + int msglen; + + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_EAP); + + msglen = EAP_HEADERLEN + 5 * sizeof (u_char); + msglen += message_len; + + PUTCHAR(EAP_REQUEST, outp); + PUTCHAR(id, outp); + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_MSCHAPV2, outp); + PUTCHAR(opcode, outp); + PUTCHAR(chapid, outp); + /* MS len */ + PUTSHORT(msglen - 5, outp); + BCOPY(message, outp, message_len); + + output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); + + if (opcode == CHAP_SUCCESS) { + auth_peer_success(esp->es_unit, PPP_EAP, 0, + esp->es_server.ea_peer, esp->es_server.ea_peerlen); + } + else { + esp->es_server.ea_state = eapBadAuth; + auth_peer_fail(esp->es_unit, PPP_EAP); + } +} +#endif /* CHAPMS */ + /* * Format an EAP Request message and send it to the peer. Message * type depends on current state. (Server operation) @@ -792,6 +956,30 @@ eap_send_request(eap_state *esp) INCPTR(esp->es_server.ea_namelen, outp); break; +#ifdef CHAPMS + case eapMSCHAPv2Chall: + challen = 0x10; + esp->es_challen = challen; + esp->es_challenge[0] = challen; + random_bytes(&esp->es_challenge[1], challen); + + PUTCHAR(EAPT_MSCHAPV2, outp); + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(esp->es_server.ea_id, outp); + /* MS len */ + PUTSHORT(5 + challen + + esp->es_server.ea_namelen, + outp); + /* challen + challenge */ + BCOPY(esp->es_challenge, outp, challen+1); + INCPTR(challen+1, outp); + BCOPY(esp->es_server.ea_name, + outp, + esp->es_server.ea_namelen); + INCPTR(esp->es_server.ea_namelen, outp); + break; +#endif /* CHAPMS */ + #ifdef USE_EAPTLS case eapTlsStart: PUTCHAR(EAPT_TLS, outp); @@ -2018,6 +2206,12 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) struct eaptls_session *ets; u_char flags; #endif /* USE_EAPTLS */ +#ifdef CHAPMS + u_char opcode; + int (*chap_verifier)(char *, char *, int, struct chap_digest_type *, + unsigned char *, unsigned char *, char *, int); + char response_message[256]; +#endif /* CHAPMS */ /* * Ignore responses if we're not open @@ -2160,6 +2354,13 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) break; #endif /* USE_EAPTLS */ +#ifdef CHAPMS + case EAPT_MSCHAPV2: + info("EAP: peer proposes MSCHAPv2"); + esp->es_server.ea_state = eapMSCHAPv2Chall; + break; +#endif /* CHAPMS */ + default: dbglog("EAP: peer requesting unknown Type %d", vallen); switch (esp->es_server.ea_state) { @@ -2241,6 +2442,103 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); break; +#ifdef CHAPMS + case EAPT_MSCHAPV2: + if (len < 1) { + error("EAP: received MSCHAPv2 with no data"); + eap_figure_next_state(esp, 1); + break; + } + GETCHAR(opcode, inp); + len--; + + switch (opcode) { + case CHAP_RESPONSE: + if (esp->es_server.ea_state != eapMSCHAPv2Chall) { + error("EAP: unexpected MSCHAPv2-Response"); + eap_figure_next_state(esp, 1); + break; + } + /* skip MS ID + len */ + INCPTR(3, inp); + GETCHAR(vallen, inp); + len -= 4; + + if (vallen != MS_CHAP2_RESPONSE_LEN || vallen > len) { + error("EAP: Invalid MSCHAPv2-Response " + "length %d", vallen); + eap_figure_next_state(esp, 1); + break; + } + + /* Not so likely to happen. */ + if (len - vallen >= sizeof (rhostname)) { + dbglog("EAP: trimming really long peer name down"); + BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1); + rhostname[sizeof (rhostname) - 1] = '\0'; + } else { + BCOPY(inp + vallen, rhostname, len - vallen); + rhostname[len - vallen] = '\0'; + } + + /* In case the remote doesn't give us his name. */ + if (explicit_remote || + (remote_name[0] != '\0' && vallen == len)) + strlcpy(rhostname, remote_name, sizeof (rhostname)); + + if (chap_verify_hook) + chap_verifier = chap_verify_hook; + else + chap_verifier = eap_chap_verify_response; + + esp->es_server.ea_id += 1; + if ((*chap_verifier)(rhostname, + esp->es_server.ea_name, + id, + &eap_chapms2_digest, + esp->es_challenge, + inp - 1, + response_message, + sizeof(response_message))) + { + info("EAP: MSCHAPv2 success for peer %q", + rhostname); + esp->es_server.ea_type = EAPT_MSCHAPV2; + eap_chapms2_send_request(esp, + esp->es_server.ea_id, + CHAP_SUCCESS, + esp->es_server.ea_id, + response_message, + strlen(response_message)); + eap_figure_next_state(esp, 0); + if (esp->es_rechallenge != 0) + TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); + } + else { + warn("EAP: MSCHAPv2 failure for peer %q", + rhostname); + eap_chapms2_send_request(esp, + esp->es_server.ea_id, + CHAP_FAILURE, + esp->es_server.ea_id, + response_message, + strlen(response_message)); + } + break; + case CHAP_SUCCESS: + info("EAP: MSCHAPv2 success confirmed"); + break; + case CHAP_FAILURE: + info("EAP: MSCHAPv2 failure confirmed"); + break; + default: + error("EAP: Unhandled MSCHAPv2 opcode %d", opcode); + eap_send_nak(esp, id, EAPT_SRP); + } + + break; +#endif /* CHAPMS */ + #ifdef USE_SRP case EAPT_SRP: if (len < 1) { @@ -2499,7 +2797,9 @@ static char *eap_typenames[] = { "OTP", "Generic-Token", NULL, NULL, "RSA", "DSS", "KEA", "KEA-Validate", "TLS", "Defender", "Windows 2000", "Arcot", - "Cisco", "Nokia", "SRP" + "Cisco", "Nokia", "SRP", NULL, + "TTLS", "RAS", "AKA", "3COM", "PEAP", + "MSCHAPv2" }; static int @@ -2512,6 +2812,9 @@ eap_printpkt(u_char *inp, int inlen, #ifdef USE_EAPTLS u_char flags; #endif /* USE_EAPTLS */ +#ifdef CHAPMS + u_char opcode; +#endif /* CHAPMS */ if (inlen < EAP_HEADERLEN) return (0); @@ -2576,6 +2879,61 @@ eap_printpkt(u_char *inp, int inlen, } break; +#ifdef CHAPMS + case EAPT_MSCHAPV2: + if (len <= 0) + break; + GETCHAR(opcode, inp); + len--; + switch (opcode) { + case CHAP_CHALLENGE: + INCPTR(3, inp); + len -= 3; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + len -= vallen; + printer(arg, " Challenge <"); + for (; vallen > 0; --vallen) { + u_char val; + GETCHAR(val, inp); + printer(arg, "%.2x", val); + } + printer(arg, ">"); + if (len > 0) { + printer(arg, ", "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, ", "); + } + break; + case CHAP_SUCCESS: + INCPTR(3, inp); + len -= 3; + printer(arg, " Success "); + break; + case CHAP_FAILURE: + INCPTR(3, inp); + len -= 3; + printer(arg, " Failure "); + break; + default: + INCPTR(3, inp); + len -= 3; + printer(arg, " opcode=0x%x <%.*B>", opcode, len, inp); + break; + } + break; +#endif /* CHAPMS */ + #ifdef USE_EAPTLS case EAPT_TLS: if (len < 1) @@ -2761,6 +3119,51 @@ eap_printpkt(u_char *inp, int inlen, } break; +#ifdef CHAPMS + case EAPT_MSCHAPV2: + if (len <= 0) + break; + GETCHAR(opcode, inp); + len--; + switch (opcode) { + case CHAP_RESPONSE: + INCPTR(3, inp); + len -= 3; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + len -= vallen; + printer(arg, " Response <"); + for (; vallen > 0; --vallen) { + u_char val; + GETCHAR(val, inp); + printer(arg, "%.2x", val); + } + printer(arg, ">"); + if (len > 0) { + printer(arg, ", "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, ", "); + } + break; + case CHAP_SUCCESS: + printer(arg, " Success"); + break; + case CHAP_FAILURE: + printer(arg, " Failure"); + break; + default: + printer(arg, " opcode=0x%x <%.*B>", opcode, len, inp); + break; + } + break; +#endif /* CHAPMS */ + case EAPT_SRP: if (len < 1) goto truncated; diff --git a/pppd/eap.h b/pppd/eap.h index 56bef13..f72fe61 100644 --- a/pppd/eap.h +++ b/pppd/eap.h @@ -59,6 +59,18 @@ extern "C" { #define EAPT_NOKIACARD 18 /* Nokia IP smart card */ #define EAPT_SRP 19 /* Secure Remote Password */ /* 20 is deprecated */ +#define EAPT_TTLS 21 /* EAP Tunneled TLS Authentication Protocol RFC5281 */ +#define EAPT_RAS 22 /* Remote Access Service */ +#define EAPT_AKA 23 /* EAP method for 3rd Generation Authentication and Key Agreement RFC4187 */ +#define EAPT_3COM 24 /* EAP-3Com Wireless */ +#define EAPT_PEAP 25 /* Protected EAP */ +#define EAPT_MSCHAPV2 26 /* EAP-MSCHAPv2 RFC-draft-kamath-pppext-eap-mschapv2-02 */ + +/* OpCodes for MSCHAPv2 */ +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 /* EAP SRP-SHA1 Subtypes */ #define EAPSRP_CHALLENGE 1 /* Request 1 - Challenge */ @@ -98,6 +110,7 @@ enum eap_state_code { eapSRP2, /* Sent EAP SRP-SHA1 Subtype 2 */ eapSRP3, /* Sent EAP SRP-SHA1 Subtype 3 */ eapMD5Chall, /* Sent MD5-Challenge */ + eapMSCHAPv2Chall, /* Sent MSCHAPv2-Challenge */ eapOpen, /* Completed authentication */ eapSRP4, /* Sent EAP SRP-SHA1 Subtype 4 */ eapBadAuth /* Failed authentication */ @@ -107,7 +120,7 @@ enum eap_state_code { "Initial", "Pending", "Closed", "Listen", "Identify", \ "TlsStart", "TlsRecv", "TlsSendAck", "TlsSend", "TlsRecvAck", "TlsRecvClient",\ "TlsSendAlert", "TlsRecvAlertAck" , "TlsRecvSuccess", "TlsRecvFailure", \ - "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth" + "SRP1", "SRP2", "SRP3", "MD5Chall", "MSCHAPv2Chall", "Open", "SRP4", "BadAuth" #ifdef USE_EAPTLS #define eap_client_active(esp) ((esp)->es_client.ea_state != eapInitial &&\ diff --git a/pppd/plugins/radius/avpair.c b/pppd/plugins/radius/avpair.c index ebc1895..b97a7cf 100644 --- a/pppd/plugins/radius/avpair.c +++ b/pppd/plugins/radius/avpair.c @@ -222,6 +222,9 @@ VALUE_PAIR *rc_avpair_gen (AUTH_HDR *auth) { case PW_TYPE_STRING: + case PW_TYPE_IFID: + case PW_TYPE_IPV6ADDR: + case PW_TYPE_IPV6PREFIX: memcpy (pair->strvalue, (char *) ptr, (size_t) attrlen); pair->strvalue[attrlen] = '\0'; pair->lvalue = attrlen; @@ -692,9 +695,10 @@ int rc_avpair_parse (char *buffer, VALUE_PAIR **first_pair) int rc_avpair_tostr (VALUE_PAIR *pair, char *name, int ln, char *value, int lv) { DICT_VALUE *dval; - char buffer[32]; + char buffer[INET6_ADDRSTRLEN + 4]; // for a prefix: addr + '/' + prefixlen struct in_addr inad; unsigned char *ptr; + char *str; *name = *value = '\0'; @@ -753,6 +757,26 @@ int rc_avpair_tostr (VALUE_PAIR *pair, char *name, int ln, char *value, int lv) strncpy(value, buffer, lv-1); break; + case PW_TYPE_IFID: + ptr = pair->strvalue; + snprintf(buffer, sizeof (buffer), "%x:%x:%x:%x", + (ptr[0] << 8) + ptr[1], (ptr[2] << 8) + ptr[3], + (ptr[4] << 8) + ptr[5], (ptr[6] << 8) + ptr[7]); + strncpy(value, buffer, lv-1); + break; + + case PW_TYPE_IPV6ADDR: + inet_ntop(AF_INET6, pair->strvalue, buffer, sizeof (buffer)); + strncpy(value, buffer, lv-1); + break; + + case PW_TYPE_IPV6PREFIX: + inet_ntop(AF_INET6, pair->strvalue + 2, buffer, sizeof (buffer)); + str = buffer + strlen(buffer); + snprintf(str, sizeof (buffer) - (str - buffer), "/%d", *(pair->strvalue + 1)); + strncpy(value, buffer, lv-1); + break; + default: error("rc_avpair_tostr: unknown attribute type %d", pair->type); return (-1); diff --git a/pppd/plugins/radius/dict.c b/pppd/plugins/radius/dict.c index 72b3e70..3b2add2 100644 --- a/pppd/plugins/radius/dict.c +++ b/pppd/plugins/radius/dict.c @@ -158,6 +158,18 @@ int rc_read_dictionary (char *filename) { type = PW_TYPE_DATE; } + else if (strcmp (typestr, "ifid") == 0) + { + type = PW_TYPE_IFID; + } + else if (strcmp (typestr, "ipv6addr") == 0) + { + type = PW_TYPE_IPV6ADDR; + } + else if (strcmp (typestr, "ipv6prefix") == 0) + { + type = PW_TYPE_IPV6PREFIX; + } else { error("rc_read_dictionary: invalid type on line %d of dictionary %s", diff --git a/pppd/plugins/radius/radiusclient.h b/pppd/plugins/radius/radiusclient.h index c3a8e7a..17f6425 100644 --- a/pppd/plugins/radius/radiusclient.h +++ b/pppd/plugins/radius/radiusclient.h @@ -77,6 +77,17 @@ typedef struct pw_auth_hdr #define PW_TYPE_INTEGER 1 #define PW_TYPE_IPADDR 2 #define PW_TYPE_DATE 3 +#define PW_TYPE_ABINARY 4 +#define PW_TYPE_OCTETS 5 +#define PW_TYPE_IFID 6 +#define PW_TYPE_IPV6ADDR 7 +#define PW_TYPE_IPV6PREFIX 8 +#define PW_TYPE_BYTE 9 +#define PW_TYPE_SHORT 10 +#define PW_TYPE_ETHERNET 11 +#define PW_TYPE_SIGNED 12 +#define PW_TYPE_COMBO_IP 13 +#define PW_TYPE_TLV 14 /* standard RADIUS codes */ diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index 6e14238..28bc4be 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -2087,7 +2087,7 @@ get_if_hwaddr(u_char *addr, char *name) sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) - return 0; + return -1; memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr)); strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name)); ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq); @@ -2101,10 +2101,43 @@ get_if_hwaddr(u_char *addr, char *name) * get_first_ethernet - return the name of the first ethernet-style * interface on this system. */ +static char first_ether_name[IF_NAMESIZE]; char * get_first_ethernet(void) { - return "eth0"; + struct if_nameindex *if_ni, *i; + struct ifreq ifreq; + int ret, sock_fd; + + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd < 0) + return NULL; + + if_ni = if_nameindex(); + if (!if_ni) { + close(sock_fd); + return NULL; + } + + first_ether_name[0] = 0; + + for (i = if_ni; !(i->if_index == 0 && i->if_name == NULL); i++) { + memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr)); + strlcpy(ifreq.ifr_name, i->if_name, sizeof(ifreq.ifr_name)); + ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq); + if (ret >= 0 && ifreq.ifr_hwaddr.sa_family == ARPHRD_ETHER) { + strlcpy(first_ether_name, i->if_name, sizeof(first_ether_name)); + break; + } + } + + if_freenameindex(if_ni); + close(sock_fd); + + if (!first_ether_name[0]) + return NULL; + + return first_ether_name; } /******************************************************************** diff --git a/pppd/sys-solaris.c b/pppd/sys-solaris.c index d96d23c..6e925f9 100644 --- a/pppd/sys-solaris.c +++ b/pppd/sys-solaris.c @@ -1508,7 +1508,7 @@ netif_set_mtu(int unit, int mtu) memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - ifr.ifr_metric = link_mtu; + ifr.ifr_metric = mtu; if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) { error("Couldn't set IP MTU (%s): %m", ifr.ifr_name); } @@ -1520,7 +1520,7 @@ netif_set_mtu(int unit, int mtu) memset(&lifr, 0, sizeof(lifr)); strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); - lifr.lifr_mtu = link_mtu; + lifr.lifr_mtu = mtu; if (ioctl(fd, SIOCSLIFMTU, &lifr) < 0) { close(fd); error("Couldn't set IPv6 MTU (%s): %m", ifr.ifr_name);