]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/auth.c
add stuff for packet filtering
[ppp.git] / pppd / auth.c
index ac2eee93a654066c15a2c3553d102081fc79b426..f24b98be1ed59d820971cfa4ea3371cbf1fd2c00 100644 (file)
@@ -32,7 +32,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: auth.c,v 1.58 1999/09/11 12:08:56 paulus Exp $"
+#define RCSID  "$Id: auth.c,v 1.67 2000/08/01 01:38:29 paulus Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -112,6 +112,9 @@ 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;
 
@@ -123,6 +126,9 @@ 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;
 
@@ -139,6 +145,8 @@ 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.
  */
@@ -184,6 +192,7 @@ static int  setupapfile __P((char **));
 static int  privgroup __P((char **));
 static int  set_noauth_addr __P((char **));
 static void check_access __P((FILE *, char *));
+static int  wordlist_count __P((struct wordlist *));
 
 /*
  * Authentication-related options.
@@ -223,14 +232,14 @@ option_t auth_options[] = {
       "Use system password database for PAP", 1 },
     { "papcrypt", o_bool, &cryptpap,
       "PAP passwords are encrypted", 1 },
-    { "+ua", o_special, setupapfile,
+    { "+ua", o_special, (void *)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,
+    { "privgroup", o_special, (void *)privgroup,
       "Allow group members to use privileged options", OPT_PRIV },
-    { "allow-ip", o_special, set_noauth_addr,
+    { "allow-ip", o_special, (void *)set_noauth_addr,
       "Set IP address(es) which can be used without authentication",
       OPT_PRIV },
     { NULL }
@@ -312,10 +321,10 @@ set_noauth_addr(argv)
     char **argv;
 {
     char *addr = *argv;
-    int l = strlen(addr);
+    int l = strlen(addr) + 1;
     struct wordlist *wp;
 
-    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1);
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
     if (wp == NULL)
        novm("allow-ip argument");
     wp->word = (char *) (wp + 1);
@@ -346,8 +355,12 @@ link_terminated(unit)
 {
     if (phase == PHASE_DEAD)
        return;
-    if (logged_in)
-       plogout();
+    if (pap_logout_hook) {
+       pap_logout_hook();
+    } else {
+       if (logged_in)
+           plogout();
+    }
     new_phase(PHASE_DEAD);
     notice("Connection terminated.");
 }
@@ -414,8 +427,8 @@ link_established(unit)
         * boot it out.
         */
        if (noauth_addrs != NULL) {
-           set_allowed_addrs(unit, noauth_addrs, NULL);
-       } else if (!wo->neg_upap || !null_login(unit)) {
+           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;
@@ -424,6 +437,7 @@ link_established(unit)
     }
 
     new_phase(PHASE_AUTHENTICATE);
+    used_login = 0;
     auth = 0;
     if (go->neg_chap) {
        ChapAuthPeer(unit, our_name, go->chap_mdtype);
@@ -499,7 +513,18 @@ start_networks()
     struct protent *protp;
 
     new_phase(PHASE_NETWORK);
-#if 0
+
+#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
@@ -560,7 +585,7 @@ auth_peer_success(unit, protocol, name, namelen)
        namelen = sizeof(peer_authname) - 1;
     BCOPY(name, peer_authname, namelen);
     peer_authname[namelen] = 0;
-    script_setenv("PEERNAME", peer_authname);
+    script_setenv("PEERNAME", peer_authname, 0);
 
     /*
      * If there is no more authentication still to be done,
@@ -748,10 +773,12 @@ auth_check_options()
 
     /*
      * If we have a default route, require the peer to authenticate
-     * unless the noauth option was given.
+     * unless the noauth option was given or the real user is root.
      */
-    if (!auth_required && !allow_any_ip && have_route_to(0))
+    if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) {
        auth_required = 1;
+       default_auth = 1;
+    }
 
     /* If authentication is required, ask peer for CHAP or PAP. */
     if (auth_required) {
@@ -776,20 +803,23 @@ auth_check_options()
     }
 
     if (auth_required && !can_auth && noauth_addrs == NULL) {
-       if (explicit_remote)
+       if (default_auth) {
            option_error(
-"The remote system (%s) is required to authenticate itself but I",
-                        remote_name);
-       else
+"By default the remote system is required to authenticate itself");
            option_error(
-"The remote system is required to authenticate itself but I");
-
-       if (!lacks_ip)
+"(because this system has a default route to the internet)");
+       } else if (explicit_remote)
            option_error(
-"couldn't find any suitable secret (password) for it to use to do so.");
+"The remote system (%s) is required to authenticate itself",
+                        remote_name);
        else
            option_error(
-"couldn't find any secret (password) which would let it use an IP address.");
+"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);
     }
