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