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.
87 #include <security/pam_appl.h>
88 #endif /* #ifdef USE_PAM */
90 #define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
91 #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
93 #define SUCCESS_MSG "Session started successfully"
94 #define ABORT_MSG "Session can't be started without a username"
95 #define SERVICE_NAME "ppp"
97 #define SESSION_FAILED 0
100 /* We have successfully started a session */
101 static bool logged_in
= 0;
105 * Static variables used to communicate between the conversation function
106 * and the server_login function
108 static const char *PAM_username
;
109 static const char *PAM_password
;
110 static int PAM_session
= 0;
111 static pam_handle_t
*pamh
= NULL
;
113 /* PAM conversation function
114 * Here we assume (for now, at least) that echo on means login name, and
115 * echo off means password.
118 static int conversation (int num_msg
,
122 struct pam_message
**msg
,
123 struct pam_response
**resp
, void *appdata_ptr
)
126 struct pam_response
*reply
= NULL
;
128 reply
= malloc(sizeof(struct pam_response
) * num_msg
);
129 if (!reply
) return PAM_CONV_ERR
;
131 for (replies
= 0; replies
< num_msg
; replies
++) {
132 switch (msg
[replies
]->msg_style
) {
133 case PAM_PROMPT_ECHO_ON
:
134 reply
[replies
].resp_retcode
= PAM_SUCCESS
;
135 reply
[replies
].resp
= COPY_STRING(PAM_username
);
138 case PAM_PROMPT_ECHO_OFF
:
139 reply
[replies
].resp_retcode
= PAM_SUCCESS
;
140 reply
[replies
].resp
= COPY_STRING(PAM_password
);
146 /* ignore it, but pam still wants a NULL response... */
147 reply
[replies
].resp_retcode
= PAM_SUCCESS
;
148 reply
[replies
].resp
= NULL
;
151 /* Must be an error of some sort... */
160 static struct pam_conv pam_conv_data
= {
164 #endif /* #ifdef USE_PAM */
167 session_start(flags
, user
, passwd
, ttyName
, msg
)
178 bool try_session
= 0;
179 #else /* #ifdef USE_PAM */
183 struct spwd
*getspnam();
185 #endif /* #ifdef HAS_SHADOW */
186 #endif /* #ifdef USE_PAM */
188 SET_MSG(msg
, SUCCESS_MSG
);
190 /* If no verification is requested, then simply return an OK */
191 if (!(SESS_ALL
& flags
)) {
196 SET_MSG(msg
, ABORT_MSG
);
197 return SESSION_FAILED
;
201 /* Find the '\\' in the username */
202 /* This needs to be fixed to support different username schemes */
203 if ((usr
= strchr(user
, '\\')) == NULL
)
210 PAM_password
= passwd
;
212 dbglog("Initializing PAM (%d) for user %s", flags
, usr
);
213 pam_error
= pam_start (SERVICE_NAME
, usr
, &pam_conv_data
, &pamh
);
214 dbglog("---> PAM INIT Result = %d", pam_error
);
215 ok
= (pam_error
== PAM_SUCCESS
);
218 ok
= (pam_set_item(pamh
, PAM_TTY
, ttyName
) == PAM_SUCCESS
) &&
219 (pam_set_item(pamh
, PAM_RHOST
, ifname
) == PAM_SUCCESS
);
222 if (ok
&& (SESS_AUTH
& flags
)) {
223 dbglog("Attempting PAM authentication");
224 pam_error
= pam_authenticate (pamh
, PAM_SILENT
);
225 if (pam_error
== PAM_SUCCESS
) {
226 /* PAM auth was OK */
227 dbglog("PAM Authentication OK for %s", user
);
229 /* No matter the reason, we fail because we're authenticating */
231 if (pam_error
== PAM_USER_UNKNOWN
) {
232 dbglog("User unknown, failing PAM authentication");
233 SET_MSG(msg
, "User unknown - cannot authenticate via PAM");
235 /* Any other error means authentication was bad */
236 dbglog("PAM Authentication failed: %d: %s", pam_error
,
237 pam_strerror(pamh
, pam_error
));
238 SET_MSG(msg
, (char *) pam_strerror (pamh
, pam_error
));
243 if (ok
&& (SESS_ACCT
& flags
)) {
244 dbglog("Attempting PAM account checks");
245 pam_error
= pam_acct_mgmt (pamh
, PAM_SILENT
);
246 if (pam_error
== PAM_SUCCESS
) {
248 * PAM account was OK, set the flag which indicates that we should
249 * try to perform the session checks.
252 dbglog("PAM Account OK for %s", user
);
255 * If the account checks fail, then we should not try to perform
256 * the session check, because they don't make sense.
259 if (pam_error
== PAM_USER_UNKNOWN
) {
261 * We're checking the account, so it's ok to not have one
262 * because the user might come from the secrets files, or some
265 dbglog("User unknown, ignoring PAM restrictions");
266 SET_MSG(msg
, "User unknown - ignoring PAM restrictions");
268 /* Any other error means session is rejected */
270 dbglog("PAM Account checks failed: %d: %s", pam_error
,
271 pam_strerror(pamh
, pam_error
));
272 SET_MSG(msg
, (char *) pam_strerror (pamh
, pam_error
));
277 if (ok
&& try_session
&& (SESS_ACCT
& flags
)) {
278 /* Only open a session if the user's account was found */
279 pam_error
= pam_open_session (pamh
, PAM_SILENT
);
280 if (pam_error
== PAM_SUCCESS
) {
281 dbglog("PAM Session opened for user %s", user
);
284 dbglog("PAM Session denied for user %s", user
);
285 SET_MSG(msg
, (char *) pam_strerror (pamh
, pam_error
));
290 /* This is needed because apparently the PAM stuff closes the log */
293 /* If our PAM checks have already failed, then we must return a failure */
294 if (!ok
) return SESSION_FAILED
;
296 #else /* #ifdef USE_PAM */
299 * Use the non-PAM methods directly. 'pw' will remain NULL if the user
300 * has not been authenticated using local UNIX system services.
304 if ((SESS_AUTH
& flags
)) {
309 * Here, we bail if we have no user account, because there is nothing
313 return SESSION_FAILED
;
317 spwd
= getspnam(user
);
321 * If there is no shadow entry for the user, then we can't verify the
325 return SESSION_FAILED
;
328 * We check validity all the time, because if the password has expired,
329 * then clearly we should not authenticate against it (if we're being
330 * called for authentication only). Thus, in this particular instance,
331 * there is no real difference between using the AUTH, SESS or ACCT
332 * flags, or combinations thereof.
334 now
= time(NULL
) / 86400L;
335 if ((spwd
->sp_expire
> 0 && now
>= spwd
->sp_expire
)
336 || ((spwd
->sp_max
>= 0 && spwd
->sp_max
< 10000)
337 && spwd
->sp_lstchg
>= 0
338 && now
>= spwd
->sp_lstchg
+ spwd
->sp_max
)) {
339 warn("Password for %s has expired", user
);
340 return SESSION_FAILED
;
343 /* We have a valid shadow entry, keep the password */
344 pw
->pw_passwd
= spwd
->sp_pwdp
;
346 #endif /* #ifdef HAS_SHADOW */
349 * If no passwd, don't let them login if we're authenticating.
351 if (pw
->pw_passwd
== NULL
|| strlen(pw
->pw_passwd
) < 2
352 || strcmp(crypt(passwd
, pw
->pw_passwd
), pw
->pw_passwd
) != 0)
353 return SESSION_FAILED
;
356 #endif /* #ifdef USE_PAM */
359 * Write a wtmp entry for this user.
362 if (SESS_ACCT
& flags
) {
363 if (strncmp(ttyName
, "/dev/", 5) == 0)
365 logwtmp(ttyName
, user
, ifname
); /* Add wtmp login entry */
368 #if defined(_PATH_LASTLOG) && !defined(USE_PAM)
370 * Enter the user in lastlog only if he has been authenticated using
371 * local system services. If he has not, then we don't know what his
372 * UID might be, and lastlog is indexed by UID.
379 if ((fd
= open(_PATH_LASTLOG
, O_RDWR
, 0)) >= 0) {
380 (void)lseek(fd
, (off_t
)(pw
->pw_uid
* sizeof(ll
)), SEEK_SET
);
381 memset((void *)&ll
, 0, sizeof(ll
));
384 (void)strncpy(ll
.ll_line
, ttyName
, sizeof(ll
.ll_line
));
385 (void)strncpy(ll
.ll_host
, ifname
, sizeof(ll
.ll_host
));
386 (void)write(fd
, (char *)&ll
, sizeof(ll
));
390 #endif /* _PATH_LASTLOG and not USE_PAM */
391 info("user %s logged in on tty %s intf %s", user
, ttyName
, ifname
);
398 * session_end - Logout the user.
401 session_end(const char* ttyName
)
404 int pam_error
= PAM_SUCCESS
;
407 if (PAM_session
) pam_error
= pam_close_session (pamh
, PAM_SILENT
);
409 pam_end (pamh
, pam_error
);
411 /* Apparently the pam stuff does closelog(). */
416 if (strncmp(ttyName
, "/dev/", 5) == 0)
418 logwtmp(ttyName
, "", ""); /* Wipe out utmp logout entry */