]> git.ozlabs.org Git - ppp.git/commitdiff
Added new "enable-session" option to enable session accounting and
authorJames Carlson <carlsonj@workingcode.com>
Tue, 19 Jun 2007 02:08:35 +0000 (02:08 +0000)
committerJames Carlson <carlsonj@workingcode.com>
Tue, 19 Jun 2007 02:08:35 +0000 (02:08 +0000)
logging without system-based authentication (works with all PPP
authentication types, including CHAP, unlike the "login" option), and
repaired misuse of wtmpx data in Solaris port.
Contributed by Diego Rivera <diego@rivera.net>.

pppd/Makefile.linux
pppd/Makefile.sol2
pppd/auth.c
pppd/chap-new.c
pppd/pppd.8
pppd/pppd.h
pppd/session.c [new file with mode: 0644]
pppd/session.h [new file with mode: 0644]
pppd/sys-solaris.c

index af8f6c615dc37c280adfbb48a9bb1d5dba30e52c..060db6a25113208ba6b4107d6a7590f974897fbd 100644 (file)
@@ -1,6 +1,6 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.69 2006/06/04 05:07:46 paulus Exp $
+# $Id: Makefile.linux,v 1.70 2007/06/19 02:08:34 carlsonj Exp $
 #
 
 # Default installation locations
@@ -13,16 +13,16 @@ TARGETS = pppd
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap-new.c md5.c ccp.c \
           ecp.c ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c \
-          demand.c utils.c tty.c eap.c chap-md5.c
+          demand.c utils.c tty.c eap.c chap-md5.c session.c
 
-HEADERS = ccp.h chap-new.h ecp.h fsm.h ipcp.h \
+HEADERS = ccp.h session.h chap-new.h ecp.h fsm.h ipcp.h \
        ipxcp.h lcp.h magic.h md5.h patchlevel.h pathnames.h pppd.h \
        upap.h eap.h
 
 MANPAGES = pppd.8
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o md5.o ccp.o \
           ecp.o auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o \
-          eap.o chap-md5.o
+          eap.o chap-md5.o session.o
 
 #
 # include dependencies if present
index 690b2f7b940f3ecae459a312d5f3d086cb4d1ea6..62b23702be5e125df030ab84bdb683ea2bdd54c8 100644 (file)
@@ -1,6 +1,6 @@
 #
 # Makefile for pppd under Solaris 2.
-# $Id: Makefile.sol2,v 1.27 2005/08/28 06:49:12 paulus Exp $
+# $Id: Makefile.sol2,v 1.28 2007/06/19 02:08:35 carlsonj Exp $
 #
 
 include ../Makedefs.com
@@ -10,7 +10,7 @@ LIBS  = -lsocket -lnsl
 
 OBJS   =  main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o eap.o md5.o \
        tty.o ccp.o ecp.o auth.o options.o demand.o utils.o sys-solaris.o \
-       chap-md5.o
+       chap-md5.o session.o
 
 #
 # uncomment the following to enable plugins
@@ -40,6 +40,10 @@ LIBS += -ldl
 #CFLAGS += -DCBCP_SUPPORT
 #OBJS += cbcp.o
 
+# Uncomment for PAM
+#CFLAGS += -DUSE_PAM
+#LIBS += -lpam
+
 #
 # Make targets
 #
index cd0477780cf2111c604eaebd102fa9b0b81fa78a..8189787b198f26c13b90d8b5dd2c3d207edc5777 100644 (file)
@@ -68,7 +68,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: auth.c,v 1.112 2006/06/18 11:26:00 paulus Exp $"
+#define RCSID  "$Id: auth.c,v 1.113 2007/06/19 02:08:35 carlsonj Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -91,9 +91,6 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#ifdef USE_PAM
-#include <security/pam_appl.h>
-#endif
 
 #ifdef HAS_SHADOW
 #include <shadow.h>
 #include "cbcp.h"
 #endif
 #include "pathnames.h"
+#include "session.h"
 
 static const char rcsid[] = RCSID;
 
@@ -134,9 +132,6 @@ 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];
 
@@ -215,12 +210,11 @@ 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 session_mgmt = 0;         /* Do session management (login records) */
 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 */
@@ -247,8 +241,6 @@ extern char *crypt __P((const char *, const char *));
 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 *));
