| /* |
| * auth.c - PPP authentication and phase control. |
| * |
| * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. The name(s) of the authors of this software must not be used to |
| * endorse or promote products derived from this software without |
| * prior written permission. |
| * |
| * 3. Redistributions of any form whatsoever must retain the following |
| * acknowledgment: |
| * "This product includes software developed by Paul Mackerras |
| * <paulus@samba.org>". |
| * |
| * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO |
| * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
| * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| * Derived from main.c, which is: |
| * |
| * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The name "Carnegie Mellon University" must not be used to |
| * endorse or promote products derived from this software without |
| * prior written permission. For permission or any legal |
| * details, please contact |
| * Office of Technology Transfer |
| * Carnegie Mellon University |
| * 5000 Forbes Avenue |
| * Pittsburgh, PA 15213-3890 |
| * (412) 268-4387, fax: (412) 268-7395 |
| * tech-transfer@andrew.cmu.edu |
| * |
| * 4. Redistributions of any form whatsoever must retain the following |
| * acknowledgment: |
| * "This product includes software developed by Computing Services |
| * at Carnegie Mellon University (http://www.cmu.edu/computing/)." |
| * |
| * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
| * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
| * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
| * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #define RCSID "$Id: auth.c,v 1.101 2004/11/12 10:30:51 paulus Exp $" |
| |
| #include <stdio.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <pwd.h> |
| #include <grp.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <utmp.h> |
| #include <fcntl.h> |
| #if defined(_PATH_LASTLOG) && defined(__linux__) |
| #include <lastlog.h> |
| #endif |
| |
| #include <netdb.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| |
| #ifdef USE_PAM |
| #include <security/pam_appl.h> |
| #endif |
| |
| #ifdef HAS_SHADOW |
| #include <shadow.h> |
| #ifndef PW_PPP |
| #define PW_PPP PW_LOGIN |
| #endif |
| #endif |
| #include <time.h> |
| |
| #include "pppd.h" |
| #include "fsm.h" |
| #include "lcp.h" |
| #include "ccp.h" |
| #include "ecp.h" |
| #include "ipcp.h" |
| #include "upap.h" |
| #include "chap-new.h" |
| #include "eap.h" |
| #ifdef CBCP_SUPPORT |
| #include "cbcp.h" |
| #endif |
| #include "pathnames.h" |
| |
| static const char rcsid[] = RCSID; |
| |
| /* Bits in scan_authfile return value */ |
| #define NONWILD_SERVER 1 |
| #define NONWILD_CLIENT 2 |
| |
| #define ISWILD(word) (word[0] == '*' && word[1] == 0) |
| |
| /* The name by which the peer authenticated itself to us. */ |
| char peer_authname[MAXNAMELEN]; |
| |
| /* Records which authentication operations haven't completed yet. */ |
| static int auth_pending[NUM_PPP]; |
| |
| /* Records which authentication operations have been completed. */ |
| int auth_done[NUM_PPP]; |
| |
| /* Set if we have successfully called plogin() */ |
| static int logged_in; |
| |
| /* List of addresses which the peer may use. */ |
| static struct permitted_ip *addresses[NUM_PPP]; |
| |
| /* Wordlist giving addresses which the peer may use |
| without authenticating itself. */ |
| static struct wordlist *noauth_addrs; |
| |
| /* Remote telephone number, if available */ |
| char remote_number[MAXNAMELEN]; |
| |
| /* Wordlist giving remote telephone numbers which may connect. */ |
| static struct wordlist *permitted_numbers; |
| |
| /* 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; |
| |
| /* Number of network protocols which have come up. */ |
| static int num_np_up; |
| |
| /* Set if we got the contents of passwd[] from the pap-secrets file. */ |
| static int passwd_from_file; |
| |
| /* Set if we require authentication only because we have a default route. */ |
| static bool default_auth; |
| |
| /* Hook to enable a plugin to control the idle time limit */ |
| int (*idle_time_hook) __P((struct ppp_idle *)) = NULL; |
| |
| /* Hook for a plugin to say whether we can possibly authenticate any peer */ |
| int (*pap_check_hook) __P((void)) = NULL; |
| |
| /* Hook for a plugin to check the PAP user and password */ |
| int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp, |
| struct wordlist **paddrs, |
| struct wordlist **popts)) = NULL; |
| |
| /* Hook for a plugin to know about the PAP user logout */ |
| void (*pap_logout_hook) __P((void)) = NULL; |
| |
| /* Hook for a plugin to get the PAP password for authenticating us */ |
| int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL; |
| |
| /* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */ |
| int (*chap_check_hook) __P((void)) = NULL; |
| |
| /* Hook for a plugin to get the CHAP password for authenticating us */ |
| int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL; |
| |
| /* Hook for a plugin to say whether it is OK if the peer |
| refuses to authenticate. */ |
| int (*null_auth_hook) __P((struct wordlist **paddrs, |
| struct wordlist **popts)) = NULL; |
| |
| int (*allowed_address_hook) __P((u_int32_t addr)) = NULL; |
| |
| /* A notifier for when the peer has authenticated itself, |
| and we are proceeding to the network phase. */ |
| struct notifier *auth_up_notifier = NULL; |
| |
| /* A notifier for when the link goes down. */ |
| struct notifier *link_down_notifier = NULL; |
| |
| /* |
| * This is used to ensure that we don't start an auth-up/down |
| * script while one is already running. |
| */ |
| enum script_state { |
| s_down, |
| s_up |
| }; |
| |
| static enum script_state auth_state = s_down; |
| static enum script_state auth_script_state = s_down; |
| static pid_t auth_script_pid = 0; |
| |
| static int used_login; /* peer authenticated against login database */ |
| |
| /* |
| * Option variables. |
| */ |
| bool uselogin = 0; /* Use /etc/passwd for checking PAP */ |
| bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ |
| bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ |
| bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ |
| bool refuse_eap = 0; /* Don't wanna auth. ourselves with EAP */ |
| #ifdef CHAPMS |
| bool refuse_mschap = 0; /* Don't wanna auth. ourselves with MS-CHAP */ |
| bool refuse_mschap_v2 = 0; /* Don't wanna auth. ourselves with MS-CHAPv2 */ |
| #else |
| bool refuse_mschap = 1; /* Don't wanna auth. ourselves with MS-CHAP */ |
| bool refuse_mschap_v2 = 1; /* Don't wanna auth. ourselves with MS-CHAPv2 */ |
| #endif |
| bool usehostname = 0; /* Use hostname for our_name */ |
| bool auth_required = 0; /* Always require authentication from peer */ |
| bool allow_any_ip = 0; /* Allow peer to use any IP address */ |
| bool explicit_remote = 0; /* User specified explicit remote name */ |
| char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ |
| |
| static char *uafname; /* name of most recent +ua file */ |
| |
| extern char *crypt __P((const char *, const char *)); |
| |
| /* Prototypes for procedures local to this file. */ |
| |
| static void network_phase __P((int)); |
| static void check_idle __P((void *)); |
| static void connect_time_expired __P((void *)); |
| static int plogin __P((char *, char *, char **)); |
| static void plogout __P((void)); |
| static int null_login __P((int)); |
| static int get_pap_passwd __P((char *)); |
| static int have_pap_secret __P((int *)); |
| static int have_chap_secret __P((char *, char *, int, int *)); |
| static int have_srp_secret __P((char *client, char *server, int need_ip, |
| int *lacks_ipp)); |
| static int ip_addr_check __P((u_int32_t, struct permitted_ip *)); |
| static int scan_authfile __P((FILE *, char *, char *, char *, |
| struct wordlist **, struct wordlist **, |
| char *, int)); |
| static void free_wordlist __P((struct wordlist *)); |
| static void auth_script __P((char *)); |
| static void auth_script_done __P((void *)); |
| static void set_allowed_addrs __P((int, struct wordlist *, struct wordlist *)); |
| static int some_ip_ok __P((struct wordlist *)); |
| static int setupapfile __P((char **)); |
| static int privgroup __P((char **)); |
| static int set_noauth_addr __P((char **)); |
| static int set_permitted_number __P((char **)); |
| static void check_access __P((FILE *, char *)); |
| static int wordlist_count __P((struct wordlist *)); |
| |
| #ifdef MAXOCTETS |
| static void check_maxoctets __P((void *)); |
| #endif |
| |
| /* |
| * Authentication-related options. |
| */ |
| option_t auth_options[] = { |
| { "auth", o_bool, &auth_required, |
| "Require authentication from peer", OPT_PRIO | 1 }, |
| { "noauth", o_bool, &auth_required, |
| "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV, |
| &allow_any_ip }, |
| { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, |
| "Require PAP authentication from peer", |
| OPT_PRIOSUB | 1, &auth_required }, |
| { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, |
| "Require PAP authentication from peer", |
| OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required }, |
| { "require-chap", o_bool, &auth_required, |
| "Require CHAP authentication from peer", |
| OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, |
| &lcp_wantoptions[0].chap_mdtype }, |
| { "+chap", o_bool, &auth_required, |
| "Require CHAP authentication from peer", |
| OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, |
| &lcp_wantoptions[0].chap_mdtype }, |
| #ifdef CHAPMS |
| { "require-mschap", o_bool, &auth_required, |
| "Require MS-CHAP authentication from peer", |
| OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, |
| &lcp_wantoptions[0].chap_mdtype }, |
| { "+mschap", o_bool, &auth_required, |
| "Require MS-CHAP authentication from peer", |
| OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, |
| &lcp_wantoptions[0].chap_mdtype }, |
| { "require-mschap-v2", o_bool, &auth_required, |
| "Require MS-CHAPv2 authentication from peer", |
| OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, |
| &lcp_wantoptions[0].chap_mdtype }, |
| { "+mschap-v2", o_bool, &auth_required, |
| "Require MS-CHAPv2 authentication from peer", |
| OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, |
| &lcp_wantoptions[0].chap_mdtype }, |
| #endif |
| |
| { "refuse-pap", o_bool, &refuse_pap, |
| "Don't agree to auth to peer with PAP", 1 }, |
| { "-pap", o_bool, &refuse_pap, |
| "Don't allow PAP authentication with peer", OPT_ALIAS | 1 }, |
| { "refuse-chap", o_bool, &refuse_chap, |
| "Don't agree to auth to peer with CHAP", |
| OPT_A2CLRB | MDTYPE_MD5, |
| &lcp_allowoptions[0].chap_mdtype }, |
| { "-chap", o_bool, &refuse_chap, |
| "Don't allow CHAP authentication with peer", |
| OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5, |
| &lcp_allowoptions[0].chap_mdtype }, |
| #ifdef CHAPMS |
| { "refuse-mschap", o_bool, &refuse_mschap, |
| "Don't agree to auth to peer with MS-CHAP", |
| OPT_A2CLRB | MDTYPE_MICROSOFT, |
| &lcp_allowoptions[0].chap_mdtype }, |
| { "-mschap", o_bool, &refuse_mschap, |
| "Don't allow MS-CHAP authentication with peer", |
| OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT, |
| &lcp_allowoptions[0].chap_mdtype }, |
| { "refuse-mschap-v2", o_bool, &refuse_mschap_v2, |
| "Don't agree to auth to peer with MS-CHAPv2", |
| OPT_A2CLRB | MDTYPE_MICROSOFT_V2, |
| &lcp_allowoptions[0].chap_mdtype }, |
| { "-mschap-v2", o_bool, &refuse_mschap_v2, |
| "Don't allow MS-CHAPv2 authentication with peer", |
| OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2, |
| &lcp_allowoptions[0].chap_mdtype }, |
| #endif |
| |
| { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap, |
| "Require EAP authentication from peer", OPT_PRIOSUB | 1, |
| &auth_required }, |
| { "refuse-eap", o_bool, &refuse_eap, |
| "Don't agree to authenticate to peer with EAP", 1 }, |
| |
| { "name", o_string, our_name, |
| "Set local name for authentication", |
| OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN }, |
| |
| { "+ua", o_special, (void *)setupapfile, |
| "Get PAP user and password from file", |
| OPT_PRIO | OPT_A2STRVAL, &uafname }, |
| |
| { "user", o_string, user, |
| "Set name for auth with peer", OPT_PRIO | OPT_STATIC, NULL, MAXNAMELEN }, |
| |
| { "password", o_string, passwd, |
| "Password for authenticating us to the peer", |
| OPT_PRIO | OPT_STATIC | OPT_HIDE, NULL, MAXSECRETLEN }, |
| |
| { "usehostname", o_bool, &usehostname, |
| "Must use hostname for authentication", 1 }, |
| |
| { "remotename", o_string, remote_name, |
| "Set remote name for authentication", OPT_PRIO | OPT_STATIC, |
| &explicit_remote, MAXNAMELEN }, |
| |
| { "login", o_bool, &uselogin, |
| "Use system password database for PAP", 1 }, |
| |
| { "papcrypt", o_bool, &cryptpap, |
| "PAP passwords are encrypted", 1 }, |
| |
| { "privgroup", o_special, (void *)privgroup, |
| "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST }, |
| |
| { "allow-ip", o_special, (void *)set_noauth_addr, |
| "Set IP address(es) which can be used without authentication", |
| OPT_PRIV | OPT_A2LIST }, |
| |
| { "remotenumber", o_string, remote_number, |
| "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC, |
| NULL, MAXNAMELEN }, |
| |
| { "allow-number", o_special, (void *)set_permitted_number, |
| "Set telephone number(s) which are allowed to connect", |
| OPT_PRIV | OPT_A2LIST }, |
| |
| { NULL } |
| }; |
| |
| /* |
| * setupapfile - specifies UPAP info for authenticating with peer. |
| */ |
| static int |
| setupapfile(argv) |
| char **argv; |
| { |
| FILE *ufile; |
| int l; |
| char u[MAXNAMELEN], p[MAXSECRETLEN]; |
| char *fname; |
| |
| lcp_allowoptions[0].neg_upap = 1; |
| |
| /* open user info file */ |
| fname = strdup(*argv); |
| if (fname == NULL) |
| novm("+ua file name"); |
| seteuid(getuid()); |
| ufile = fopen(fname, "r"); |
| seteuid(0); |
| if (ufile == NULL) { |
| option_error("unable to open user login data file %s", fname); |
| return 0; |
| } |
| check_access(ufile, fname); |
| uafname = fname; |
| |
| /* get username */ |
| if (fgets(u, MAXNAMELEN - 1, ufile) == NULL |
| || fgets(p, MAXSECRETLEN - 1, ufile) == NULL) { |
| fclose(ufile); |
| option_error("unable to read user login data file %s", fname); |
| return 0; |
| } |
| fclose(ufile); |
| |
| /* get rid of newlines */ |
| l = strlen(u); |
| if (l > 0 && u[l-1] == '\n') |
| u[l-1] = 0; |
| l = strlen(p); |
| if (l > 0 && p[l-1] == '\n') |
| p[l-1] = 0; |
| |
| if (override_value("user", option_priority, fname)) |
| strlcpy(user, u, sizeof(user)); |
| if (override_value("passwd", option_priority, fname)) |
| strlcpy(passwd, p, sizeof(passwd)); |
| |
| return (1); |
| } |
| |
| |
| /* |
| * privgroup - allow members of the group to have privileged access. |
| */ |
| static int |
| privgroup(argv) |
| char **argv; |
| { |
| struct group *g; |
| int i; |
| |
| g = getgrnam(*argv); |
| if (g == 0) { |
| option_error("group %s is unknown", *argv); |
| return 0; |
| } |
| for (i = 0; i < ngroups; ++i) { |
| if (groups[i] == g->gr_gid) { |
| privileged = 1; |
| break; |
| } |
| } |
| return 1; |
| } |
| |
| |
| /* |
| * set_noauth_addr - set address(es) that can be used without authentication. |
| * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets. |
| */ |
| static int |
| set_noauth_addr(argv) |
| char **argv; |
| { |
| char *addr = *argv; |
| int l = strlen(addr) + 1; |
| struct wordlist *wp; |
| |
| wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); |
| if (wp == NULL) |
| novm("allow-ip argument"); |
| wp->word = (char *) (wp + 1); |
| wp->next = noauth_addrs; |
| BCOPY(addr, wp->word, l); |
| noauth_addrs = wp; |
| return 1; |
| } |
| |
| |
| /* |
| * set_permitted_number - set remote telephone number(s) that may connect. |
| */ |
| static int |
| set_permitted_number(argv) |
| char **argv; |
| { |
| char *number = *argv; |
| int l = strlen(number) + 1; |
| struct wordlist *wp; |
| |
| wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); |
| if (wp == NULL) |
| novm("allow-number argument"); |
| wp->word = (char *) (wp + 1); |
| wp->next = permitted_numbers; |
| BCOPY(number, wp->word, l); |
| permitted_numbers = wp; |
| return 1; |
| } |
| |
| |
| /* |
| * An Open on LCP has requested a change from Dead to Establish phase. |
| * Do what's necessary to bring the physical layer up. |
| */ |
| void |
| link_required(unit) |
| int unit; |
| { |
| new_phase(PHASE_SERIALCONN); |
| |
| devfd = the_channel->connect(); |
| if (devfd < 0) |
| goto fail; |
| |
| /* set up the serial device as a ppp interface */ |
| /* |
| * N.B. we used to do tdb_writelock/tdb_writeunlock around this |
| * (from establish_ppp to set_ifunit). However, we won't be |
| * doing the set_ifunit in multilink mode, which is the only time |
| * we need the atomicity that the tdb_writelock/tdb_writeunlock |
| * gives us. Thus we don't need the tdb_writelock/tdb_writeunlock. |
| */ |
| fd_ppp = the_channel->establish_ppp(devfd); |
| if (fd_ppp < 0) { |
| status = EXIT_FATAL_ERROR; |
| goto disconnect; |
| } |
| |
| if (!demand && ifunit >= 0) |
| set_ifunit(1); |
| |
| /* |
| * Start opening the connection and wait for |
| * incoming events (reply, timeout, etc.). |
| */ |
| if (ifunit >= 0) |
| notice("Connect: %s <--> %s", ifname, ppp_devnam); |
| else |
| notice("Starting negotiation on %s", ppp_devnam); |
| add_fd(fd_ppp); |
| |
| status = EXIT_NEGOTIATION_FAILED; |
| new_phase(PHASE_ESTABLISH); |
| |
| lcp_lowerup(0); |
| return; |
| |
| disconnect: |
| new_phase(PHASE_DISCONNECT); |
| if (the_channel->disconnect) |
| the_channel->disconnect(); |
| |
| fail: |
| new_phase(PHASE_DEAD); |
| if (the_channel->cleanup) |
| (*the_channel->cleanup)(); |
| |
| } |
| |
| /* |
| * LCP has terminated the link; go to the Dead phase and take the |
| * physical layer down. |
| */ |
| void |
| link_terminated(unit) |
| int unit; |
| { |
| if (phase == PHASE_DEAD || phase == PHASE_MASTER) |
| return; |
| new_phase(PHASE_DISCONNECT); |
| |
| if (pap_logout_hook) { |
| pap_logout_hook(); |
| } else { |
| if (logged_in) |
| plogout(); |
| } |
| |
| if (!doing_multilink) { |
| notice("Connection terminated."); |
| print_link_stats(); |
| } else |
| notice("Link terminated."); |
| |
| /* |
| * Delete pid files before disestablishing ppp. Otherwise it |
| * can happen that another pppd gets the same unit and then |
| * we delete its pid file. |
| */ |
| if (!doing_multilink && !demand) |
| remove_pidfiles(); |
| |
| /* |
| * If we may want to bring the link up again, transfer |
| * the ppp unit back to the loopback. Set the |
| * real serial device back to its normal mode of operation. |
| */ |
| if (fd_ppp >= 0) { |
| remove_fd(fd_ppp); |
| clean_check(); |
| the_channel->disestablish_ppp(devfd); |
| if (doing_multilink) |
| mp_exit_bundle(); |
| fd_ppp = -1; |
| } |
| if (!hungup) |
| lcp_lowerdown(0); |
| if (!doing_multilink && !demand) |
| script_unsetenv("IFNAME"); |
| |
| /* |
| * Run disconnector script, if requested. |
| * XXX we may not be able to do this if the line has hung up! |
| */ |
| if (devfd >= 0 && the_channel->disconnect) { |
| the_channel->disconnect(); |
| devfd = -1; |
| } |
| |
| if (doing_multilink && multilink_master) { |
| if (!bundle_terminating) |
| new_phase(PHASE_MASTER); |
| else |
| mp_bundle_terminated(); |
| } else |
| new_phase(PHASE_DEAD); |
| } |
| |
| /* |
| * LCP has gone down; it will either die or try to re-establish. |
| */ |
| void |
| link_down(unit) |
| int unit; |
| { |
| if (auth_state != s_down) { |
| notify(link_down_notifier, 0); |
| auth_state = s_down; |
| if (auth_script_state == s_up && auth_script_pid == 0) { |
| update_link_stats(unit); |
| auth_script_state = s_down; |
| auth_script(_PATH_AUTHDOWN); |
| } |
| } |
| if (!doing_multilink) { |
| upper_layers_down(unit); |
| if (phase != PHASE_DEAD && phase != PHASE_MASTER) |
| new_phase(PHASE_ESTABLISH); |
| } |
| /* XXX if doing_multilink, should do something to stop |
| network-layer traffic on the link */ |
| } |
| |
| void upper_layers_down(int unit) |
| { |
| int i; |
| struct protent *protp; |
| |
| for (i = 0; (protp = protocols[i]) != NULL; ++i) { |
| if (!protp->enabled_flag) |
| continue; |
| if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) |
| (*protp->lowerdown)(unit); |
| if (protp->protocol < 0xC000 && protp->close != NULL) |
| (*protp->close)(unit, "LCP down"); |
| } |
| num_np_open = 0; |
| num_np_up = 0; |
| } |
| |
| /* |
| * The link is established. |
| * Proceed to the Dead, Authenticate or Network phase as appropriate. |
| */ |
| void |
| link_established(unit) |
| int unit; |
| { |
| int auth; |
| lcp_options *wo = &lcp_wantoptions[unit]; |
| lcp_options *go = &lcp_gotoptions[unit]; |
| lcp_options *ho = &lcp_hisoptions[unit]; |
| int i; |
| struct protent *protp; |
| |
| /* |
| * Tell higher-level protocols that LCP is up. |
| */ |
| if (!doing_multilink) { |
| for (i = 0; (protp = protocols[i]) != NULL; ++i) |
| if (protp->protocol != PPP_LCP && protp->enabled_flag |
| && protp->lowerup != NULL) |
| (*protp->lowerup)(unit); |
| } |
| |
| if (!auth_required && noauth_addrs != NULL) |
| set_allowed_addrs(unit, NULL, NULL); |
| |
| if (auth_required && !(go->neg_upap || go->neg_chap || go->neg_eap)) { |
| /* |
| * We wanted the peer to authenticate itself, and it refused: |
| * if we have some address(es) it can use without auth, fine, |
| * otherwise treat it as though it authenticated with PAP using |
| * a username of "" and a password of "". If that's not OK, |
| * boot it out. |
| */ |
| if (noauth_addrs != NULL) { |
| set_allowed_addrs(unit, NULL, NULL); |
| } else if (!wo->neg_upap || uselogin || !null_login(unit)) { |
| warn("peer refused to authenticate: terminating link"); |
| lcp_close(unit, "peer refused to authenticate"); |
| status = EXIT_PEER_AUTH_FAILED; |
| return; |
| } |
| } |
| |
| new_phase(PHASE_AUTHENTICATE); |
| used_login = 0; |
| auth = 0; |
| if (go->neg_eap) { |
| eap_authpeer(unit, our_name); |
| auth |= EAP_PEER; |
| } else if (go->neg_chap) { |
| chap_auth_peer(unit, our_name, CHAP_DIGEST(go->chap_mdtype)); |
| auth |= CHAP_PEER; |
| } else if (go->neg_upap) { |
| upap_authpeer(unit); |
| auth |= PAP_PEER; |
| } |
| if (ho->neg_eap) { |
| eap_authwithpeer(unit, user); |
| auth |= EAP_WITHPEER; |
| } else if (ho->neg_chap) { |
| chap_auth_with_peer(unit, user, CHAP_DIGEST(ho->chap_mdtype)); |
| auth |= CHAP_WITHPEER; |
| } else if (ho->neg_upap) { |
| if (passwd[0] == 0) { |
| passwd_from_file = 1; |
| if (!get_pap_passwd(passwd)) |
| error("No secret found for PAP login"); |
| } |
| upap_authwithpeer(unit, user, passwd); |
| auth |= PAP_WITHPEER; |
| } |
| auth_pending[unit] = auth; |
| auth_done[unit] = 0; |
| |
| if (!auth) |
| network_phase(unit); |
| } |
| |
| /* |
| * Proceed to the network phase. |
| */ |
| static void |
| network_phase(unit) |
| int unit; |
| { |
| lcp_options *go = &lcp_gotoptions[unit]; |
| |
| /* Log calling number. */ |
| if (*remote_number) |
| notice("peer from calling number %q authorized", remote_number); |
| |
| /* |
| * If the peer had to authenticate, run the auth-up script now. |
| */ |
| if (go->neg_chap || go->neg_upap || go->neg_eap) { |
| notify(auth_up_notifier, 0); |
| auth_state = s_up; |
| if (auth_script_state == s_down && auth_script_pid == 0) { |
| auth_script_state = s_up; |
| auth_script(_PATH_AUTHUP); |
| } |
| } |
| |
| #ifdef CBCP_SUPPORT |
| /* |
| * If we negotiated callback, do it now. |
| */ |
| if (go->neg_cbcp) { |
| new_phase(PHASE_CALLBACK); |
| (*cbcp_protent.open)(unit); |
| return; |
| } |
| #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(unit); |
| } |
| |
| void |
| start_networks(unit) |
| int unit; |
| { |
| int i; |
| struct protent *protp; |
| int ecp_required, mppe_required; |
| |
| new_phase(PHASE_NETWORK); |
| |
| #ifdef HAVE_MULTILINK |
| if (multilink) { |
| if (mp_join_bundle()) { |
| if (updetach && !nodetach) |
| detach(); |
| return; |
| } |
| } |
| #endif /* HAVE_MULTILINK */ |
| |
| #ifdef PPP_FILTER |
| if (!demand) |
| set_filters(&pass_filter, &active_filter); |
| #endif |
| /* Start CCP and ECP */ |
| for (i = 0; (protp = protocols[i]) != NULL; ++i) |
| if ((protp->protocol == PPP_ECP || protp->protocol == PPP_CCP) |
| && protp->enabled_flag && protp->open != NULL) |
| (*protp->open)(0); |
| |
| /* |
| * Bring up other network protocols iff encryption is not required. |
| */ |
| ecp_required = ecp_gotoptions[unit].required; |
| mppe_required = ccp_gotoptions[unit].mppe; |
| if (!ecp_required && !mppe_required) |
| continue_networks(unit); |
| } |
| |
| void |
| continue_networks(unit) |
| int unit; |
| { |
| int i; |
| struct protent *protp; |
| |
| /* |
| * Start the "real" network protocols. |
| */ |
| for (i = 0; (protp = protocols[i]) != NULL; ++i) |
| if (protp->protocol < 0xC000 |
| && protp->protocol != PPP_CCP && protp->protocol != PPP_ECP |
| && protp->enabled_flag && protp->open != NULL) { |
| (*protp->open)(0); |
| ++num_np_open; |
| } |
| |
| if (num_np_open == 0) |
| /* nothing to do */ |
| lcp_close(0, "No network protocols running"); |
| } |
| |
| /* |
| * The peer has failed to authenticate himself using `protocol'. |
| */ |
| void |
| auth_peer_fail(unit, protocol) |
| int unit, protocol; |
| { |
| /* |
| * Authentication failure: take the link down |
| */ |
| lcp_close(unit, "Authentication failed"); |
| status = EXIT_PEER_AUTH_FAILED; |
| } |
| |
| /* |
| * The peer has been successfully authenticated using `protocol'. |
| */ |
| void |
| auth_peer_success(unit, protocol, prot_flavor, name, namelen) |
| int unit, protocol, prot_flavor; |
| char *name; |
| int namelen; |
| { |
| int bit; |
| |
| switch (protocol) { |
| case PPP_CHAP: |
| bit = CHAP_PEER; |
| switch (prot_flavor) { |
| case CHAP_MD5: |
| bit |= CHAP_MD5_PEER; |
| break; |
| #ifdef CHAPMS |
| case CHAP_MICROSOFT: |
| bit |= CHAP_MS_PEER; |
| break; |
| case CHAP_MICROSOFT_V2: |
| bit |= CHAP_MS2_PEER; |
| break; |
| #endif |
| } |
| break; |
| case PPP_PAP: |
| bit = PAP_PEER; |
| break; |
| case PPP_EAP: |
| bit = EAP_PEER; |
| break; |
| default: |
| warn("auth_peer_success: unknown protocol %x", protocol); |
| return; |
| } |
| |
| /* |
| * Save the authenticated name of the peer for later. |
| */ |
| if (namelen > sizeof(peer_authname) - 1) |
| namelen = sizeof(peer_authname) - 1; |
| BCOPY(name, peer_authname, namelen); |
| peer_authname[namelen] = 0; |
| script_setenv("PEERNAME", peer_authname, 0); |
| |
| /* Save the authentication method for later. */ |
| auth_done[unit] |= bit; |
| |
| /* |
| * If there is no more authentication still to be done, |
| * proceed to the network (or callback) phase. |
| */ |
| if ((auth_pending[unit] &= ~bit) == 0) |
| network_phase(unit); |
| } |
| |
| /* |
| * We have failed to authenticate ourselves to the peer using `protocol'. |
| */ |
| void |
| auth_withpeer_fail(unit, protocol) |
| int unit, protocol; |
| { |
| if (passwd_from_file) |
| BZERO(passwd, MAXSECRETLEN); |
| /* |
| * We've failed to authenticate ourselves to our peer. |
| * 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; |
| } |
| |
| /* |
| * We have successfully authenticated ourselves with the peer using `protocol'. |
| */ |
| void |
| auth_withpeer_success(unit, protocol, prot_flavor) |
| int unit, protocol, prot_flavor; |
| { |
| int bit; |
| |
| switch (protocol) { |
| case PPP_CHAP: |
| bit = CHAP_WITHPEER; |
| switch (prot_flavor) { |
| case CHAP_MD5: |
| bit |= CHAP_MD5_WITHPEER; |
| break; |
| #ifdef CHAPMS |
| case CHAP_MICROSOFT: |
| bit |= CHAP_MS_WITHPEER; |
| break; |
| case CHAP_MICROSOFT_V2: |
| bit |= CHAP_MS2_WITHPEER; |
| break; |
| #endif |
| } |
| break; |
| case PPP_PAP: |
| if (passwd_from_file) |
| BZERO(passwd, MAXSECRETLEN); |
| bit = PAP_WITHPEER; |
| break; |
| case PPP_EAP: |
| bit = EAP_WITHPEER; |
| break; |
| default: |
| warn("auth_withpeer_success: unknown protocol %x", protocol); |
| bit = 0; |
| } |
| |
| /* Save the authentication method for later. */ |
| auth_done[unit] |= bit; |
| |
| /* |
| * If there is no more authentication still being done, |
| * proceed to the network (or callback) phase. |
| */ |
| if ((auth_pending[unit] &= ~bit) == 0) |
| network_phase(unit); |
| } |
| |
| |
| /* |
| * np_up - a network protocol has come up. |
| */ |
| void |
| np_up(unit, proto) |
| int unit, proto; |
| { |
| int tlim; |
| |
| if (num_np_up == 0) { |
| /* |
| * At this point we consider that the link has come up successfully. |
| */ |
| status = EXIT_OK; |
| unsuccess = 0; |
| new_phase(PHASE_RUNNING); |
| |
| if (idle_time_hook != 0) |
| tlim = (*idle_time_hook)(NULL); |
| else |
| tlim = idle_time_limit; |
| if (tlim > 0) |
| TIMEOUT(check_idle, NULL, tlim); |
| |
| /* |
| * Set a timeout to close the connection once the maximum |
| * connect time has expired. |
| */ |
| if (maxconnect > 0) |
| TIMEOUT(connect_time_expired, 0, maxconnect); |
| |
| #ifdef MAXOCTETS |
| if (maxoctets > 0) |
| TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); |
| #endif |
| |
| /* |
| * Detach now, if the updetach option was given. |
| */ |
| if (updetach && !nodetach) |
| detach(); |
| } |
| ++num_np_up; |
| } |
| |
| /* |
| * np_down - a network protocol has gone down. |
| */ |
| void |
| np_down(unit, proto) |
| int unit, proto; |
| { |
| if (--num_np_up == 0) { |
| UNTIMEOUT(check_idle, NULL); |
| UNTIMEOUT(connect_time_expired, NULL); |
| #ifdef MAXOCTETS |
| UNTIMEOUT(check_maxoctets, NULL); |
| #endif |
| new_phase(PHASE_NETWORK); |
| } |
| } |
| |
| /* |
| * np_finished - a network protocol has finished using the link. |
| */ |
| void |
| np_finished(unit, proto) |
| int unit, proto; |
| { |
| if (--num_np_open <= 0) { |
| /* no further use for the link: shut up shop. */ |
| lcp_close(0, "No network protocols running"); |
| } |
| } |
| |
| #ifdef MAXOCTETS |
| static void |
| check_maxoctets(arg) |
| void *arg; |
| { |
| int diff; |
| unsigned int used; |
| |
| update_link_stats(ifunit); |
| link_stats_valid=0; |
| |
| switch(maxoctets_dir) { |
| case PPP_OCTETS_DIRECTION_IN: |
| used = link_stats.bytes_in; |
| break; |
| case PPP_OCTETS_DIRECTION_OUT: |
| used = link_stats.bytes_out; |
| break; |
| case PPP_OCTETS_DIRECTION_MAXOVERAL: |
| case PPP_OCTETS_DIRECTION_MAXSESSION: |
| used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out; |
| break; |
| default: |
| used = link_stats.bytes_in+link_stats.bytes_out; |
| break; |
| } |
| diff = maxoctets - used; |
| if(diff < 0) { |
| notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used); |
| lcp_close(0, "Traffic limit"); |
| need_holdoff = 0; |
| status = EXIT_TRAFFIC_LIMIT; |
| } else { |
| TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); |
| } |
| } |
| #endif |
| |
| /* |
| * check_idle - check whether the link has been idle for long |
| * enough that we can shut it down. |
| */ |
| static void |
| check_idle(arg) |
| void *arg; |
| { |
| struct ppp_idle idle; |
| time_t itime; |
| int tlim; |
| |
| if (!get_idle_time(0, &idle)) |
| return; |
| if (idle_time_hook != 0) { |
| tlim = idle_time_hook(&idle); |
| } else { |
| itime = MIN(idle.xmit_idle, idle.recv_idle); |
| tlim = idle_time_limit - itime; |
| } |
| if (tlim <= 0) { |
| /* link is idle: shut it down. */ |
| 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, tlim); |
| } |
| } |
| |
| /* |
| * connect_time_expired - log a message and close the connection. |
| */ |
| static void |
| connect_time_expired(arg) |
| void *arg; |
| { |
| info("Connect time expired"); |
| status = EXIT_CONNECT_TIME; |
| lcp_close(0, "Connect time expired"); /* Close connection */ |
| } |
| |
| /* |
| * auth_check_options - called to check authentication options. |
| */ |
| void |
| auth_check_options() |
| { |
| lcp_options *wo = &lcp_wantoptions[0]; |
| int can_auth; |
| int lacks_ip; |
| |
| /* Default our_name to hostname, and user to our_name */ |
| if (our_name[0] == 0 || usehostname) |
| strlcpy(our_name, hostname, sizeof(our_name)); |
| if (user[0] == 0) |
| strlcpy(user, our_name, sizeof(user)); |
| |
| /* |
| * If we have a default route, require the peer to authenticate |
| * unless the noauth option was given or the real user is root. |
| */ |
| if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) { |
| auth_required = 1; |
| default_auth = 1; |
| } |
| |
| /* If we selected any CHAP flavors, we should probably negotiate it. :-) */ |
| if (wo->chap_mdtype) |
| wo->neg_chap = 1; |
| |
| /* If authentication is required, ask peer for CHAP, PAP, or EAP. */ |
| if (auth_required) { |
| allow_any_ip = 0; |
| if (!wo->neg_chap && !wo->neg_upap && !wo->neg_eap) { |
| wo->neg_chap = chap_mdtype_all != MDTYPE_NONE; |
| wo->chap_mdtype = chap_mdtype_all; |
| wo->neg_upap = 1; |
| wo->neg_eap = 1; |
| } |
| } else { |
| wo->neg_chap = 0; |
| wo->chap_mdtype = MDTYPE_NONE; |
| wo->neg_upap = 0; |
| wo->neg_eap = 0; |
| } |
| |
| /* |
| * Check whether we have appropriate secrets to use |
| * to authenticate the peer. Note that EAP can authenticate by way |
| * of a CHAP-like exchanges as well as SRP. |
| */ |
| lacks_ip = 0; |
| can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip)); |
| if (!can_auth && (wo->neg_chap || wo->neg_eap)) { |
| can_auth = have_chap_secret((explicit_remote? remote_name: NULL), |
| our_name, 1, &lacks_ip); |
| } |
| if (!can_auth && wo->neg_eap) { |
| can_auth = have_srp_secret((explicit_remote? remote_name: NULL), |
| our_name, 1, &lacks_ip); |
| } |
| |
| if (auth_required && !can_auth && noauth_addrs == NULL) { |
| if (default_auth) { |
| option_error( |
| "By default the remote system is required to authenticate itself"); |
| option_error( |
| "(because this system has a default route to the internet)"); |
| } else if (explicit_remote) |
| option_error( |
| "The remote system (%s) is required to authenticate itself", |
| remote_name); |
| else |
| option_error( |
| "The remote system is required to authenticate itself"); |
| option_error( |
| "but I couldn't find any suitable secret (password) for it to use to do so."); |
| if (lacks_ip) |
| option_error( |
| "(None of the available passwords would let it use an IP address.)"); |
| |
| exit(1); |
| } |
| |
| /* |
| * Early check for remote number authorization. |
| */ |
| if (!auth_number()) { |
| warn("calling number %q is not authorized", remote_number); |
| exit(EXIT_CNID_AUTH_FAILED); |
| } |
| } |
| |
| /* |
| * auth_reset - called when LCP is starting negotiations to recheck |
| * authentication options, i.e. whether we have appropriate secrets |
| * to use for authenticating ourselves and/or the peer. |
| */ |
| void |
| auth_reset(unit) |
| int unit; |
| { |
| lcp_options *go = &lcp_gotoptions[unit]; |
| lcp_options *ao = &lcp_allowoptions[unit]; |
| int hadchap; |
| |
| hadchap = -1; |
| ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); |
| ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2) |
| && (passwd[0] != 0 || |
| (hadchap = have_chap_secret(user, (explicit_remote? remote_name: |
| NULL), 0, NULL))); |
| ao->neg_eap = !refuse_eap && ( |
| passwd[0] != 0 || |
| (hadchap == 1 || (hadchap == -1 && have_chap_secret(user, |
| (explicit_remote? remote_name: NULL), 0, NULL))) || |
| have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL)); |
| |
| hadchap = -1; |
| if (go->neg_upap && !uselogin && !have_pap_secret(NULL)) |
| go->neg_upap = 0; |
| if (go->neg_chap) { |
| if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL), |
| our_name, 1, NULL))) |
| go->neg_chap = 0; |
| } |
| if (go->neg_eap && |
| (hadchap == 0 || (hadchap == -1 && |
| !have_chap_secret((explicit_remote? remote_name: NULL), our_name, |
| 1, NULL))) && |
| !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1, |
| NULL)) |
| go->neg_eap = 0; |
| } |
| |
| |
| /* |
| * check_passwd - Check the user name and passwd against the PAP secrets |
| * file. If requested, also check against the system password database, |
| * and login the user if OK. |
| * |
| * returns: |
| * UPAP_AUTHNAK: Authentication failed. |
| * UPAP_AUTHACK: Authentication succeeded. |
| * In either case, msg points to an appropriate message. |
| */ |
| int |
| check_passwd(unit, auser, userlen, apasswd, passwdlen, msg) |
| int unit; |
| char *auser; |
| int userlen; |
| char *apasswd; |
| int passwdlen; |
| char **msg; |
| { |
| #if 1 |
| return UPAP_AUTHNAK; |
| #else |
| int ret; |
| char *filename; |
| FILE *f; |
| struct wordlist *addrs = NULL, *opts = NULL; |
| char passwd[256], user[256]; |
| char secret[MAXWORDLEN]; |
| static int attempts = 0; |
| |
| /* |
| * Make copies of apasswd and auser, then null-terminate them. |
| * If there are unprintable characters in the password, make |
| * them visible. |
| */ |
| slprintf(passwd, sizeof(passwd), "%.*v", passwdlen, apasswd); |
| slprintf(user, sizeof(user), "%.*v", userlen, auser); |
| *msg = ""; |
| |
| /* |
| * Check if a plugin wants to handle this. |
| */ |
| if (pap_auth_hook) { |
| ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts); |
| if (ret >= 0) { |
| /* note: set_allowed_addrs() saves opts (but not addrs): |
| don't free it! */ |
| if (ret) |
| set_allowed_addrs(unit, addrs, opts); |
| else if (opts != 0) |
| free_wordlist(opts); |
| if (addrs != 0) |
| free_wordlist(addrs); |
| BZERO(passwd, sizeof(passwd)); |
| return ret? UPAP_AUTHACK: UPAP_AUTHNAK; |
| } |
| } |
| |
| /* |
| * Open the file of pap secrets and scan for a suitable secret |
| * for authenticating this user. |
| */ |
| filename = _PATH_UPAPFILE; |
| addrs = opts = NULL; |
| ret = UPAP_AUTHNAK; |
| f = fopen(filename, "r"); |
| if (f == NULL) { |
| error("Can't open PAP password file %s: %m", filename); |
| |
| } else { |
| check_access(f, filename); |
| if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename, 0) < 0) { |
| warn("no PAP secret found for %s", user); |
| } else { |
| /* |
| * If the secret is "@login", it means to check |
| * the password against the login database. |
| */ |
| int login_secret = strcmp(secret, "@login") == 0; |
| ret = UPAP_AUTHACK; |
| if (uselogin || login_secret) { |
| /* login option or secret is @login */ |
| if ((ret = plogin(user, passwd, msg)) == UPAP_AUTHACK) |
| used_login = 1; |
| } |
| if (secret[0] != 0 && !login_secret) { |
| /* password given in pap-secrets - must match */ |
| if ((cryptpap || strcmp(passwd, secret) != 0) |
| && strcmp(crypt(passwd, secret), secret) != 0) |
| ret = UPAP_AUTHNAK; |
| } |
| } |
| fclose(f); |
| } |
| |
| if (ret == UPAP_AUTHNAK) { |
| if (**msg == 0) |
| *msg = "Login incorrect"; |
| /* |
| * XXX can we ever get here more than once?? |
| * Frustrate passwd stealer programs. |
| * Allow 10 tries, but start backing off after 3 (stolen from login). |
| * On 10'th, drop the connection. |
| */ |
| if (attempts++ >= 10) { |
| warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user); |
| lcp_close(unit, "login failed"); |
| } |
| if (attempts > 3) |
| sleep((u_int) (attempts - 3) * 5); |
| if (opts != NULL) |
| free_wordlist(opts); |
| |
| } else { |
| attempts = 0; /* Reset count */ |
| if (**msg == 0) |
| *msg = "Login ok"; |
| set_allowed_addrs(unit, addrs, opts); |
| } |
| |
| if (addrs != NULL) |
| free_wordlist(addrs); |
| BZERO(passwd, sizeof(passwd)); |
| BZERO(secret, sizeof(secret)); |
| |
| return ret; |
| #endif |
| } |
| |
| /* |
| * This function is needed for PAM. |
| */ |
| |
| #ifdef USE_PAM |
| /* 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, |
| #ifndef SOL2 |
| const |
| #endif |
| struct pam_message **msg, |
| struct pam_response **resp, void *appdata_ptr) |
| { |
| int replies = 0; |
| struct pam_response *reply = NULL; |
| |
| #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; |
| } |
| |
| static struct pam_conv PAM_conversation = { |
| &PAM_conv, |
| NULL |
| }; |
| #endif /* USE_PAM */ |
| |
| /* |
| * plogin - Check the user name and password against the system |
| * password database, and login the user if OK. |
| * |
| * returns: |
| * UPAP_AUTHNAK: Login failed. |
| * UPAP_AUTHACK: Login succeeded. |
| * In either case, msg points to an appropriate message. |
| */ |
| |
| static int |
| plogin(user, passwd, msg) |
| char *user; |
| char *passwd; |
| char **msg; |
| { |
| char *tty; |
| |
| #if 1 |
| return UPAP_AUTHNAK; |
| #else |
| #ifdef USE_PAM |
| int pam_error; |
| |
| pam_error = pam_start ("ppp", user, &PAM_conversation, &pamh); |
| if (pam_error != PAM_SUCCESS) { |
| *msg = (char *) pam_strerror (pamh, pam_error); |
| reopen_log(); |
| return UPAP_AUTHNAK; |
| } |
| /* |
| * Define the fields for the credential validation |
| */ |
| |
| PAM_username = user; |
| PAM_password = passwd; |
| 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 && !PAM_error) { |
| pam_error = pam_acct_mgmt (pamh, PAM_SILENT); |
| if (pam_error == PAM_SUCCESS) |
| pam_error = pam_open_session (pamh, PAM_SILENT); |
| } |
| |
| *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 |
| */ |
| |
| #ifdef HAS_SHADOW |
| struct spwd *spwd; |
| struct spwd *getspnam(); |
| #endif |
| struct passwd *pw = getpwnam(user); |
| |
| endpwent(); |
| if (pw == NULL) |
| return (UPAP_AUTHNAK); |
| |
| #ifdef HAS_SHADOW |
| spwd = getspnam(user); |
| endspent(); |
| 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; |
| } |
| #endif |
| |
| /* |
| * If no passwd, don't let them login. |
| */ |
| if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2 |
| || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0) |
| return (UPAP_AUTHNAK); |
| |
| #endif /* #ifdef USE_PAM */ |
| |
| /* |
| * Write a wtmp entry for this user. |
| */ |
| |
| tty = devnam; |
| if (strncmp(tty, "/dev/", 5) == 0) |
| tty += 5; |
| logwtmp(tty, user, ifname); /* Add wtmp login entry */ |
| |
| #if defined(_PATH_LASTLOG) && !defined(USE_PAM) |
| if (pw != (struct passwd *)NULL) { |
| struct lastlog ll; |
| int fd; |
| |
| if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { |
| (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET); |
| memset((void *)&ll, 0, sizeof(ll)); |
| (void)time(&ll.ll_time); |
| (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); |
| (void)write(fd, (char *)&ll, sizeof(ll)); |
| (void)close(fd); |
| } |
| } |
| #endif /* _PATH_LASTLOG and not USE_PAM */ |
| |
| info("user %s logged in", user); |
| logged_in = 1; |
| |
| return (UPAP_AUTHACK); |
| #endif |
| } |
| |
| /* |
| * plogout - Logout the user. |
| */ |
| static void |
| plogout() |
| { |
| #ifdef USE_PAM |
| int pam_error; |
| |
| 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 /* ! USE_PAM */ |
| logged_in = 0; |
| } |
| |
| |
| /* |
| * null_login - Check if a username of "" and a password of "" are |
| * acceptable, and iff so, set the list of acceptable IP addresses |
| * and return 1. |
| */ |
| static int |
| null_login(unit) |
| int unit; |
| { |
| char *filename; |
| FILE *f; |
| int i, ret; |
| struct wordlist *addrs, *opts; |
| char secret[MAXWORDLEN]; |
| |
| /* |
| * Check if a plugin wants to handle this. |
| */ |
| ret = -1; |
| if (null_auth_hook) |
| ret = (*null_auth_hook)(&addrs, &opts); |
| |
| /* |
| * Open the file of pap secrets and scan for a suitable secret. |
| */ |
| if (ret <= 0) { |
| filename = _PATH_UPAPFILE; |
| addrs = NULL; |
| f = fopen(filename, "r"); |
| if (f == NULL) |
| return 0; |
| check_access(f, filename); |
| |
| i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0); |
| ret = i >= 0 && secret[0] == 0; |
| BZERO(secret, sizeof(secret)); |
| fclose(f); |
| } |
| |
| if (ret) |
| set_allowed_addrs(unit, addrs, opts); |
| else if (opts != 0) |
| free_wordlist(opts); |
| if (addrs != 0) |
| free_wordlist(addrs); |
| |
| return ret; |
| } |
| |
| |
| /* |
| * get_pap_passwd - get a password for authenticating ourselves with |
| * our peer using PAP. Returns 1 on success, 0 if no suitable password |
| * could be found. |
| * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null). |
| */ |
| static int |
| get_pap_passwd(passwd) |
| char *passwd; |
| { |
| char *filename; |
| FILE *f; |
| int ret; |
| char secret[MAXWORDLEN]; |
| |
| /* |
| * Check whether a plugin wants to supply this. |
| */ |
| if (pap_passwd_hook) { |
| ret = (*pap_passwd_hook)(user, passwd); |
| if (ret >= 0) |
| return ret; |
| } |
| |
| filename = _PATH_UPAPFILE; |
| f = fopen(filename, "r"); |
| if (f == NULL) |
| return 0; |
| check_access(f, filename); |
| ret = scan_authfile(f, user, |
| (remote_name[0]? remote_name: NULL), |
| secret, NULL, NULL, filename, 0); |
| fclose(f); |
| if (ret < 0) |
| return 0; |
| if (passwd != NULL) |
| strlcpy(passwd, secret, MAXSECRETLEN); |
| BZERO(secret, sizeof(secret)); |
| return 1; |
| } |
| |
| |
| /* |
| * have_pap_secret - check whether we have a PAP file with any |
| * secrets that we could possibly use for authenticating the peer. |
| */ |
| static int |
| have_pap_secret(lacks_ipp) |
| int *lacks_ipp; |
| { |
| FILE *f; |
| int ret; |
| char *filename; |
| struct wordlist *addrs; |
| |
| /* let the plugin decide, if there is one */ |
| if (pap_check_hook) { |
| ret = (*pap_check_hook)(); |
| if (ret >= 0) |
| return ret; |
| } |
| |
| filename = _PATH_UPAPFILE; |
| f = fopen(filename, "r"); |
| if (f == NULL) |
| return 0; |
| |
| ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name, |
| NULL, &addrs, NULL, filename, 0); |
| fclose(f); |
| if (ret >= 0 && !some_ip_ok(addrs)) { |
| if (lacks_ipp != 0) |
| *lacks_ipp = 1; |
| ret = -1; |
| } |
| if (addrs != 0) |
| free_wordlist(addrs); |
| |
| return ret >= 0; |
| } |
| |
| |
| /* |
| * have_chap_secret - check whether we have a CHAP file with a |
| * secret that we could possibly use for authenticating `client' |
| * on `server'. Either can be the null string, meaning we don't |
| * know the identity yet. |
| */ |
| static int |
| have_chap_secret(client, server, need_ip, lacks_ipp) |
| char *client; |
| char *server; |
| int need_ip; |
| int *lacks_ipp; |
| { |
| FILE *f; |
| int ret; |
| char *filename; |
| struct wordlist *addrs; |
| |
| if (chap_check_hook) { |
| ret = (*chap_check_hook)(); |
| if (ret >= 0) { |
| return ret; |
| } |
| } |
| |
| filename = _PATH_CHAPFILE; |
| f = fopen(filename, "r"); |
| if (f == NULL) |
| return 0; |
| |
| if (client != NULL && client[0] == 0) |
| client = NULL; |
| else if (server != NULL && server[0] == 0) |
| server = NULL; |
| |
| ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); |
| fclose(f); |
| if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { |
| if (lacks_ipp != 0) |
| *lacks_ipp = 1; |
| ret = -1; |
| } |
| if (addrs != 0) |
| free_wordlist(addrs); |
| |
| return ret >= 0; |
| } |
| |
| |
| /* |
| * have_srp_secret - check whether we have a SRP file with a |
| * secret that we could possibly use for authenticating `client' |
| * on `server'. Either can be the null string, meaning we don't |
| * know the identity yet. |
| */ |
| static int |
| have_srp_secret(client, server, need_ip, lacks_ipp) |
| char *client; |
| char *server; |
| int need_ip; |
| int *lacks_ipp; |
| { |
| FILE *f; |
| int ret; |
| char *filename; |
| struct wordlist *addrs; |
| |
| filename = _PATH_SRPFILE; |
| f = fopen(filename, "r"); |
| if (f == NULL) |
| return 0; |
| |
| if (client != NULL && client[0] == 0) |
| client = NULL; |
| else if (server != NULL && server[0] == 0) |
| server = NULL; |
| |
| ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); |
| fclose(f); |
| if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { |
| if (lacks_ipp != 0) |
| *lacks_ipp = 1; |
| ret = -1; |
| } |
| if (addrs != 0) |
| free_wordlist(addrs); |
| |
| return ret >= 0; |
| } |
| |
| |
| /* |
| * get_secret - open the CHAP secret file and return the secret |
| * for authenticating the given client on the given server. |
| * (We could be either client or server). |
| */ |
| int |
| get_secret(unit, client, server, secret, secret_len, am_server) |
| int unit; |
| char *client; |
| char *server; |
| char *secret; |
| int *secret_len; |
| int am_server; |
| { |
| FILE *f; |
| int ret, len; |
| char *filename; |
| struct wordlist *addrs, *opts; |
| char secbuf[MAXWORDLEN]; |
| |
| if (!am_server && passwd[0] != 0) { |
| strlcpy(secbuf, passwd, sizeof(secbuf)); |
| } else if (!am_server && chap_passwd_hook) { |
| if ( (*chap_passwd_hook)(client, secbuf) < 0) { |
| error("Unable to obtain CHAP password for %s on %s from plugin", |
| client, server); |
| return 0; |
| } |
| } 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, &opts, filename, 0); |
| fclose(f); |
| if (ret < 0) |
| return 0; |
| |
| if (am_server) |
| set_allowed_addrs(unit, addrs, opts); |
| else if (opts != 0) |
| free_wordlist(opts); |
| if (addrs != 0) |
| free_wordlist(addrs); |
| } |
| |
| len = strlen(secbuf); |
| if (len > MAXSECRETLEN) { |
| error("Secret for %s on %s is too long", client, server); |
| len = MAXSECRETLEN; |
| } |
| BCOPY(secbuf, secret, len); |
| BZERO(secbuf, sizeof(secbuf)); |
| *secret_len = len; |
| |
| return 1; |
| } |
| |
| |
| /* |
| * get_srp_secret - open the SRP secret file and return the secret |
| * for authenticating the given client on the given server. |
| * (We could be either client or server). |
| */ |
| int |
| get_srp_secret(unit, client, server, secret, am_server) |
| int unit; |
| char *client; |
| char *server; |
| char *secret; |
| int am_server; |
| { |
| FILE *fp; |
| int ret; |
| char *filename; |
| struct wordlist *addrs, *opts; |
| |
| if (!am_server && passwd[0] != '\0') { |
| strlcpy(secret, passwd, MAXWORDLEN); |
| } else { |
| filename = _PATH_SRPFILE; |
| addrs = NULL; |
| |
| fp = fopen(filename, "r"); |
| if (fp == NULL) { |
| error("Can't open srp secret file %s: %m", filename); |
| return 0; |
| } |
| check_access(fp, filename); |
| |
| secret[0] = '\0'; |
| ret = scan_authfile(fp, client, server, secret, &addrs, &opts, |
| filename, am_server); |
| fclose(fp); |
| if (ret < 0) |
| return 0; |
| |
| if (am_server) |
| set_allowed_addrs(unit, addrs, opts); |
| else if (opts != NULL) |
| free_wordlist(opts); |
| if (addrs != NULL) |
| free_wordlist(addrs); |
| } |
| |
| return 1; |
| } |
| |
| /* |
| * 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, opts) |
| int unit; |
| struct wordlist *addrs; |
| struct wordlist *opts; |
| { |
| int n; |
| struct wordlist *ap, **plink; |
| struct permitted_ip *ip; |
| char *ptr_word, *ptr_mask; |
| struct hostent *hp; |
| struct netent *np; |
| 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 = opts; |
| |
| /* |
| * Count the number of IP addresses given. |
| */ |
| n = wordlist_count(addrs) + wordlist_count(noauth_addrs); |
| if (n == 0) |
| return; |
| ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip)); |
| if (ip == 0) |
| return; |
| |
| /* temporarily append the noauth_addrs list to addrs */ |
| for (plink = &addrs; *plink != NULL; plink = &(*plink)->next) |
| ; |
| *plink = noauth_addrs; |
| |
| n = 0; |
| for (ap = addrs; ap != NULL; ap = ap->next) { |
| /* "-" means no addresses authorized, "*" means any address allowed */ |
| ptr_word = ap->word; |
| if (strcmp(ptr_word, "-") == 0) |
| break; |
| if (strcmp(ptr_word, "*") == 0) { |
| ip[n].permit = 1; |
| ip[n].base = ip[n].mask = 0; |
| ++n; |
| break; |
| } |
| |
| ip[n].permit = 1; |
| if (*ptr_word == '!') { |
| ip[n].permit = 0; |
| ++ptr_word; |
| } |
| |
| 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, &endp, 10); |
| if (bit_count <= 0 || bit_count > 32) { |
| warn("invalid address length %v in auth. address list", |
| 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 <<= bit_count; |
| } |
| |
| hp = gethostbyname(ptr_word); |
| if (hp != NULL && hp->h_addrtype == AF_INET) { |
| a = *(u_int32_t *)hp->h_addr; |
| } else { |
| np = getnetbyname (ptr_word); |
| if (np != NULL && np->n_addrtype == AF_INET) { |
| a = htonl ((u_int32_t)np->n_net); |
| if (ptr_mask == NULL) { |
| /* calculate appropriate mask for net */ |
| ah = ntohl(a); |
| if (IN_CLASSA(ah)) |
| mask = IN_CLASSA_NET; |
| else if (IN_CLASSB(ah)) |
| mask = IN_CLASSB_NET; |
| else if (IN_CLASSC(ah)) |
| mask = IN_CLASSC_NET; |
| } |
| } else { |
| a = inet_addr (ptr_word); |
| } |
| } |
| |
| if (ptr_mask != NULL) |
| *ptr_mask = '/'; |
| |
| if (a == (u_int32_t)-1L) { |
| warn("unknown host %s in auth. address list", ap->word); |
| 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; |
| } |
| *plink = NULL; |
| |
| ip[n].permit = 0; /* make the last entry forbid all addresses */ |
| ip[n].base = 0; /* to terminate the list */ |
| ip[n].mask = 0; |
| |
| addresses[unit] = ip; |
| |
| /* |
| * If the address given for the peer isn't authorized, or if |
| * the user hasn't given one, AND there is an authorized address |
| * which is a single host, then use that if we find one. |
| */ |
| if (suggested_ip != 0 |
| && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) { |
| wo->hisaddr = suggested_ip; |
| /* |
| * Do we insist on this address? No, if there are other |
| * addresses authorized than the suggested one. |
| */ |
| if (n > 1) |
| wo->accept_remote = 1; |
| } |
| } |
| |
| /* |
| * auth_ip_addr - check whether the peer is authorized to use |
| * a given IP address. Returns 1 if authorized, 0 otherwise. |
| */ |
| int |
| auth_ip_addr(unit, addr) |
| int unit; |
| u_int32_t addr; |
| { |
| int ok; |
| |
| /* don't allow loopback or multicast address */ |
| if (bad_ip_adrs(addr)) |
| return 0; |
| |
| if (allowed_address_hook) { |
| ok = allowed_address_hook(addr); |
| if (ok >= 0) return ok; |
| } |
| |
| if (addresses[unit] != NULL) { |
| ok = ip_addr_check(addr, addresses[unit]); |
| if (ok >= 0) |
| return ok; |
| } |
| |
| if (auth_required) |
| return 0; /* no addresses authorized */ |
| return allow_any_ip || privileged || !have_route_to(addr); |
| } |
| |
| static int |
| ip_addr_check(addr, addrs) |
| u_int32_t addr; |
| struct permitted_ip *addrs; |
| { |
| for (; ; ++addrs) |
| if ((addr & addrs->mask) == addrs->base) |
| return addrs->permit; |
| } |
| |
| /* |
| * bad_ip_adrs - return 1 if the IP address is one we don't want |
| * to use, such as an address in the loopback net or a multicast address. |
| * addr is in network byte order. |
| */ |
| int |
| bad_ip_adrs(addr) |
| u_int32_t addr; |
| { |
| addr = ntohl(addr); |
| return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET |
| || IN_MULTICAST(addr) || IN_BADCLASS(addr); |
| } |
| |
| /* |
| * some_ip_ok - check a wordlist to see if it authorizes any |
| * IP address(es). |
| */ |
| static int |
| some_ip_ok(addrs) |
| struct wordlist *addrs; |
| { |
| for (; addrs != 0; addrs = addrs->next) { |
| if (addrs->word[0] == '-') |
| break; |
| if (addrs->word[0] != '!') |
| return 1; /* some IP address is allowed */ |
| } |
| return 0; |
| } |
| |
| /* |
| * auth_number - check whether the remote number is allowed to connect. |
| * Returns 1 if authorized, 0 otherwise. |
| */ |
| int |
| auth_number() |
| { |
| struct wordlist *wp = permitted_numbers; |
| int l; |
| |
| /* Allow all if no authorization list. */ |
| if (!wp) |
| return 1; |
| |
| /* Allow if we have a match in the authorization list. */ |
| while (wp) { |
| /* trailing '*' wildcard */ |
| l = strlen(wp->word); |
| if ((wp->word)[l - 1] == '*') |
| l--; |
| if (!strncasecmp(wp->word, remote_number, l)) |
| return 1; |
| wp = wp->next; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * check_access - complain if a secret file has too-liberal permissions. |
| */ |
| static void |
| check_access(f, filename) |
| FILE *f; |
| char *filename; |
| { |
| struct stat sbuf; |
| |
| if (fstat(fileno(f), &sbuf) < 0) { |
| warn("cannot stat secret file %s: %m", filename); |
| } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { |
| warn("Warning - secret file %s has world and/or group access", |
| filename); |
| } |
| } |
| |
| |
| /* |
| * scan_authfile - Scan an authorization file for a secret suitable |
| * for authenticating `client' on `server'. The return value is -1 |
| * if no secret is found, otherwise >= 0. The return value has |
| * NONWILD_CLIENT set if the secret didn't have "*" for the client, and |
| * NONWILD_SERVER set if the secret didn't have "*" for the server. |
| * Any following words on the line up to a "--" (i.e. address authorization |
| * info) are placed in a wordlist and returned in *addrs. Any |
| * following words (extra options) are placed in a wordlist and |
| * returned in *opts. |
| * We assume secret is NULL or points to MAXWORDLEN bytes of space. |
| * Flags are non-zero if we need two colons in the secret in order to |
| * match. |
| */ |
| static int |
| scan_authfile(f, client, server, secret, addrs, opts, filename, flags) |
| FILE *f; |
| char *client; |
| char *server; |
| char *secret; |
| struct wordlist **addrs; |
| struct wordlist **opts; |
| char *filename; |
| int flags; |
| { |
| int newline, xxx; |
| int got_flag, best_flag; |
| FILE *sf; |
| struct wordlist *ap, *addr_list, *alist, **app; |
| char word[MAXWORDLEN]; |
| char atfile[MAXWORDLEN]; |
| char lsecret[MAXWORDLEN]; |
| char *cp; |
| |
| if (addrs != NULL) |
| *addrs = NULL; |
| if (opts != NULL) |
| *opts = NULL; |
| addr_list = NULL; |
| if (!getword(f, word, &newline, filename)) |
| return -1; /* file is empty??? */ |
| newline = 1; |
| best_flag = -1; |
| for (;;) { |
| /* |
| * Skip until we find a word at the start of a line. |
| */ |
| while (!newline && getword(f, word, &newline, filename)) |
| ; |
| if (!newline) |
| break; /* got to end of file */ |
| |
| /* |
| * Got a client - check if it's a match or a wildcard. |
| */ |
| got_flag = 0; |
| if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) { |
| newline = 0; |
| continue; |
| } |
| if (!ISWILD(word)) |
| got_flag = NONWILD_CLIENT; |
| |
| /* |
| * Now get a server and check if it matches. |
| */ |
| if (!getword(f, word, &newline, filename)) |
| break; |
| if (newline) |
| continue; |
| if (!ISWILD(word)) { |
| if (server != NULL && strcmp(word, server) != 0) |
| continue; |
| got_flag |= NONWILD_SERVER; |
| } |
| |
| /* |
| * Got some sort of a match - see if it's better than what |
| * we have already. |
| */ |
| if (got_flag <= best_flag) |
| continue; |
| |
| /* |
| * Get the secret. |
| */ |
| if (!getword(f, word, &newline, filename)) |
| break; |
| if (newline) |
| continue; |
| |
| /* |
| * SRP-SHA1 authenticator should never be reading secrets from |
| * a file. (Authenticatee may, though.) |
| */ |
| if (flags && ((cp = strchr(word, ':')) == NULL || |
| strchr(cp + 1, ':') == NULL)) |
| continue; |
| |
| if (secret != NULL) { |
| /* |
| * Special syntax: @/pathname means read secret from file. |
| */ |
| if (word[0] == '@' && word[1] == '/') { |
| strlcpy(atfile, word+1, sizeof(atfile)); |
| if ((sf = fopen(atfile, "r")) == NULL) { |
| warn("can't open indirect secret file %s", atfile); |
| continue; |
| } |
| check_access(sf, atfile); |
| if (!getword(sf, word, &xxx, atfile)) { |
| warn("no secret in indirect secret file %s", atfile); |
| fclose(sf); |
| continue; |
| } |
| fclose(sf); |
| } |
| strlcpy(lsecret, word, sizeof(lsecret)); |
| } |
| |
| /* |
| * Now read address authorization info and make a wordlist. |
| */ |
| app = &alist; |
| for (;;) { |
| if (!getword(f, word, &newline, filename) || newline) |
| break; |
| ap = (struct wordlist *) |
| malloc(sizeof(struct wordlist) + strlen(word) + 1); |
| if (ap == NULL) |
| novm("authorized addresses"); |
| ap->word = (char *) (ap + 1); |
| strcpy(ap->word, word); |
| *app = ap; |
| app = &ap->next; |
| } |
| *app = NULL; |
| |
| /* |
| * This is the best so far; remember it. |
| */ |
| best_flag = got_flag; |
| if (addr_list) |
| free_wordlist(addr_list); |
| addr_list = alist; |
| if (secret != NULL) |
| strlcpy(secret, lsecret, MAXWORDLEN); |
| |
| if (!newline) |
| break; |
| } |
| |
| /* scan for a -- word indicating the start of options */ |
| for (app = &addr_list; (ap = *app) != NULL; app = &ap->next) |
| if (strcmp(ap->word, "--") == 0) |
| break; |
| /* ap = start of options */ |
| if (ap != NULL) { |
| ap = ap->next; /* first option */ |
| free(*app); /* free the "--" word */ |
| *app = NULL; /* terminate addr list */ |
| } |
| if (opts != NULL) |
| *opts = ap; |
| else if (ap != NULL) |
| free_wordlist(ap); |
| if (addrs != NULL) |
| *addrs = addr_list; |
| else if (addr_list != NULL) |
| free_wordlist(addr_list); |
| |
| return best_flag; |
| } |
| |
| /* |
| * wordlist_count - return the number of items in a wordlist |
| */ |
| static int |
| wordlist_count(wp) |
| struct wordlist *wp; |
| { |
| int n; |
| |
| for (n = 0; wp != NULL; wp = wp->next) |
| ++n; |
| return n; |
| } |
| |
| /* |
| * free_wordlist - release memory allocated for a wordlist. |
| */ |
| static void |
| free_wordlist(wp) |
| struct wordlist *wp; |
| { |
| struct wordlist *next; |
| |
| while (wp != NULL) { |
| next = wp->next; |
| free(wp); |
| wp = next; |
| } |
| } |
| |
| /* |
| * auth_script_done - called when the auth-up or auth-down script |
| * has finished. |
| */ |
| static void |
| auth_script_done(arg) |
| void *arg; |
| { |
| auth_script_pid = 0; |
| switch (auth_script_state) { |
| case s_up: |
| if (auth_state == s_down) { |
| auth_script_state = s_down; |
| auth_script(_PATH_AUTHDOWN); |
| } |
| break; |
| case s_down: |
| if (auth_state == s_up) { |
| auth_script_state = s_up; |
| auth_script(_PATH_AUTHUP); |
| } |
| break; |
| } |
| } |
| |
| /* |
| * auth_script - execute a script with arguments |
| * interface-name peer-name real-user tty speed |
| */ |
| static void |
| auth_script(script) |
| char *script; |
| { |
| char strspeed[32]; |
| struct passwd *pw; |
| char struid[32]; |
| char *user_name; |
| char *argv[8]; |
| |
| if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL) |
| user_name = pw->pw_name; |
| else { |
| slprintf(struid, sizeof(struid), "%d", getuid()); |
| user_name = struid; |
| } |
| slprintf(strspeed, sizeof(strspeed), "%d", baud_rate); |
| |
| argv[0] = script; |
| argv[1] = ifname; |
| argv[2] = peer_authname; |
| argv[3] = user_name; |
| argv[4] = devnam; |
| argv[5] = strspeed; |
| argv[6] = NULL; |
| |
| auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL); |
| } |