• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * session.c - PPP session control.
3  *
4  * Copyright (c) 2007 Diego Rivera. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
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.
16  *
17  * 3. Redistributions of any form whatsoever must retain the following
18  *    acknowledgment:
19  *    "This product includes software developed by Paul Mackerras
20  *     <paulus@samba.org>".
21  *
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.
29  *
30  * Derived from auth.c, which is:
31  *
32  * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  *
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  *
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
44  *    distribution.
45  *
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
52  *      5000 Forbes Avenue
53  *      Pittsburgh, PA  15213-3890
54  *      (412) 268-4387, fax: (412) 268-7395
55  *      tech-transfer@andrew.cmu.edu
56  *
57  * 4. Redistributions of any form whatsoever must retain the following
58  *    acknowledgment:
59  *    "This product includes software developed by Computing Services
60  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
61  *
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.
69  */
70 
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <pwd.h>
75 #if !defined(__ANDROID__)
76 #include <crypt.h>
77 #endif
78 #ifdef HAS_SHADOW
79 #include <shadow.h>
80 #endif
81 #include <time.h>
82 #include <utmp.h>
83 #include <fcntl.h>
84 #include <unistd.h>
85 #include "pppd.h"
86 #include "session.h"
87 
88 #ifdef USE_PAM
89 #include <security/pam_appl.h>
90 #endif /* #ifdef USE_PAM */
91 
92 #define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
93 #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
94 
95 #define SUCCESS_MSG "Session started successfully"
96 #define ABORT_MSG "Session can't be started without a username"
97 #define SERVICE_NAME "ppp"
98 
99 #define SESSION_FAILED  0
100 #define SESSION_OK      1
101 
102 /* We have successfully started a session */
103 static bool logged_in = 0;
104 
105 #ifdef USE_PAM
106 /*
107  * Static variables used to communicate between the conversation function
108  * and the server_login function
109  */
110 static const char *PAM_username;
111 static const char *PAM_password;
112 static int   PAM_session = 0;
113 static pam_handle_t *pamh = NULL;
114 
115 /* PAM conversation function
116  * Here we assume (for now, at least) that echo on means login name, and
117  * echo off means password.
118  */
119 
conversation(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)120 static int conversation (int num_msg,
121 #ifndef SOL2
122     const
123 #endif
124     struct pam_message **msg,
125     struct pam_response **resp, void *appdata_ptr)
126 {
127     int replies = 0;
128     struct pam_response *reply = NULL;
129 
130     reply = malloc(sizeof(struct pam_response) * num_msg);
131     if (!reply) return PAM_CONV_ERR;
132 
133     for (replies = 0; replies < num_msg; replies++) {
134         switch (msg[replies]->msg_style) {
135             case PAM_PROMPT_ECHO_ON:
136                 reply[replies].resp_retcode = PAM_SUCCESS;
137                 reply[replies].resp = COPY_STRING(PAM_username);
138                 /* PAM frees resp */
139                 break;
140             case PAM_PROMPT_ECHO_OFF:
141                 reply[replies].resp_retcode = PAM_SUCCESS;
142                 reply[replies].resp = COPY_STRING(PAM_password);
143                 /* PAM frees resp */
144                 break;
145             case PAM_TEXT_INFO:
146                 /* fall through */
147             case PAM_ERROR_MSG:
148                 /* ignore it, but pam still wants a NULL response... */
149                 reply[replies].resp_retcode = PAM_SUCCESS;
150                 reply[replies].resp = NULL;
151                 break;
152             default:
153                 /* Must be an error of some sort... */
154                 free (reply);
155                 return PAM_CONV_ERR;
156         }
157     }
158     *resp = reply;
159     return PAM_SUCCESS;
160 }
161 
162 static struct pam_conv pam_conv_data = {
163     &conversation,
164     NULL
165 };
166 #endif /* #ifdef USE_PAM */
167 
168 int
session_start(flags,user,passwd,ttyName,msg)169 session_start(flags, user, passwd, ttyName, msg)
170     const int flags;
171     const char *user;
172     const char *passwd;
173     const char *ttyName;
174     char **msg;
175 {
176 #ifdef USE_PAM
177     bool ok = 1;
178     const char *usr;
179     int pam_error;
180     bool try_session = 0;
181 #else /* #ifdef USE_PAM */
182     struct passwd *pw;
183     char *cbuf;
184 #ifdef HAS_SHADOW
185     struct spwd *spwd;
186     struct spwd *getspnam();
187     long now = 0;
188 #endif /* #ifdef HAS_SHADOW */
189 #endif /* #ifdef USE_PAM */
190 
191     SET_MSG(msg, SUCCESS_MSG);
192 
193     /* If no verification is requested, then simply return an OK */
194     if (!(SESS_ALL & flags)) {
195         return SESSION_OK;
196     }
197 
198 #if defined(__ANDROID__)
199     return SESSION_FAILED;
200 #endif
201 
202     if (user == NULL) {
203        SET_MSG(msg, ABORT_MSG);
204        return SESSION_FAILED;
205     }
206 
207 #ifdef USE_PAM
208     /* Find the '\\' in the username */
209     /* This needs to be fixed to support different username schemes */
210     if ((usr = strchr(user, '\\')) == NULL)
211 	usr = user;
212     else
213 	usr++;
214 
215     PAM_session = 0;
216     PAM_username = usr;
217     PAM_password = passwd;
218 
219     dbglog("Initializing PAM (%d) for user %s", flags, usr);
220     pam_error = pam_start (SERVICE_NAME, usr, &pam_conv_data, &pamh);
221     dbglog("---> PAM INIT Result = %d", pam_error);
222     ok = (pam_error == PAM_SUCCESS);
223 
224     if (ok) {
225         ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) &&
226 	    (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS);
227     }
228 
229     if (ok && (SESS_AUTH & flags)) {
230         dbglog("Attempting PAM authentication");
231         pam_error = pam_authenticate (pamh, PAM_SILENT);
232         if (pam_error == PAM_SUCCESS) {
233             /* PAM auth was OK */
234             dbglog("PAM Authentication OK for %s", user);
235         } else {
236             /* No matter the reason, we fail because we're authenticating */
237             ok = 0;
238             if (pam_error == PAM_USER_UNKNOWN) {
239                 dbglog("User unknown, failing PAM authentication");
240                 SET_MSG(msg, "User unknown - cannot authenticate via PAM");
241             } else {
242                 /* Any other error means authentication was bad */
243                 dbglog("PAM Authentication failed: %d: %s", pam_error,
244 		       pam_strerror(pamh, pam_error));
245                 SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
246             }
247         }
248     }
249 
250     if (ok && (SESS_ACCT & flags)) {
251         dbglog("Attempting PAM account checks");
252         pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
253         if (pam_error == PAM_SUCCESS) {
254             /*
255 	     * PAM account was OK, set the flag which indicates that we should
256 	     * try to perform the session checks.
257 	     */
258             try_session = 1;
259             dbglog("PAM Account OK for %s", user);
260         } else {
261             /*
262 	     * If the account checks fail, then we should not try to perform
263 	     * the session check, because they don't make sense.
264 	     */
265             try_session = 0;
266             if (pam_error == PAM_USER_UNKNOWN) {
267                 /*
268 		 * We're checking the account, so it's ok to not have one
269 		 * because the user might come from the secrets files, or some
270 		 * other plugin.
271 		 */
272                 dbglog("User unknown, ignoring PAM restrictions");
273                 SET_MSG(msg, "User unknown - ignoring PAM restrictions");
274             } else {
275                 /* Any other error means session is rejected */
276                 ok = 0;
277                 dbglog("PAM Account checks failed: %d: %s", pam_error,
278 		       pam_strerror(pamh, pam_error));
279                 SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
280             }
281         }
282     }
283 
284     if (ok && try_session && (SESS_ACCT & flags)) {
285         /* Only open a session if the user's account was found */
286         pam_error = pam_open_session (pamh, PAM_SILENT);
287         if (pam_error == PAM_SUCCESS) {
288             dbglog("PAM Session opened for user %s", user);
289             PAM_session = 1;
290         } else {
291             dbglog("PAM Session denied for user %s", user);
292             SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
293             ok = 0;
294         }
295     }
296 
297     /* This is needed because apparently the PAM stuff closes the log */
298     reopen_log();
299 
300     /* If our PAM checks have already failed, then we must return a failure */
301     if (!ok) return SESSION_FAILED;
302 
303 #elif !defined(__ANDROID__) /* #ifdef USE_PAM */
304 
305 /*
306  * Use the non-PAM methods directly.  'pw' will remain NULL if the user
307  * has not been authenticated using local UNIX system services.
308  */
309 
310     pw = NULL;
311     if ((SESS_AUTH & flags)) {
312 	pw = getpwnam(user);
313 
314 	endpwent();
315 	/*
316 	 * Here, we bail if we have no user account, because there is nothing
317 	 * to verify against.
318 	 */
319 	if (pw == NULL)
320 	    return SESSION_FAILED;
321 
322 #ifdef HAS_SHADOW
323 
324 	spwd = getspnam(user);
325 	endspent();
326 
327 	/*
328 	 * If there is no shadow entry for the user, then we can't verify the
329 	 * account.
330 	 */
331 	if (spwd == NULL)
332 	    return SESSION_FAILED;
333 
334 	/*
335 	 * We check validity all the time, because if the password has expired,
336 	 * then clearly we should not authenticate against it (if we're being
337 	 * called for authentication only).  Thus, in this particular instance,
338 	 * there is no real difference between using the AUTH, SESS or ACCT
339 	 * flags, or combinations thereof.
340 	 */
341 	now = time(NULL) / 86400L;
342 	if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
343 	    || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
344 	    && spwd->sp_lstchg >= 0
345 	    && now >= spwd->sp_lstchg + spwd->sp_max)) {
346 	    warn("Password for %s has expired", user);
347 	    return SESSION_FAILED;
348 	}
349 
350 	/* We have a valid shadow entry, keep the password */
351 	pw->pw_passwd = spwd->sp_pwdp;
352 
353 #endif /* #ifdef HAS_SHADOW */
354 
355 	/*
356 	 * If no passwd, don't let them login if we're authenticating.
357 	 */
358         if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2)
359             return SESSION_FAILED;
360 	cbuf = crypt(passwd, pw->pw_passwd);
361 	if (!cbuf || strcmp(cbuf, pw->pw_passwd) != 0)
362             return SESSION_FAILED;
363     }
364 
365 #endif /* #ifdef USE_PAM */
366 
367     /*
368      * Write a wtmp entry for this user.
369      */
370 
371     if (SESS_ACCT & flags) {
372 	if (strncmp(ttyName, "/dev/", 5) == 0)
373 	    ttyName += 5;
374 	logwtmp(ttyName, user, ifname); /* Add wtmp login entry */
375 	logged_in = 1;
376 
377 #if defined(_PATH_LASTLOG) && !defined(USE_PAM)
378 	/*
379 	 * Enter the user in lastlog only if he has been authenticated using
380 	 * local system services.  If he has not, then we don't know what his
381 	 * UID might be, and lastlog is indexed by UID.
382 	 */
383 	if (pw != NULL) {
384             struct lastlog ll;
385             int fd;
386 	    time_t tnow;
387 
388             if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
389                 (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
390                 memset((void *)&ll, 0, sizeof(ll));
391 		(void)time(&tnow);
392                 ll.ll_time = tnow;
393                 (void)strncpy(ll.ll_line, ttyName, sizeof(ll.ll_line));
394                 (void)strncpy(ll.ll_host, ifname, sizeof(ll.ll_host));
395                 (void)write(fd, (char *)&ll, sizeof(ll));
396                 (void)close(fd);
397             }
398 	}
399 #endif /* _PATH_LASTLOG and not USE_PAM */
400 	info("user %s logged in on tty %s intf %s", user, ttyName, ifname);
401     }
402 
403     return SESSION_OK;
404 }
405 
406 /*
407  * session_end - Logout the user.
408  */
409 void
session_end(const char * ttyName)410 session_end(const char* ttyName)
411 {
412 #ifdef USE_PAM
413     int pam_error = PAM_SUCCESS;
414 
415     if (pamh != NULL) {
416         if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT);
417         PAM_session = 0;
418         pam_end (pamh, pam_error);
419         pamh = NULL;
420 	/* Apparently the pam stuff does closelog(). */
421 	reopen_log();
422     }
423 #endif
424     if (logged_in) {
425 	if (strncmp(ttyName, "/dev/", 5) == 0)
426 	    ttyName += 5;
427 	logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */
428 	logged_in = 0;
429     }
430 }
431