@@ -377,7 +369,10 @@ option_t auth_options[] = {
       &explicit_remote, MAXNAMELEN },
 
     { "login", o_bool, &uselogin,
-      "Use system password database for PAP", 1 },
+      "Use system password database for PAP", OPT_A2COPY | 1 ,
+      &session_mgmt },
+    { "enable-session", o_bool, &session_mgmt,
+      "Enable session accounting for remote peers", OPT_PRIV | 1 },
 
     { "papcrypt", o_bool, &cryptpap,
       "PAP passwords are encrypted", 1 },
@@ -613,10 +608,8 @@ link_terminated(unit)
 
     if (pap_logout_hook) {
        pap_logout_hook();
-    } else {
-       if (logged_in)
-           plogout();
     }
+    session_end(devnam);
 
     if (!doing_multilink) {
        notice("Connection terminated.");
@@ -759,7 +752,6 @@ link_established(unit)
     }
 
     new_phase(PHASE_AUTHENTICATE);
-    used_login = 0;
     auth = 0;
     if (go->neg_eap) {
        eap_authpeer(unit, our_name);
@@ -1421,8 +1413,14 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
            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 (session_full(user, passwd, devnam, msg) == 0) {
+                   ret = UPAP_AUTHNAK;
+               }
+           } else if (session_mgmt) {
+               if (session_check(user, NULL, devnam, NULL) == 0) {
+                   warn("Peer %q failed PAP Session verification", user);
+                   ret = UPAP_AUTHNAK;
+               }
            }
            if (secret[0] != 0 && !login_secret) {
                /* password given in pap-secrets - must match */
@@ -1467,232 +1465,6 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
     return ret;
 }
 