@@ -868,6 +898,8 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
            if (ret)
                set_allowed_addrs(unit, addrs, opts);
            BZERO(passwd, sizeof(passwd));
+           if (addrs != 0)
+               free_wordlist(addrs);
            return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
        }
     }
@@ -887,21 +919,29 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
        check_access(f, filename);
        if (scan_authfile(f, user, our_name, secret, &addrs, &opts, 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);
-           if (ret == UPAP_AUTHNAK)
-               warn("PAP login failure for %s", user);
        } else {
-           /* empty password in pap-secrets and login option not used */
+           /*
+            * 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 */
+               ret = plogin(user, passwd, msg);
+               if (ret == UPAP_AUTHNAK)
+                   warn("PAP login failure for %s", user);
+               else
+                   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;
+                   warn("PAP authentication failure for %s", user);
+               }
+           }
        }
        fclose(f);
     }
@@ -921,8 +961,6 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
        }
        if (attempts > 3)
            sleep((u_int) (attempts - 3) * 5);
-       if (addrs != NULL)
-           free_wordlist(addrs);
        if (opts != NULL)
            free_wordlist(opts);
 
@@ -933,6 +971,8 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
        set_allowed_addrs(unit, addrs, opts);
     }
 
+    if (addrs != NULL)
+       free_wordlist(addrs);
     BZERO(passwd, sizeof(passwd));
     BZERO(secret, sizeof(secret));
 
@@ -1193,10 +1233,10 @@ null_login(unit)
 
     if (ret)
        set_allowed_addrs(unit, addrs, opts);
-    else {
-       free_wordlist(addrs);
+    else if (opts != 0)
        free_wordlist(opts);
-    }
+    if (addrs != 0)
+       free_wordlist(addrs);
 
     fclose(f);
     return ret;
@@ -1216,7 +1256,6 @@ get_pap_passwd(passwd)
     char *filename;
     FILE *f;
     int ret;
-    struct wordlist *addrs;
     char secret[MAXWORDLEN];
 
     /*
@@ -1229,7 +1268,6 @@ get_pap_passwd(passwd)
     }
 
     filename = _PATH_UPAPFILE;
-    addrs = NULL;
     f = fopen(filename, "r");
     if (f == NULL)
        return 0;
@@ -1370,10 +1408,10 @@ get_secret(unit, client, server, secret, secret_len, am_server)
 
        if (am_server)
            set_allowed_addrs(unit, addrs, opts);
-       else {
-           free_wordlist(addrs);
+       else if (opts != 0)
            free_wordlist(opts);
-       }
+       if (addrs != 0)
+           free_wordlist(addrs);
     }
 
     len = strlen(secbuf);
@@ -1400,7 +1438,7 @@ set_allowed_addrs(unit, addrs, opts)
     struct wordlist *opts;
 {
     int n;
-    struct wordlist *ap, **pap;
+    struct wordlist *ap, **plink;
     struct permitted_ip *ip;
     char *ptr_word, *ptr_mask;
     struct hostent *hp;
@@ -1419,14 +1457,18 @@ set_allowed_addrs(unit, addrs, opts)
     /*
      * Count the number of IP addresses given.
      */
-    for (n = 0, pap = &addrs; (ap = *pap) != NULL; pap = &ap->next)
-       ++n;
+    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 */
@@ -1516,6 +1558,7 @@ set_allowed_addrs(unit, addrs, opts)
        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 */
@@ -1555,7 +1598,7 @@ auth_ip_addr(unit, addr)
     }
     if (auth_required)
        return 0;               /* no addresses authorized */
-    return allow_any_ip || !have_route_to(addr);
+    return allow_any_ip || privileged || !have_route_to(addr);
 }
 
 static int
@@ -1705,25 +1748,26 @@ scan_authfile(f, client, server, secret, addrs, opts, filename)
        if (newline)
            continue;
 
-       /*
-        * Special syntax: @filename means read secret from file.
-        */
-       if (word[0] == '@') {
-           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);
+       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);
-               continue;
            }
-           fclose(sf);
-       }
-       if (secret != NULL)
            strlcpy(lsecret, word, sizeof(lsecret));
+       }
 
        /*
         * Now read address authorization info and make a wordlist.
@@ -1779,6 +1823,20 @@ scan_authfile(f, client, server, secret, addrs, opts, filename)
     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.
  */