]> git.ozlabs.org Git - ppp.git/blob - pppd/session.c
pppd man page: Update header to refer to pppd 2.5.x
[ppp.git] / pppd / session.c
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 #ifdef HAVE_CONFIG_H
72 #include "config.h"
73 #endif
74
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <pwd.h>
79
80 #ifdef HAVE_CRYPT_H
81 #include <crypt.h>
82 #endif
83
84 #ifdef HAVE_SHADOW_H
85 #include <shadow.h>
86 #endif
87
88 #include <time.h>
89 #include <utmp.h>
90 #include <fcntl.h>
91 #include <unistd.h>
92 #include "pppd-private.h"
93 #include "session.h"
94
95 #ifdef PPP_WITH_PAM
96 #include <security/pam_appl.h>
97 #endif /* #ifdef PPP_WITH_PAM */
98
99 #define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
100 #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
101
102 #define SUCCESS_MSG "Session started successfully"
103 #define ABORT_MSG "Session can't be started without a username"
104 #define SERVICE_NAME "ppp"
105
106 #define SESSION_FAILED  0
107 #define SESSION_OK      1
108
109 /* We have successfully started a session */
110 static bool logged_in = 0;
111
112 #ifdef PPP_WITH_PAM
113 /*
114  * Static variables used to communicate between the conversation function
115  * and the server_login function
116  */
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;
121
122 /* PAM conversation function
123  * Here we assume (for now, at least) that echo on means login name, and
124  * echo off means password.
125  */
126
127 static int conversation (int num_msg,
128     const struct pam_message **msg,
129     struct pam_response **resp, void *appdata_ptr)
130 {
131     int replies = 0;
132     struct pam_response *reply = NULL;
133
134     reply = malloc(sizeof(struct pam_response) * num_msg);
135     if (!reply) return PAM_CONV_ERR;
136
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);
142                 /* PAM frees resp */
143                 break;
144             case PAM_PROMPT_ECHO_OFF:
145                 reply[replies].resp_retcode = PAM_SUCCESS;
146                 reply[replies].resp = COPY_STRING(PAM_password);
147                 /* PAM frees resp */
148                 break;
149             case PAM_TEXT_INFO:
150                 /* fall through */
151             case PAM_ERROR_MSG:
152                 /* ignore it, but pam still wants a NULL response... */
153                 reply[replies].resp_retcode = PAM_SUCCESS;
154                 reply[replies].resp = NULL;
155                 break;
156             default:
157                 /* Must be an error of some sort... */
158                 free (reply);
159                 return PAM_CONV_ERR;
160         }
161     }
162     *resp = reply;
163     return PAM_SUCCESS;
164 }
165
166 static struct pam_conv pam_conv_data = {
167     &conversation,
168     NULL
169 };
170 #endif /* #ifdef PPP_WITH_PAM */
171
172 int
173 session_start(const int flags, const char *user, const char *passwd, const char *ttyName, char **msg)
174 {
175 #ifdef PPP_WITH_PAM
176     bool ok = 1;
177     const char *usr;
178     int pam_error;
179     bool try_session = 0;
180 #else /* #ifdef PPP_WITH_PAM */
181     struct passwd *pw;
182     char *cbuf;
183 #ifdef HAVE_SHADOW_H
184     struct spwd *spwd;
185     struct spwd *getspnam();
186     long now = 0;
187 #endif /* #ifdef HAVE_SHADOW_H */
188 #endif /* #ifdef PPP_WITH_PAM */
189
190     SET_MSG(msg, SUCCESS_MSG);
191
192     /* If no verification is requested, then simply return an OK */
193     if (!(SESS_ALL & flags)) {
194         return SESSION_OK;
195     }
196
197     if (user == NULL) {
198        SET_MSG(msg, ABORT_MSG);
199        return SESSION_FAILED;
200     }
201
202 #ifdef PPP_WITH_PAM
203     /* Find the '\\' in the username */
204     /* This needs to be fixed to support different username schemes */
205     if ((usr = strchr(user, '\\')) == NULL)
206         usr = user;
207     else
208         usr++;
209
210     PAM_session = 0;
211     PAM_username = usr;
212     PAM_password = passwd;
213
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);
218
219     if (ok) {
220         ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) &&
221             (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS);
222     }
223
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);
230         } else {
231             /* No matter the reason, we fail because we're authenticating */
232             ok = 0;
233             if (pam_error == PAM_USER_UNKNOWN) {
234                 dbglog("User unknown, failing PAM authentication");
235                 SET_MSG(msg, "User unknown - cannot authenticate via PAM");
236             } else {
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));
241             }
242         }
243     }
244
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) {
249             /*
250              * PAM account was OK, set the flag which indicates that we should
251              * try to perform the session checks.
252              */
253             try_session = 1;
254             dbglog("PAM Account OK for %s", user);
255         } else {
256             /*
257              * If the account checks fail, then we should not try to perform
258              * the session check, because they don't make sense.
259              */
260             try_session = 0;
261             if (pam_error == PAM_USER_UNKNOWN) {
262                 /*
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
265                  * other plugin.
266                  */
267                 dbglog("User unknown, ignoring PAM restrictions");
268                 SET_MSG(msg, "User unknown - ignoring PAM restrictions");
269             } else {
270                 /* Any other error means session is rejected */
271                 ok = 0;
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));
275             }
276         }
277     }
278
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);
284             PAM_session = 1;
285         } else {
286             dbglog("PAM Session denied for user %s", user);
287             SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
288             ok = 0;
289         }
290     }
291
292     /* This is needed because apparently the PAM stuff closes the log */
293     reopen_log();
294
295     /* If our PAM checks have already failed, then we must return a failure */
296     if (!ok) return SESSION_FAILED;
297
298 #else /* #ifdef PPP_WITH_PAM */
299
300 /*
301  * Use the non-PAM methods directly.  'pw' will remain NULL if the user
302  * has not been authenticated using local UNIX system services.
303  */
304
305     pw = NULL;
306     if ((SESS_AUTH & flags)) {
307         pw = getpwnam(user);
308
309         endpwent();
310         /*
311          * Here, we bail if we have no user account, because there is nothing
312          * to verify against.
313          */
314         if (pw == NULL)
315             return SESSION_FAILED;
316
317 #ifdef HAVE_SHADOW_H
318
319         spwd = getspnam(user);
320         endspent();
321
322         /*
323          * If there is no shadow entry for the user, then we can't verify the
324          * account.
325          */
326         if (spwd == NULL)
327             return SESSION_FAILED;
328
329         /*
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.
335          */
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;
343         }
344
345         /* We have a valid shadow entry, keep the password */
346         pw->pw_passwd = spwd->sp_pwdp;
347
348 #endif /* #ifdef HAVE_SHADOW_H */
349
350         /*
351          * If no passwd, don't let them login if we're authenticating.
352          */
353         if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2)
354             return SESSION_FAILED;
355 #ifdef HAVE_CRYPT_H
356         cbuf = crypt(passwd, pw->pw_passwd);
357         if (!cbuf || strcmp(cbuf, pw->pw_passwd) != 0)
358 #endif
359             return SESSION_FAILED;
360     }
361
362 #endif /* #ifdef PPP_WITH_PAM */
363
364     /*
365      * Write a wtmp entry for this user.
366      */
367
368     if (SESS_ACCT & flags) {
369         if (strncmp(ttyName, "/dev/", 5) == 0)
370             ttyName += 5;
371         logwtmp(ttyName, user, ifname); /* Add wtmp login entry */
372         logged_in = 1;
373
374 #if defined(_PATH_LASTLOG) && !defined(PPP_WITH_PAM)
375         /*
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.
379          */
380         if (pw != NULL) {
381             struct lastlog ll;
382             int fd;
383             time_t tnow;
384
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));
388                 (void)time(&tnow);
389                 ll.ll_time = tnow;
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));
393                 (void)close(fd);
394             }
395         }
396 #endif /* _PATH_LASTLOG and not PPP_WITH_PAM */
397         info("user %s logged in on tty %s intf %s", user, ttyName, ifname);
398     }
399
400     return SESSION_OK;
401 }
402
403 /*
404  * session_end - Logout the user.
405  */
406 void
407 session_end(const char* ttyName)
408 {
409 #ifdef PPP_WITH_PAM
410     int pam_error = PAM_SUCCESS;
411
412     if (pamh != NULL) {
413         if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT);
414         PAM_session = 0;
415         pam_end (pamh, pam_error);
416         pamh = NULL;
417         /* Apparently the pam stuff does closelog(). */
418         reopen_log();
419     }
420 #endif
421     if (logged_in) {
422         if (strncmp(ttyName, "/dev/", 5) == 0)
423             ttyName += 5;
424         logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */
425         logged_in = 0;
426     }
427 }