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 (at) 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 (at) 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 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 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 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