X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fauth.c;h=716d849ae8546a51a0cefc12e5bac2fb44873d78;hp=7a77a83381c82b926a52e7b1dae4acf2c731b3cd;hb=31b4bba68d46b38119fd8620ee09ff7f8831f4b5;hpb=75951d7bb62003e37dd390e773df19f16250d27d diff --git a/pppd/auth.c b/pppd/auth.c index 7a77a83..716d849 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -32,9 +32,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static char rcsid[] = "$Id: auth.c,v 1.47 1999/03/19 01:17:51 paulus Exp $"; -#endif +#define RCSID "$Id: auth.c,v 1.57 1999/08/13 06:46:10 paulus Exp $" #include #include @@ -78,11 +76,7 @@ static char rcsid[] = "$Id: auth.c,v 1.47 1999/03/19 01:17:51 paulus Exp $"; #endif #include "pathnames.h" -/* Used for storing a sequence of words. Usually malloced. */ -struct wordlist { - struct wordlist *next; - char *word; -}; +static const char rcsid[] = RCSID; /* Bits in scan_authfile return value */ #define NONWILD_SERVER 1 @@ -102,6 +96,9 @@ static int logged_in; /* List of addresses which the peer may use. */ static struct permitted_ip *addresses[NUM_PPP]; +/* Extra options to apply, from the secrets file entry for the peer. */ +static struct wordlist *extra_options; + /* Number of network protocols which we have opened. */ static int num_np_open; @@ -208,6 +205,9 @@ option_t auth_options[] = { "PAP passwords are encrypted", 1 }, { "+ua", o_special, setupapfile, "Get PAP user and password from file" }, + { "password", o_string, passwd, + "Password for authenticating us to the peer", OPT_STATIC, + NULL, MAXSECRETLEN }, { "privgroup", o_special, privgroup, "Allow group members to use privileged options", OPT_PRIV }, { NULL } @@ -367,6 +367,7 @@ link_established(unit) if (!wo->neg_upap || !null_login(unit)) { warn("peer refused to authenticate: terminating link"); lcp_close(unit, "peer refused to authenticate"); + status = EXIT_PEER_AUTH_FAILED; return; } } @@ -405,8 +406,6 @@ static void network_phase(unit) int unit; { - int i; - struct protent *protp; lcp_options *go = &lcp_gotoptions[unit]; /* @@ -431,6 +430,23 @@ network_phase(unit) } #endif + /* + * Process extra options from the secrets file + */ + if (extra_options) { + options_from_list(extra_options, 1); + free_wordlist(extra_options); + extra_options = 0; + } + start_networks(); +} + +void +start_networks() +{ + int i; + struct protent *protp; + phase = PHASE_NETWORK; #if 0 if (!demand) @@ -439,7 +455,7 @@ network_phase(unit) for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) { - (*protp->open)(unit); + (*protp->open)(0); if (protp->protocol != PPP_CCP) ++num_np_open; } @@ -460,6 +476,7 @@ auth_peer_fail(unit, protocol) * Authentication failure: take the link down */ lcp_close(unit, "Authentication failed"); + status = EXIT_PEER_AUTH_FAILED; } /* @@ -513,9 +530,12 @@ auth_withpeer_fail(unit, protocol) BZERO(passwd, MAXSECRETLEN); /* * We've failed to authenticate ourselves to our peer. - * He'll probably take the link down, and there's not much - * we can do except wait for that. + * Some servers keep sending CHAP challenges, but there + * is no point in persisting without any way to get updated + * authentication secrets. */ + lcp_close(unit, "Failed to authenticate ourselves to peer"); + status = EXIT_AUTH_TOPEER_FAILED; } /* @@ -561,7 +581,8 @@ np_up(unit, proto) /* * At this point we consider that the link has come up successfully. */ - need_holdoff = 0; + status = EXIT_OK; + unsuccess = 0; if (idle_time_limit > 0) TIMEOUT(check_idle, NULL, idle_time_limit); @@ -623,8 +644,10 @@ check_idle(arg) itime = MIN(idle.xmit_idle, idle.recv_idle); if (itime >= idle_time_limit) { /* link is idle: shut it down. */ - info("Terminating connection due to lack of activity."); + notice("Terminating connection due to lack of activity."); lcp_close(0, "Link inactive"); + need_holdoff = 0; + status = EXIT_IDLE_TIMEOUT; } else { TIMEOUT(check_idle, NULL, idle_time_limit - itime); } @@ -639,6 +662,7 @@ connect_time_expired(arg) { info("Connect time expired"); lcp_close(0, "Connect time expired"); /* Close connection */ + status = EXIT_CONNECT_TIME; } /* @@ -720,8 +744,9 @@ auth_reset(unit) ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); ao->neg_chap = !refuse_chap - && have_chap_secret(user, (explicit_remote? remote_name: NULL), - 0, NULL); + && (passwd[0] != 0 + || have_chap_secret(user, (explicit_remote? remote_name: NULL), + 0, NULL)); if (go->neg_upap && !uselogin && !have_pap_secret(NULL)) go->neg_upap = 0; @@ -776,30 +801,34 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen) */ filename = _PATH_UPAPFILE; addrs = NULL; - ret = UPAP_AUTHACK; + ret = UPAP_AUTHNAK; f = fopen(filename, "r"); if (f == NULL) { error("Can't open PAP password file %s: %m", filename); - ret = UPAP_AUTHNAK; } else { check_access(f, filename); - if (scan_authfile(f, user, our_name, secret, &addrs, filename) < 0 - || (secret[0] != 0 && (cryptpap || strcmp(passwd, secret) != 0) - && strcmp(crypt(passwd, secret), secret) != 0)) { - warn("PAP authentication failure for %s", user); - ret = UPAP_AUTHNAK; + if (scan_authfile(f, user, our_name, secret, &addrs, filename) < 0) { + warn("no PAP secret found for %s", user); + } else if (secret[0] != 0) { + /* password given in pap-secrets - must match */ + if ((!cryptpap && strcmp(passwd, secret) == 0) + || strcmp(crypt(passwd, secret), secret) == 0) + ret = UPAP_AUTHACK; + else + warn("PAP authentication failure for %s", user); + } else if (uselogin) { + /* empty password in pap-secrets and login option */ + ret = plogin(user, passwd, msg, msglen); + if (ret == UPAP_AUTHNAK) + warn("PAP login failure for %s", user); + } else { + /* empty password in pap-secrets and login option not used */ + ret = UPAP_AUTHACK; } fclose(f); } - if (uselogin && ret == UPAP_AUTHACK) { - ret = plogin(user, passwd, msg, msglen); - if (ret == UPAP_AUTHNAK) { - warn("PAP login failure for %s", user); - } - } - if (ret == UPAP_AUTHNAK) { if (*msg == (char *) 0) *msg = "Login incorrect"; @@ -838,63 +867,65 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen) */ #ifdef USE_PAM -static char *PAM_username = ""; -static char *PAM_password = ""; - -#ifdef PAM_ESTABLISH_CRED /* new PAM defines :(^ */ -#define MY_PAM_STRERROR(err_code) (char *) pam_strerror(pamh,err_code) -#else -#define MY_PAM_STRERROR(err_code) (char *) pam_strerror(err_code) -#endif +/* Static variables used to communicate between the conversation function + * and the server_login function + */ +static char *PAM_username; +static char *PAM_password; +static int PAM_error = 0; +static pam_handle_t *pamh = NULL; + +/* PAM conversation function + * Here we assume (for now, at least) that echo on means login name, and + * echo off means password. + */ -static int pam_conv (int num_msg, - const struct pam_message **msg, - struct pam_response **resp, - void *appdata_ptr) +static int PAM_conv (int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) { - int count = 0, replies = 0; + int replies = 0; struct pam_response *reply = NULL; - int size = 0; - - for (count = 0; count < num_msg; count++) - { - size += sizeof (struct pam_response); - reply = realloc (reply, size); /* ANSI: is malloc() if reply==NULL */ - if (!reply) - return PAM_CONV_ERR; - - switch (msg[count]->msg_style) - { - case PAM_PROMPT_ECHO_ON: - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies++].resp = strdup(PAM_username); /* never NULL */ - break; - - case PAM_PROMPT_ECHO_OFF: - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies++].resp = strdup(PAM_password); /* never NULL */ - break; - - case PAM_TEXT_INFO: - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies++].resp = NULL; - break; - - case PAM_ERROR_MSG: - default: - free (reply); - return PAM_CONV_ERR; - } - } - - if (resp) - *resp = reply; - else - free (reply); +#define COPY_STRING(s) (s) ? strdup(s) : NULL + + reply = malloc(sizeof(struct pam_response) * num_msg); + if (!reply) return PAM_CONV_ERR; + + for (replies = 0; replies < num_msg; replies++) { + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(PAM_username); + /* PAM frees resp */ + break; + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(PAM_password); + /* PAM frees resp */ + break; + case PAM_TEXT_INFO: + /* fall through */ + case PAM_ERROR_MSG: + /* ignore it, but pam still wants a NULL response... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + default: + /* Must be an error of some sort... */ + free (reply); + PAM_error = 1; + return PAM_CONV_ERR; + } + } + *resp = reply; return PAM_SUCCESS; } -#endif + +static struct pam_conv PAM_conversation = { + &PAM_conv, + NULL +}; +#endif /* USE_PAM */ /* * plogin - Check the user name and password against the system @@ -913,68 +944,58 @@ plogin(user, passwd, msg, msglen) char **msg; int *msglen; { + char *tty; #ifdef USE_PAM - - struct pam_conv pam_conversation; - pam_handle_t *pamh; int pam_error; -/* - * Fill the pam_conversion structure - */ - memset (&pam_conversation, '\0', sizeof (struct pam_conv)); - pam_conversation.conv = &pam_conv; - - pam_error = pam_start ("ppp", user, &pam_conversation, &pamh); + pam_error = pam_start ("ppp", user, &PAM_conversation, &pamh); if (pam_error != PAM_SUCCESS) { - *msg = MY_PAM_STRERROR (pam_error); + *msg = (char *) pam_strerror (pamh, pam_error); + reopen_log(); return UPAP_AUTHNAK; } -/* - * Define the fields for the credintial validation - */ - (void) pam_set_item (pamh, PAM_TTY, devnam); + /* + * Define the fields for the credential validation + */ + PAM_username = user; PAM_password = passwd; -/* - * Validate the user - */ + PAM_error = 0; + pam_set_item (pamh, PAM_TTY, devnam); /* this might be useful to some modules */ + + /* + * Validate the user + */ pam_error = pam_authenticate (pamh, PAM_SILENT); - if (pam_error == PAM_SUCCESS) { + if (pam_error == PAM_SUCCESS && !PAM_error) { pam_error = pam_acct_mgmt (pamh, PAM_SILENT); - - /* start a session for this user. Session closed when link ends. */ - if (pam_error == PAM_SUCCESS) - (void) pam_open_session (pamh, PAM_SILENT); + if (pam_error == PAM_SUCCESS) + pam_open_session (pamh, PAM_SILENT); } - *msg = MY_PAM_STRERROR (pam_error); - - PAM_username = - PAM_password = ""; -/* - * Clean up the mess - */ - (void) pam_end (pamh, pam_error); + *msg = (char *) pam_strerror (pamh, pam_error); + /* + * Clean up the mess + */ + reopen_log(); /* apparently the PAM stuff does closelog() */ + PAM_username = NULL; + PAM_password = NULL; if (pam_error != PAM_SUCCESS) return UPAP_AUTHNAK; +#else /* #ifdef USE_PAM */ + /* * Use the non-PAM methods directly */ -#else /* #ifdef USE_PAM */ - - struct passwd *pw; - char *tty; #ifdef HAS_SHADOW struct spwd *spwd; struct spwd *getspnam(); - long now; #endif + struct passwd *pw = getpwnam(user); - pw = getpwnam(user); endpwent(); if (pw == NULL) return (UPAP_AUTHNAK); @@ -982,34 +1003,29 @@ plogin(user, passwd, msg, msglen) #ifdef HAS_SHADOW spwd = getspnam(user); endspent(); - if (spwd == NULL) - return UPAP_AUTHNAK; - - /* check the age of the password entry */ - now = time(NULL) / 86400L; - - if ((spwd->sp_expire > 0 && now >= spwd->sp_expire) - || ((spwd->sp_max >= 0 && spwd->sp_max < 10000) - && spwd->sp_lstchg >= 0 - && now >= spwd->sp_lstchg + spwd->sp_max)) { - warn("Password for %s has expired", user); - return (UPAP_AUTHNAK); + if (spwd) { + /* check the age of the password entry */ + long now = time(NULL) / 86400L; + + if ((spwd->sp_expire > 0 && now >= spwd->sp_expire) + || ((spwd->sp_max >= 0 && spwd->sp_max < 10000) + && spwd->sp_lstchg >= 0 + && now >= spwd->sp_lstchg + spwd->sp_max)) { + warn("Password for %s has expired", user); + return (UPAP_AUTHNAK); + } + pw->pw_passwd = spwd->sp_pwdp; } - pw->pw_passwd = spwd->sp_pwdp; #endif /* * If no passwd, don't let them login. */ - if (pw->pw_passwd == NULL || *pw->pw_passwd == '\0' + if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2 || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0) return (UPAP_AUTHNAK); - /* These functions are not enabled for PAM. The reason for this is that */ - /* there is not necessarily a "passwd" entry for this user. That is */ - /* real purpose of 'PAM' -- to virtualize the account data from the */ - /* application. If you want to do the same thing, write the entry in */ - /* the 'session' hook. */ +#endif /* #ifdef USE_PAM */ /* * Write a wtmp entry for this user. @@ -1020,8 +1036,8 @@ plogin(user, passwd, msg, msglen) tty += 5; logwtmp(tty, user, remote_name); /* Add wtmp login entry */ -#if defined(_PATH_LASTLOG) - { +#if defined(_PATH_LASTLOG) && !defined(USE_PAM) + if (pw != (struct passwd *)NULL) { struct lastlog ll; int fd; @@ -1029,14 +1045,12 @@ plogin(user, passwd, msg, msglen) (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET); memset((void *)&ll, 0, sizeof(ll)); (void)time(&ll.ll_time); - (void)strlcpy(ll.ll_line, tty, sizeof(ll.ll_line)); + (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); (void)write(fd, (char *)&ll, sizeof(ll)); (void)close(fd); } } -#endif - -#endif /* #ifdef USE_PAM */ +#endif /* _PATH_LASTLOG and not USE_PAM */ info("user %s logged in", user); logged_in = 1; @@ -1051,33 +1065,23 @@ static void plogout() { #ifdef USE_PAM - struct pam_conv pam_conversation; - pam_handle_t *pamh; int pam_error; -/* - * Fill the pam_conversion structure. The PAM specification states that the - * session must be able to be closed by a totally different handle from which - * it was created. Hold the PAM group to their own specification! - */ - memset (&pam_conversation, '\0', sizeof (struct pam_conv)); - pam_conversation.conv = &pam_conv; - - pam_error = pam_start ("ppp", user, &pam_conversation, &pamh); - if (pam_error == PAM_SUCCESS) { - (void) pam_set_item (pamh, PAM_TTY, devnam); - (void) pam_close_session (pamh, PAM_SILENT); - (void) pam_end (pamh, PAM_SUCCESS); - } -#else + if (pamh != NULL) { + pam_error = pam_close_session (pamh, PAM_SILENT); + pam_end (pamh, pam_error); + pamh = NULL; + } + /* Apparently the pam stuff does closelog(). */ + reopen_log(); +#else /* ! USE_PAM */ char *tty; tty = devnam; if (strncmp(tty, "/dev/", 5) == 0) tty += 5; logwtmp(tty, "", ""); /* Wipe out utmp logout entry */ -#endif - +#endif /* ! USE_PAM */ logged_in = 0; } @@ -1238,13 +1242,13 @@ have_chap_secret(client, server, need_ip, lacks_ipp) * (We could be either client or server). */ int -get_secret(unit, client, server, secret, secret_len, save_addrs) +get_secret(unit, client, server, secret, secret_len, am_server) int unit; char *client; char *server; char *secret; int *secret_len; - int save_addrs; + int am_server; { FILE *f; int ret, len; @@ -1252,24 +1256,28 @@ get_secret(unit, client, server, secret, secret_len, save_addrs) struct wordlist *addrs; char secbuf[MAXWORDLEN]; - filename = _PATH_CHAPFILE; - addrs = NULL; - secbuf[0] = 0; - - f = fopen(filename, "r"); - if (f == NULL) { - error("Can't open chap secret file %s: %m", filename); - return 0; - } - check_access(f, filename); + if (!am_server && passwd[0] != 0) { + strlcpy(secbuf, passwd, sizeof(secbuf)); + } else { + filename = _PATH_CHAPFILE; + addrs = NULL; + secbuf[0] = 0; + + f = fopen(filename, "r"); + if (f == NULL) { + error("Can't open chap secret file %s: %m", filename); + return 0; + } + check_access(f, filename); - ret = scan_authfile(f, client, server, secbuf, &addrs, filename); - fclose(f); - if (ret < 0) - return 0; + ret = scan_authfile(f, client, server, secbuf, &addrs, filename); + fclose(f); + if (ret < 0) + return 0; - if (save_addrs) - set_allowed_addrs(unit, addrs); + if (am_server) + set_allowed_addrs(unit, addrs); + } len = strlen(secbuf); if (len > MAXSECRETLEN) { @@ -1285,28 +1293,44 @@ get_secret(unit, client, server, secret, secret_len, save_addrs) /* * set_allowed_addrs() - set the list of allowed addresses. + * Also looks for `--' indicating options to apply for this peer + * and leaves the following words in extra_options. */ static void set_allowed_addrs(unit, addrs) int unit; struct wordlist *addrs; { - int n = 0; - struct wordlist *ap; + int n; + struct wordlist *ap, **pap; struct permitted_ip *ip; char *ptr_word, *ptr_mask; struct hostent *hp; struct netent *np; - u_int32_t a, mask, ah; + u_int32_t a, mask, ah, offset; struct ipcp_options *wo = &ipcp_wantoptions[unit]; u_int32_t suggested_ip = 0; if (addresses[unit] != NULL) free(addresses[unit]); addresses[unit] = NULL; + if (extra_options != NULL) + free_wordlist(extra_options); + extra_options = NULL; - for (ap = addrs; ap != NULL; ap = ap->next) - ++n; + /* + * Count the number of IP addresses given, and chop off + * any extra options for this peer. + */ + for (n = 0, pap = &addrs; (ap = *pap) != NULL; pap = &ap->next, ++n) { + if (strcmp(ap->word, "--") == 0) { + /* rest are options */ + *pap = 0; + extra_options = ap->next; + free(ap); + break; + } + } if (n == 0) return; ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip)); @@ -1333,18 +1357,29 @@ set_allowed_addrs(unit, addrs) } mask = ~ (u_int32_t) 0; + offset = 0; ptr_mask = strchr (ptr_word, '/'); if (ptr_mask != NULL) { int bit_count; + char *endp; - bit_count = (int) strtol (ptr_mask+1, (char **) 0, 10); + bit_count = (int) strtol (ptr_mask+1, &endp, 10); if (bit_count <= 0 || bit_count > 32) { warn("invalid address length %v in auth. address list", - ptr_mask); + ptr_mask+1); + continue; + } + bit_count = 32 - bit_count; /* # bits in host part */ + if (*endp == '+') { + offset = ifunit + 1; + ++endp; + } + if (*endp != 0) { + warn("invalid address length syntax: %v", ptr_mask+1); continue; } *ptr_mask = '\0'; - mask <<= 32 - bit_count; + mask <<= bit_count; } hp = gethostbyname(ptr_word); @@ -1372,15 +1407,24 @@ set_allowed_addrs(unit, addrs) if (ptr_mask != NULL) *ptr_mask = '/'; - if (a == (u_int32_t)-1L) + if (a == (u_int32_t)-1L) { warn("unknown host %s in auth. address list", ap->word); - else { - ip[n].mask = htonl(mask); - ip[n].base = a & ip[n].mask; - ++n; - if (~mask == 0 && suggested_ip == 0) - suggested_ip = a; + continue; + } + if (offset != 0) { + if (offset >= ~mask) { + warn("interface unit %d too large for subnet %v", + ifunit, ptr_word); + continue; + } + a = htonl((ntohl(a) & mask) + offset); + mask = ~(u_int32_t)0; } + ip[n].mask = htonl(mask); + ip[n].base = a & ip[n].mask; + ++n; + if (~mask == 0 && suggested_ip == 0) + suggested_ip = a; } ip[n].permit = 0; /* make the last entry forbid all addresses */