• Home
  • History
  • Annotate
  • 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