-/*
- * 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;
-
-#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);
-}
-
-/*
- * plogout - Logout the user.
- */
-static void
-plogout()
-{
-    char *tty;
-#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();
-#endif /* USE_PAM */
-
-    tty = devnam;
-    if (strncmp(tty, "/dev/", 5) == 0)
-       tty += 5;
-    logwtmp(tty, "", "");              /* Wipe out utmp logout entry */
-    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
index cf54f57dd8568b8acc07e1bbae1401a29195b927..138648602c29f47a70d4167317ea2ac9e04c1401 100644 (file)
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: chap-new.c,v 1.8 2005/07/13 10:41:58 paulus Exp $"
+#define RCSID  "$Id: chap-new.c,v 1.9 2007/06/19 02:08:35 carlsonj Exp $"
 
 #include <stdlib.h>
 #include <string.h>
 #include "pppd.h"
+#include "session.h"
 #include "chap-new.h"
 #include "chap-md5.h"
 
@@ -366,6 +367,22 @@ chap_handle_response(struct chap_server_state *ss, int id,
 
        if (ss->flags & CHALLENGE_VALID) {
                ss->flags &= ~CHALLENGE_VALID;
+               if (!(ss->flags & AUTH_DONE) && !(ss->flags & AUTH_FAILED)) {
+                   /*
+                    * Auth is OK, so now we need to check session restrictions
+                    * to ensure everything is OK, but only if we used a
+                    * plugin, and only if we're configured to check.  This
+                    * allows us to do PAM checks on PPP servers that
+                    * authenticate against ActiveDirectory, and use AD for
+                    * account info (like when using Winbind integrated with
+                    * PAM).
+                    */
+                   if (session_mgmt &&
+                       session_check(name, NULL, devnam, NULL) == 0) {
+                       ss->flags |= AUTH_FAILED;
+                       warn("Peer %q failed CHAP Session verification", name);
+                   }
+               }
                if (ss->flags & AUTH_FAILED) {
                        auth_peer_fail(0, PPP_CHAP);
                } else {
index 5f824694f5a1f0ee84946a1900490a27514afae2..4a375640bb4ecbd477df1597633e74f205c8a8dd 100644 (file)
@@ -1,5 +1,5 @@
 .\" manual page [] for pppd 2.4
-.\" $Id: pppd.8,v 1.88 2006/06/16 00:01:23 paulus Exp $
+.\" $Id: pppd.8,v 1.89 2007/06/19 02:08:35 carlsonj Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
@@ -352,6 +352,16 @@ With the \fBdump\fR option, pppd will print out all the option values
 which have been set.  This option is like the \fBdryrun\fR option
 except that pppd proceeds as normal rather than exiting.
 .TP
+.B enable-session
+Enables session accounting via PAM or wtwp/wtmpx, as appropriate.
+When PAM is enabled, the PAM "account" and "session" module stacks
+determine behavior, and are enabled for all PPP authentication
+protocols.  When PAM is disabled, wtmp/wtmpx entries are recorded
+regardless of whether the peer name identifies a valid user on the
+local system, making peers visible in the last(1) log.  This feature
+is automatically enabled when the pppd \fBlogin\fR option is used.
+Session accounting is disabled by default.
+.TP
 .B endpoint \fI<epdisc>
 Sets the endpoint discriminator sent by the local machine to the peer
 during multilink negotiation to \fI<epdisc>\fR.  The default is to use
@@ -602,7 +612,8 @@ the user who invoked pppd, in append mode.
 Use the system password database for authenticating the peer using
 PAP, and record the user in the system wtmp file.  Note that the peer
 must have an entry in the /etc/ppp/pap\-secrets file as well as the
-system password database to be allowed access.
+system password database to be allowed access.  See also the
+\fBenable\-session\fR option.
 .TP
 .B maxconnect \fIn
 Terminate the connection when it has been available for network
index f43a0390ab13479eb5cded5a93a7140f52b13fec..cfb3a682b3800f2a74cac07b1373078125a9dc55 100644 (file)
@@ -39,7 +39,7 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $Id: pppd.h,v 1.91 2005/08/25 23:59:34 paulus Exp $
+ * $Id: pppd.h,v 1.92 2007/06/19 02:08:35 carlsonj Exp $
  */
 
 /*
@@ -293,6 +293,7 @@ extern char passwd[MAXSECRETLEN];   /* Password for PAP or CHAP */
 extern bool    auth_required;  /* Peer is required to authenticate */
 extern bool    persist;        /* Reopen link after it goes down */
 extern bool    uselogin;       /* Use /etc/passwd for checking PAP */
+extern bool    session_mgmt;   /* Do session management (login records) */
 extern char    our_name[MAXNAMELEN];/* Our name for authentication purposes */
 extern char    remote_name[MAXNAMELEN]; /* Peer's name for authentication */
 extern bool    explicit_remote;/* remote_name specified with remotename opt */
diff --git a/pppd/session.c b/pppd/session.c
new file mode 100644 (file)
index 0000000..05dcb76
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * session.c - PPP session control.
+ *
+ * Copyright (c) 2007 Diego Rivera. 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 auth.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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <crypt.h>
+#include "pppd.h"
+#include "session.h"
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif /* #ifdef USE_PAM */
+
+#define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
+#define COPY_STRING(s) ((s) ? strdup(s) : NULL)
+
+#define SUCCESS_MSG "Session started successfully"
+#define ABORT_MSG "Session can't be started without a username"
+#define SERVICE_NAME "ppp"
+
+#define SESSION_FAILED  0
+#define SESSION_OK      1
+
+/* We have successfully started a session */
+static bool logged_in = 0;
+
+#ifdef USE_PAM
+/*
+ * Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static const char *PAM_username;
+static const char *PAM_password;
+static int   PAM_session = 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 conversation (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;
+
+    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);
+                return PAM_CONV_ERR;
+        }
+    }
+    *resp = reply;
+    return PAM_SUCCESS;
+}
+
+static struct pam_conv pam_conv_data = {
+    &conversation,
+    NULL
+};
+#endif /* #ifdef USE_PAM */
+
+int
+session_start(flags, user, passwd, ttyName, msg)
+    const int flags;
+    const char *user;
+    const char *passwd;
+    const char *ttyName;
+    char **msg;
+{
+    bool ok = 1;
+#ifdef USE_PAM
+    const char *usr;
+    int pam_error;
+    bool try_session = 0;
+#else /* #ifdef USE_PAM */
+    struct passwd *pw;
+#ifdef HAS_SHADOW
+    struct spwd *spwd;
+    struct spwd *getspnam();
+    long now = 0;
+#endif /* #ifdef HAS_SHADOW */
+#endif /* #ifdef USE_PAM */
+
+    SET_MSG(msg, SUCCESS_MSG);
+
+    /* If no verification is requested, then simply return an OK */
+    if (!(SESS_ALL & flags)) {
+        return SESSION_OK;
+    }
+
+    if (user == NULL) {
+       SET_MSG(msg, ABORT_MSG);
+       return SESSION_FAILED;
+    }
+
+#ifdef USE_PAM
+    /* Find the '\\' in the username */
+    /* This needs to be fixed to support different username schemes */
+    if ((usr = strchr(user, '\\')) == NULL)
+       usr = user;
+    else
+       usr++;
+
+    PAM_session = 0;
+    PAM_username = usr;
+    PAM_password = passwd;
+
+    dbglog("Initializing PAM (%d) for user %s", flags, usr);
+    pam_error = pam_start (SERVICE_NAME, usr, &pam_conv_data, &pamh);
+    dbglog("---> PAM INIT Result = %d", pam_error);
+    ok = (pam_error == PAM_SUCCESS);
+
+    if (ok) {
+        ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) &&
+           (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS);
+    }
+
+    if (ok && (SESS_AUTH & flags)) {
+        dbglog("Attempting PAM authentication");
+        pam_error = pam_authenticate (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            /* PAM auth was OK */
+            dbglog("PAM Authentication OK for %s", user);
+        } else {
+            /* No matter the reason, we fail because we're authenticating */
+            ok = 0;
+            if (pam_error == PAM_USER_UNKNOWN) {
+                dbglog("User unknown, failing PAM authentication");
+                SET_MSG(msg, "User unknown - cannot authenticate via PAM");
+            } else {
+                /* Any other error means authentication was bad */
+                dbglog("PAM Authentication failed: %d: %s", pam_error,
+                      pam_strerror(pamh, pam_error));
+                SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            }
+        }
+    }
+
+    if (ok && (SESS_ACCT & flags)) {
+        dbglog("Attempting PAM account checks");
+        pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            /*
+            * PAM account was OK, set the flag which indicates that we should
+            * try to perform the session checks.
+            */
+            try_session = 1;
+            dbglog("PAM Account OK for %s", user);
+        } else {
+            /*
+            * If the account checks fail, then we should not try to perform
+            * the session check, because they don't make sense.
+            */
+            try_session = 0;
+            if (pam_error == PAM_USER_UNKNOWN) {
+                /*
+                * We're checking the account, so it's ok to not have one
+                * because the user might come from the secrets files, or some
+                * other plugin.
+                */
+                dbglog("User unknown, ignoring PAM restrictions");
+                SET_MSG(msg, "User unknown - ignoring PAM restrictions");
+            } else {
+                /* Any other error means session is rejected */
+                ok = 0;
+                dbglog("PAM Account checks failed: %d: %s", pam_error,
+                      pam_strerror(pamh, pam_error));
+                SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            }
+        }
+    }
+
+    if (ok && try_session && (SESS_ACCT & flags)) {
+        /* Only open a session if the user's account was found */
+        pam_error = pam_open_session (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            dbglog("PAM Session opened for user %s", user);
+            PAM_session = 1;
+        } else {
+            dbglog("PAM Session denied for user %s", user);
+            SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            ok = 0;
+        }
+    }
+
+    /* This is needed because apparently the PAM stuff closes the log */
+    reopen_log();
+
+    /* If our PAM checks have already failed, then we must return a failure */
+    if (!ok) return SESSION_FAILED;
+
+#else /* #ifdef USE_PAM */
+
+/*
+ * Use the non-PAM methods directly
+ */
+
+    if ((SESS_AUTH & flags)) {
+       pw = getpwnam(user);
+
+       endpwent();
+       /*
+        * Here, we bail if we have no user account, because there is nothing
+        * to verify against.
+        */
+       if (pw == NULL)
+           return SESSION_FAILED;
+
+#ifdef HAS_SHADOW
+
+       spwd = getspnam(user);
+       endspent();
+
+       /*
+        * If there is no shadow entry for the user, then we can't verify the
+        * account.
+        */
+       if (spwd == NULL)
+           return SESSION_FAILED;
+
+       /*
+        * We check validity all the time, because if the password has expired,
+        * then clearly we should not authenticate against it (if we're being
+        * called for authentication only).  Thus, in this particular instance,
+        * there is no real difference between using the AUTH, SESS or ACCT
+        * flags, or combinations thereof.
+        */
+       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 SESSION_FAILED;
+       }
+
+       /* We have a valid shadow entry, keep the password */
+       pw->pw_passwd = spwd->sp_pwdp;
+
+#endif /* #ifdef HAS_SHADOW */
+
+       /*
+        * If no passwd, don't let them login if we're authenticating.
+        */
+        if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2
+            || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0)
+            return SESSION_FAILED;
+    }
+
+#endif /* #ifdef USE_PAM */
+
+    /*
+     * Write a wtmp entry for this user.
+     */
+
+    if (SESS_ACCT & flags) {
+       if (strncmp(ttyName, "/dev/", 5) == 0)
+           ttyName += 5;
+       logwtmp(ttyName, user, ifname); /* Add wtmp login entry */
+       logged_in = 1;
+
+#if defined(_PATH_LASTLOG) && !defined(USE_PAM)
+       {
+            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, ttyName, sizeof(ll.ll_line));
+                (void)strncpy(ll.ll_host, ifname, sizeof(ll.ll_host));
+                (void)write(fd, (char *)&ll, sizeof(ll));
+                (void)close(fd);
+            }
+       }
+#endif /* _PATH_LASTLOG and not USE_PAM */
+       info("user %s logged in on tty %s intf %s", user, ttyName, ifname);
+    }
+
+    return SESSION_OK;
+}
+
+/*
+ * session_end - Logout the user.
+ */
+void
+session_end(const char* ttyName)
+{
+#ifdef USE_PAM
+    int pam_error = PAM_SUCCESS;
+
+    if (pamh != NULL) {
+        if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT);
+        PAM_session = 0;
+        pam_end (pamh, pam_error);
+        pamh = NULL;
+       /* Apparently the pam stuff does closelog(). */
+       reopen_log();
+    }
+#endif
+    if (logged_in) {
+       if (strncmp(ttyName, "/dev/", 5) == 0)
+           ttyName += 5;
+       logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */
+       logged_in = 0;
+    }
+}
diff --git a/pppd/session.h b/pppd/session.h
new file mode 100644 (file)
index 0000000..bee8c41
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * session.c - PPP session control.
+ *
+ * Copyright (c) 2007 Diego Rivera. 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.
+ */
+
+#ifndef __SESSION_H
+#define __SESSION_H
+
+#define SESS_AUTH  1   /* Check User Authentication */
+#define SESS_ACCT  2   /* Check Account Validity */
+
+/* Convenience parameter to do the whole enchilada */
+#define SESS_ALL   (SESS_AUTH | SESS_ACCT)
+
+/*
+ * int session_start(...)
+ *
+ * Start a session, performing any necessary validations.
+ *
+ * Parameters:
+ *     const int flags :
+ *             Any combination of the SESS_XXX flags, to indicate what the function
+ *             should do as part of its checks
+ *
+ *     const char* user :
+ *             The username to validate.  May safely be null.
+ *
+ *     const char* passwd :
+ *             The password to validate the user with. May safely be null.
+ *
+ *     const char* tty :
+ *             The TTY the user is connected on. May safely be null.
+ *
+ *     char** msg :
+ *             A char* to return an error or success message.  This message will be returned
+ *             regardless of the result.  May safely be null.
+ *
+ * Return Value:
+ *     Zero value for failure, non-zero value for successful session verification.
+ */
+int
+session_start(const int flags, const char* user, const char* passwd, const char* tty, char** msg);
+
+/* Added these macros for convenience... */
+#define session_auth(user, pass, tty, msg) \
+       session_start(SESS_AUTH, user, pass, tty, msg)
+
+#define session_check(user, pass, tty, msg) \
+       session_start(SESS_ACCT, user, pass, tty, msg)
+
+#define session_full(user, pass, tty, msg) \
+       session_start(SESS_ALL, user, pass, tty, msg)
+
+/*
+ * void session_end(...)
+ *
+ * End a previously-started session.
+ *
+ * Parameters:
+ *     const char* tty :
+ *             The TTY the user is connected on. May safely be null.
+ */
+void
+session_end(const char* tty);
+
+#endif
index add4423bac3cd281b872e07e47bc87d75c8cfd89..06e8f733a9c52447ef2de5ee8b2493de2450ed28 100644 (file)
@@ -85,7 +85,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: sys-solaris.c,v 1.14 2005/05/04 21:31:20 carlsonj Exp $"
+#define RCSID  "$Id: sys-solaris.c,v 1.15 2007/06/19 02:08:35 carlsonj Exp $"
 
 #include <limits.h>
 #include <stdio.h>
@@ -2465,8 +2465,13 @@ logwtmp(line, name, host)
     if (name[0] != 0) {
        /* logging in */
        strncpy(utmpx.ut_user, name, sizeof(utmpx.ut_user));
-       strncpy(utmpx.ut_id, ifname, sizeof(utmpx.ut_id));
        strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
+       strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host));
+       if (*host != '\0') {
+           utmpx.ut_syslen = strlen(host) + 1;
+           if (utmpx.ut_syslen > sizeof(utmpx.ut_host))
+               utmpx.ut_syslen = sizeof(utmpx.ut_host);
+       }
        utmpx.ut_pid = getpid();
        utmpx.ut_type = USER_PROCESS;
     } else {