2 * session.c - PPP session control.
4 * Copyright (c) 2007 Diego Rivera. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. The name(s) of the authors of this software must not be used to
14 * endorse or promote products derived from this software without
15 * prior written permission.
17 * 3. Redistributions of any form whatsoever must retain the following
19 * "This product includes software developed by Paul Mackerras
20 * <paulus@samba.org>".
22 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
23 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
24 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
26 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
28 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 * Derived from auth.c, which is:
32 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in
43 * the documentation and/or other materials provided with the
46 * 3. The name "Carnegie Mellon University" must not be used to
47 * endorse or promote products derived from this software without
48 * prior written permission. For permission or any legal
49 * details, please contact
50 * Office of Technology Transfer
51 * Carnegie Mellon University
53 * Pittsburgh, PA 15213-3890
54 * (412) 268-4387, fax: (412) 268-7395
55 * tech-transfer@andrew.cmu.edu
57 * 4. Redistributions of any form whatsoever must retain the following
59 * "This product includes software developed by Computing Services
60 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
62 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
63 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
64 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
65 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
66 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
67 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
68 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
96 #include <security/pam_appl.h>
97 #endif /* #ifdef PPP_WITH_PAM */
99 #define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
100 #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
102 #define SUCCESS_MSG "Session started successfully"
103 #define ABORT_MSG "Session can't be started without a username"
104 #define SERVICE_NAME "ppp"
106 #define SESSION_FAILED 0
109 /* We have successfully started a session */
110 static bool logged_in = 0;
114 * Static variables used to communicate between the conversation function
115 * and the server_login function
117 static const char *PAM_username;
118 static const char *PAM_password;
119 static int PAM_session = 0;
120 static pam_handle_t *pamh = NULL;
122 /* PAM conversation function
123 * Here we assume (for now, at least) that echo on means login name, and
124 * echo off means password.
127 static int conversation (int num_msg,
128 const struct pam_message **msg,
129 struct pam_response **resp, void *appdata_ptr)
132 struct pam_response *reply = NULL;
134 reply = malloc(sizeof(struct pam_response) * num_msg);
135 if (!reply) return PAM_CONV_ERR;
137 for (replies = 0; replies < num_msg; replies++) {
138 switch (msg[replies]->msg_style) {
139 case PAM_PROMPT_ECHO_ON:
140 reply[replies].resp_retcode = PAM_SUCCESS;
141 reply[replies].resp = COPY_STRING(PAM_username);
144 case PAM_PROMPT_ECHO_OFF:
145 reply[replies].resp_retcode = PAM_SUCCESS;
146 reply[replies].resp = COPY_STRING(PAM_password);
152 /* ignore it, but pam still wants a NULL response... */
153 reply[replies].resp_retcode = PAM_SUCCESS;
154 reply[replies].resp = NULL;
157 /* Must be an error of some sort... */
166 static struct pam_conv pam_conv_data = {
170 #endif /* #ifdef PPP_WITH_PAM */
173 session_start(const int flags, const char *user, const char *passwd, const char *ttyName, char **msg)
179 bool try_session = 0;
180 #else /* #ifdef PPP_WITH_PAM */
185 struct spwd *getspnam();
187 #endif /* #ifdef HAVE_SHADOW_H */
188 #endif /* #ifdef PPP_WITH_PAM */
190 SET_MSG(msg, SUCCESS_MSG);
192 /* If no verification is requested, then simply return an OK */
193 if (!(SESS_ALL & flags)) {
198 SET_MSG(msg, ABORT_MSG);
199 return SESSION_FAILED;
203 /* Find the '\\' in the username */
204 /* This needs to be fixed to support different username schemes */
205 if ((usr = strchr(user, '\\')) == NULL)
212 PAM_password = passwd;
214 dbglog("Initializing PAM (%d) for user %s", flags, usr);
215 pam_error = pam_start (SERVICE_NAME, usr, &pam_conv_data, &pamh);
216 dbglog("---> PAM INIT Result = %d", pam_error);
217 ok = (pam_error == PAM_SUCCESS);
220 ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) &&
221 (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS);
224 if (ok && (SESS_AUTH & flags)) {
225 dbglog("Attempting PAM authentication");
226 pam_error = pam_authenticate (pamh, PAM_SILENT);
227 if (pam_error == PAM_SUCCESS) {
228 /* PAM auth was OK */
229 dbglog("PAM Authentication OK for %s", user);
231 /* No matter the reason, we fail because we're authenticating */
233 if (pam_error == PAM_USER_UNKNOWN) {
234 dbglog("User unknown, failing PAM authentication");
235 SET_MSG(msg, "User unknown - cannot authenticate via PAM");
237 /* Any other error means authentication was bad */
238 dbglog("PAM Authentication failed: %d: %s", pam_error,
239 pam_strerror(pamh, pam_error));
240 SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
245 if (ok && (SESS_ACCT & flags)) {
246 dbglog("Attempting PAM account checks");
247 pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
248 if (pam_error == PAM_SUCCESS) {
250 * PAM account was OK, set the flag which indicates that we should
251 * try to perform the session checks.
254 dbglog("PAM Account OK for %s", user);
257 * If the account checks fail, then we should not try to perform
258 * the session check, because they don't make sense.
261 if (pam_error == PAM_USER_UNKNOWN) {
263 * We're checking the account, so it's ok to not have one
264 * because the user might come from the secrets files, or some
267 dbglog("User unknown, ignoring PAM restrictions");
268 SET_MSG(msg, "User unknown - ignoring PAM restrictions");
270 /* Any other error means session is rejected */
272 dbglog("PAM Account checks failed: %d: %s", pam_error,
273 pam_strerror(pamh, pam_error));
274 SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
279 if (ok && try_session && (SESS_ACCT & flags)) {
280 /* Only open a session if the user's account was found */
281 pam_error = pam_open_session (pamh, PAM_SILENT);
282 if (pam_error == PAM_SUCCESS) {
283 dbglog("PAM Session opened for user %s", user);
286 dbglog("PAM Session denied for user %s", user);
287 SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
292 /* This is needed because apparently the PAM stuff closes the log */
295 /* If our PAM checks have already failed, then we must return a failure */
296 if (!ok) return SESSION_FAILED;
298 #else /* #ifdef PPP_WITH_PAM */
301 * Use the non-PAM methods directly. 'pw' will remain NULL if the user
302 * has not been authenticated using local UNIX system services.
306 if ((SESS_AUTH & flags)) {
311 * Here, we bail if we have no user account, because there is nothing
315 return SESSION_FAILED;
319 spwd = getspnam(user);
323 * If there is no shadow entry for the user, then we can't verify the
327 return SESSION_FAILED;
330 * We check validity all the time, because if the password has expired,
331 * then clearly we should not authenticate against it (if we're being
332 * called for authentication only). Thus, in this particular instance,
333 * there is no real difference between using the AUTH, SESS or ACCT
334 * flags, or combinations thereof.
336 now = time(NULL) / 86400L;
337 if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
338 || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
339 && spwd->sp_lstchg >= 0
340 && now >= spwd->sp_lstchg + spwd->sp_max)) {
341 warn("Password for %s has expired", user);
342 return SESSION_FAILED;
345 /* We have a valid shadow entry, keep the password */
346 pw->pw_passwd = spwd->sp_pwdp;
348 #endif /* #ifdef HAVE_SHADOW_H */
351 * If no passwd, don't let them login if we're authenticating.
353 if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2)
354 return SESSION_FAILED;
356 cbuf = crypt(passwd, pw->pw_passwd);
357 if (!cbuf || strcmp(cbuf, pw->pw_passwd) != 0)
359 return SESSION_FAILED;
362 #endif /* #ifdef PPP_WITH_PAM */
365 * Write a wtmp entry for this user.
368 if (SESS_ACCT & flags) {
369 if (strncmp(ttyName, "/dev/", 5) == 0)
371 logwtmp(ttyName, user, ifname); /* Add wtmp login entry */
374 #if defined(_PATH_LASTLOG) && !defined(PPP_WITH_PAM)
376 * Enter the user in lastlog only if he has been authenticated using
377 * local system services. If he has not, then we don't know what his
378 * UID might be, and lastlog is indexed by UID.
385 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
386 (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
387 memset((void *)&ll, 0, sizeof(ll));
390 strlcpy(ll.ll_line, ttyName, sizeof(ll.ll_line));
391 strlcpy(ll.ll_host, ifname, sizeof(ll.ll_host));
392 (void)write(fd, (char *)&ll, sizeof(ll));
396 #endif /* _PATH_LASTLOG and not PPP_WITH_PAM */
397 info("user %s logged in on tty %s intf %s", user, ttyName, ifname);
404 * session_end - Logout the user.
407 session_end(const char* ttyName)
410 int pam_error = PAM_SUCCESS;
413 if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT);
415 pam_end (pamh, pam_error);
417 /* Apparently the pam stuff does closelog(). */
422 if (strncmp(ttyName, "/dev/", 5) == 0)
424 logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */