From e5b9a1061f126b36c558aea09fd76fd691ebf018 Mon Sep 17 00:00:00 2001 From: James Carlson Date: Tue, 19 Jun 2007 02:08:35 +0000 Subject: [PATCH] Added new "enable-session" option to enable session accounting and 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 . --- pppd/Makefile.linux | 8 +- pppd/Makefile.sol2 | 8 +- pppd/auth.c | 260 ++-------------------------- pppd/chap-new.c | 19 ++- pppd/pppd.8 | 15 +- pppd/pppd.h | 3 +- pppd/session.c | 405 ++++++++++++++++++++++++++++++++++++++++++++ pppd/session.h | 91 ++++++++++ pppd/sys-solaris.c | 9 +- 9 files changed, 562 insertions(+), 256 deletions(-) create mode 100644 pppd/session.c create mode 100644 pppd/session.h diff --git a/pppd/Makefile.linux b/pppd/Makefile.linux index af8f6c6..060db6a 100644 --- a/pppd/Makefile.linux +++ b/pppd/Makefile.linux @@ -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 diff --git a/pppd/Makefile.sol2 b/pppd/Makefile.sol2 index 690b2f7..62b2370 100644 --- a/pppd/Makefile.sol2 +++ b/pppd/Makefile.sol2 @@ -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 # diff --git a/pppd/auth.c b/pppd/auth.c index cd04777..8189787 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -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 #include @@ -91,9 +91,6 @@ #include #include -#ifdef USE_PAM -#include -#endif #ifdef HAS_SHADOW #include @@ -116,6 +113,7 @@ #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 diff --git a/pppd/chap-new.c b/pppd/chap-new.c index cf54f57..1386486 100644 --- a/pppd/chap-new.c +++ b/pppd/chap-new.c @@ -28,11 +28,12 @@ * 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 #include #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 { diff --git a/pppd/pppd.8 b/pppd/pppd.8 index 5f82469..4a37564 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -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 Sets the endpoint discriminator sent by the local machine to the peer during multilink negotiation to \fI\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 diff --git a/pppd/pppd.h b/pppd/pppd.h index f43a039..cfb3a68 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -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 index 0000000..05dcb76 --- /dev/null +++ b/pppd/session.c @@ -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 + * ". + * + * 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 +#include +#include +#include +#include +#include "pppd.h" +#include "session.h" + +#ifdef USE_PAM +#include +#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 index 0000000..bee8c41 --- /dev/null +++ b/pppd/session.h @@ -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 + * ". + * + * 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 diff --git a/pppd/sys-solaris.c b/pppd/sys-solaris.c index add4423..06e8f73 100644 --- a/pppd/sys-solaris.c +++ b/pppd/sys-solaris.c @@ -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 #include @@ -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 { -- 2.39.2