123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- /*
- * 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>
- #ifdef HAS_SHADOW
- #include <shadow.h>
- #endif
- #include <time.h>
- #include <utmp.h>
- #include <fcntl.h>
- #include <unistd.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;
- {
- #ifdef USE_PAM
- bool ok = 1;
- const char *usr;
- int pam_error;
- bool try_session = 0;
- #else /* #ifdef USE_PAM */
- struct passwd *pw;
- char *cbuf;
- #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. 'pw' will remain NULL if the user
- * has not been authenticated using local UNIX system services.
- */
- pw = NULL;
- 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)
- return SESSION_FAILED;
- cbuf = crypt(passwd, pw->pw_passwd);
- if (!cbuf || strcmp(cbuf, 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)
- /*
- * Enter the user in lastlog only if he has been authenticated using
- * local system services. If he has not, then we don't know what his
- * UID might be, and lastlog is indexed by UID.
- */
- if (pw != NULL) {
- struct lastlog ll;
- int fd;
- time_t tnow;
- 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(&tnow);
- ll.ll_time = tnow;
- (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;
- }
- }
|