]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/auth.c
Large patch from Frank Cusack <fcusack@fcusack.com> to add proper
[ppp.git] / pppd / auth.c
index 9a6bfb1a8a4a1b38c7778eeb6ed7869aac908361..4f9c29788940905657e6eaf50dc3d6ffbdf6cf5e 100644 (file)
@@ -32,7 +32,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: auth.c,v 1.68 2001/03/08 05:11:10 paulus Exp $"
+#define RCSID  "$Id: auth.c,v 1.74 2002/03/01 14:39:18 dfs Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -64,6 +64,7 @@
 #define PW_PPP PW_LOGIN
 #endif
 #endif
+#include <time.h>
 
 #include "pppd.h"
 #include "fsm.h"
@@ -132,6 +133,20 @@ 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 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.
@@ -154,6 +169,11 @@ 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 */
+#ifdef CHAPMS
+bool refuse_mschap = 0;                /* Don't wanna auth. ourselves with MS-CHAP */
+#else
+bool refuse_mschap = 1;                /* Don't wanna auth. ourselves with MS-CHAP */
+#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 */
@@ -203,30 +223,53 @@ 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,
+      "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV | OPT_A2COPY,
       &allow_any_ip },
     { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
       "Require PAP authentication from peer",
-      OPT_PRIOSUB | 1, &auth_required },
+      OPT_PRIOSUB | OPT_A2COPY | 1, &auth_required },
     { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
       "Require PAP authentication from peer",
-      OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | 1, &auth_required },
     { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
       "Require CHAP authentication from peer",
-      OPT_PRIOSUB | 1, &auth_required },
+      OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MD5,
+      &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype },
     { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
       "Require CHAP authentication from peer",
-      OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MD5,
+      &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype },
+#ifdef CHAPMS
+    { "require-mschap", o_bool, &lcp_wantoptions[0].neg_chap,
+      "Require MS-CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT,
+      &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap", o_bool, &lcp_wantoptions[0].neg_chap,
+      "Require MS-CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT,
+      &auth_required, 0, 0, NULL, 0, 0, &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", 1 },
+      "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 | 1 },
+      "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 },
+#endif
 
     { "name", o_string, our_name,
       "Set local name for authentication",
@@ -407,6 +450,7 @@ link_down(unit)
     int i;
     struct protent *protp;
 
+    notify(link_down_notifier, 0);
     auth_state = s_down;
     if (auth_script_state == s_up && auth_script_pid == 0) {
        update_link_stats(unit);
@@ -424,7 +468,7 @@ link_down(unit)
     num_np_open = 0;
     num_np_up = 0;
     if (phase != PHASE_DEAD)
-       new_phase(PHASE_TERMINATE);
+       new_phase(PHASE_ESTABLISH);
 }
 
 /*
@@ -450,12 +494,12 @@ link_established(unit)
            && protp->lowerup != NULL)
            (*protp->lowerup)(unit);
 
-    if (auth_required && !(go->neg_chap || go->neg_upap)) {
+    if (auth_required && !(go->neg_upap || go->neg_chap)) {
        /*
         * 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,
+        * a username of "" and a password of "".  If that's not OK,
         * boot it out.
         */
        if (noauth_addrs != NULL) {
@@ -472,14 +516,14 @@ link_established(unit)
     used_login = 0;
     auth = 0;
     if (go->neg_chap) {
-       ChapAuthPeer(unit, our_name, go->chap_mdtype);
+       ChapAuthPeer(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_chap) {
-       ChapAuthWithPeer(unit, user, ho->chap_mdtype);
+       ChapAuthWithPeer(unit, user, CHAP_DIGEST(ho->chap_mdtype));
        auth |= CHAP_WITHPEER;
     } else if (ho->neg_upap) {
        if (passwd[0] == 0) {
@@ -509,6 +553,7 @@ network_phase(unit)
      * If the peer had to authenticate, run the auth-up script now.
      */
     if (go->neg_chap || go->neg_upap) {
+       notify(auth_up_notifier, 0);
        auth_state = s_up;
        if (auth_script_state == s_down && auth_script_pid == 0) {
            auth_script_state = s_up;
@@ -727,6 +772,7 @@ np_down(unit, proto)
 {
     if (--num_np_up == 0) {
        UNTIMEOUT(check_idle, NULL);
+       UNTIMEOUT(connect_time_expired, NULL);
        new_phase(PHASE_NETWORK);
     }
 }
@@ -816,11 +862,11 @@ auth_check_options()
     if (auth_required) {
        allow_any_ip = 0;
        if (!wo->neg_chap && !wo->neg_upap) {
-           wo->neg_chap = 1;
+           wo->neg_chap = 1; wo->chap_mdtype = MDTYPE_ALL;
            wo->neg_upap = 1;
        }
     } else {
-       wo->neg_chap = 0;
+       wo->neg_chap = 0; wo->chap_mdtype = MDTYPE_NONE;
        wo->neg_upap = 0;
     }
 
@@ -830,7 +876,7 @@ auth_check_options()
      */
     lacks_ip = 0;
     can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
-    if (!can_auth && wo->neg_chap) {
+    if (!can_auth && (wo->neg_chap)) {
        can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
                                    our_name, 1, &lacks_ip);
     }
@@ -871,7 +917,7 @@ auth_reset(unit)
     lcp_options *ao = &lcp_allowoptions[0];
 
     ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
-    ao->neg_chap = !refuse_chap
+    ao->neg_chap = (!refuse_chap || !refuse_mschap)
        && (passwd[0] != 0
            || have_chap_secret(user, (explicit_remote? remote_name: NULL),
                                0, NULL));
@@ -933,6 +979,9 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
            BZERO(passwd, sizeof(passwd));
            if (addrs != 0)
                free_wordlist(addrs);
+           if (opts != 0) {
+               free_wordlist(opts);
+           }
            return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
        }
     }
@@ -1120,7 +1169,7 @@ plogin(user, passwd, msg)
     if (pam_error == PAM_SUCCESS && !PAM_error) {    
         pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
         if (pam_error == PAM_SUCCESS)
-           pam_open_session (pamh, PAM_SILENT);
+           pam_error = pam_open_session (pamh, PAM_SILENT);
     }
 
     *msg = (char *) pam_strerror (pamh, pam_error);
@@ -1250,19 +1299,29 @@ null_login(unit)
     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.
      */
-    filename = _PATH_UPAPFILE;
-    addrs = NULL;
-    f = fopen(filename, "r");
-    if (f == NULL)
-       return 0;
-    check_access(f, filename);
+    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);
-    ret = i >= 0 && secret[0] == 0;
-    BZERO(secret, sizeof(secret));
+       i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename);
+       ret = i >= 0 && secret[0] == 0;
+       BZERO(secret, sizeof(secret));
+       fclose(f);
+    }
 
     if (ret)
        set_allowed_addrs(unit, addrs, opts);
@@ -1271,7 +1330,6 @@ null_login(unit)
     if (addrs != 0)
        free_wordlist(addrs);
 
-    fclose(f);
     return ret;
 }
 
@@ -1376,6 +1434,13 @@ have_chap_secret(client, server, need_ip, lacks_ipp)
     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)
@@ -1422,6 +1487,12 @@ get_secret(unit, client, server, secret, secret_len, am_server)
 
     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;
@@ -1605,8 +1676,15 @@ set_allowed_addrs(unit, addrs, opts)
      * 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 == 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;
+    }
 }
 
 /*
@@ -1624,11 +1702,17 @@ auth_ip_addr(unit, addr)
     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);