1 /* $OpenBSD: auth_subr.c,v 1.56 2020/10/13 04:42:28 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2000-2002,2004 Todd C. Miller <millert@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 /*- 19 * Copyright (c) 1995,1996,1997 Berkeley Software Design, Inc. 20 * All rights reserved. 21 * 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that the following conditions 24 * are met: 25 * 1. Redistributions of source code must retain the above copyright 26 * notice, this list of conditions and the following disclaimer. 27 * 2. Redistributions in binary form must reproduce the above copyright 28 * notice, this list of conditions and the following disclaimer in the 29 * documentation and/or other materials provided with the distribution. 30 * 3. All advertising materials mentioning features or use of this software 31 * must display the following acknowledgement: 32 * This product includes software developed by Berkeley Software Design, 33 * Inc. 34 * 4. The name of Berkeley Software Design, Inc. may not be used to endorse 35 * or promote products derived from this software without specific prior 36 * written permission. 37 * 38 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND 39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 41 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE 42 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 44 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * 50 * BSDI $From: auth_subr.c,v 2.4 1999/09/08 04:10:40 prb Exp $ 51 */ 52 53 #include <sys/time.h> 54 #include <sys/resource.h> 55 #include <sys/socket.h> 56 #include <sys/wait.h> 57 58 #include <ctype.h> 59 #include <err.h> 60 #include <errno.h> 61 #include <fcntl.h> 62 #include <limits.h> 63 #include <paths.h> 64 #include <pwd.h> 65 #include <stdarg.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <syslog.h> 70 #include <unistd.h> 71 72 #include <login_cap.h> 73 74 #define MAXSPOOLSIZE (8*1024) /* Spool up to 8K of back info */ 75 76 struct rmfiles { 77 struct rmfiles *next; 78 char *file; 79 }; 80 81 struct authopts { 82 struct authopts *next; 83 char *opt; 84 }; 85 86 struct authdata { 87 struct authdata *next; 88 void *ptr; 89 size_t len; 90 }; 91 92 struct auth_session_t { 93 char *name; /* name of use being authenticated */ 94 char *style; /* style of authentication used */ 95 char *class; /* class of user */ 96 char *service; /* type of service being performed */ 97 char *challenge; /* last challenge issued */ 98 int flags; /* see below */ 99 struct passwd *pwd; /* password entry for user */ 100 struct timeval now; /* time of authentication */ 101 102 int state; /* authenticated state */ 103 104 struct rmfiles *rmlist; /* list of files to remove on failure */ 105 struct authopts *optlist; /* list of options to scripts */ 106 struct authdata *data; /* additional data to send to scripts */ 107 108 char spool[MAXSPOOLSIZE]; /* data returned from login script */ 109 int index; /* how much returned thus far */ 110 111 int fd; /* connection to authenticator */ 112 113 va_list ap0; /* argument list to auth_call */ 114 va_list ap; /* additional arguments to auth_call */ 115 }; 116 117 /* 118 * Internal flags 119 */ 120 #define AF_INTERACTIVE 0x0001 /* This is an interactive session */ 121 122 /* 123 * We cannot include bsd_auth.h until we define the above structures 124 */ 125 #include <bsd_auth.h> 126 127 /* 128 * Internally used functions 129 */ 130 static void _add_rmlist(auth_session_t *, char *); 131 static void _auth_spool(auth_session_t *, int); 132 static void _recv_fd(auth_session_t *, int); 133 static char *_auth_next_arg(auth_session_t *); 134 /* 135 * Set up a known environment for all authentication scripts. 136 */ 137 static char * const auth_environ[] = { 138 "PATH=" _PATH_DEFPATH, 139 "SHELL=" _PATH_BSHELL, 140 NULL, 141 }; 142 143 static char defservice[] = LOGIN_DEFSERVICE; 144 145 static va_list nilap; 146 147 /* 148 * Quick one liners that only exist to keep auth_session_t opaque 149 */ 150 void auth_setstate(auth_session_t *as, int s){ as->state = s; } 151 void auth_set_va_list(auth_session_t *as, va_list ap) { va_copy(as->ap, ap); } 152 int auth_getstate(auth_session_t *as) { return (as->state); } 153 struct passwd *auth_getpwd(auth_session_t *as) { return (as->pwd); } 154 DEF_WEAK(auth_setstate); 155 DEF_WEAK(auth_set_va_list); 156 DEF_WEAK(auth_getstate); 157 DEF_WEAK(auth_getpwd); 158 159 /* 160 * Open a new BSD Authentication session with the default service 161 * (which can be changed later). 162 */ 163 auth_session_t * 164 auth_open(void) 165 { 166 auth_session_t *as; 167 168 if ((as = calloc(1, sizeof(auth_session_t))) != NULL) { 169 as->service = defservice; 170 as->fd = -1; 171 } 172 173 return (as); 174 } 175 DEF_WEAK(auth_open); 176 177 /* 178 * Clean the specified BSD Authentication session. 179 */ 180 void 181 auth_clean(auth_session_t *as) 182 { 183 struct rmfiles *rm; 184 struct authdata *data; 185 186 as->state = 0; 187 188 auth_clrenv(as); 189 190 /* 191 * Clean out the rmlist and remove specified files 192 */ 193 while ((rm = as->rmlist) != NULL) { 194 as->rmlist = rm->next; 195 unlink(rm->file); 196 free(rm); 197 } 198 199 /* 200 * Clean out data 201 */ 202 while ((data = as->data) != NULL) { 203 if (as->data->len) 204 explicit_bzero(as->data->ptr, as->data->len); 205 as->data = data->next; 206 free(data); 207 } 208 209 auth_setitem(as, AUTHV_ALL, NULL); 210 211 if (as->pwd != NULL) { 212 explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); 213 free(as->pwd); 214 as->pwd = NULL; 215 } 216 217 if (as->fd != -1) { 218 close(as->fd); 219 as->fd = -1; 220 } 221 } 222 DEF_WEAK(auth_clean); 223 224 /* 225 * Close the specified BSD Authentication session. 226 * Return 0 if not authenticated. 227 */ 228 int 229 auth_close(auth_session_t *as) 230 { 231 struct rmfiles *rm; 232 struct authopts *opt; 233 struct authdata *data; 234 int s; 235 236 /* 237 * Save our return value 238 */ 239 s = as->state & AUTH_ALLOW; 240 241 if (s == 0) 242 as->index = 0; 243 244 auth_setenv(as); 245 246 247 /* 248 * Clean out the rmlist and remove specified files if the 249 * authentication failed 250 */ 251 while ((rm = as->rmlist) != NULL) { 252 as->rmlist = rm->next; 253 if (s == 0) 254 unlink(rm->file); 255 free(rm); 256 } 257 258 /* 259 * Clean out the opt list 260 */ 261 while ((opt = as->optlist) != NULL) { 262 as->optlist = opt->next; 263 free(opt); 264 } 265 266 /* 267 * Clean out data 268 */ 269 while ((data = as->data) != NULL) { 270 if (as->data->len) 271 explicit_bzero(as->data->ptr, as->data->len); 272 as->data = data->next; 273 free(data); 274 } 275 276 if (as->pwd != NULL) { 277 explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); 278 free(as->pwd); 279 as->pwd = NULL; 280 } 281 282 /* 283 * Clean up random variables 284 */ 285 if (as->service && as->service != defservice) 286 free(as->service); 287 free(as->challenge); 288 free(as->class); 289 free(as->style); 290 free(as->name); 291 292 free(as); 293 return (s); 294 } 295 DEF_WEAK(auth_close); 296 297 /* 298 * Request a challenge for the session. 299 * The name and style must have already been specified 300 */ 301 char * 302 auth_challenge(auth_session_t *as) 303 { 304 char path[PATH_MAX]; 305 int len; 306 307 if (as == NULL || as->style == NULL || as->name == NULL || 308 !_auth_validuser(as->name)) 309 return (NULL); 310 311 len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); 312 if (len < 0 || len >= sizeof(path)) 313 return (NULL); 314 315 as->state = 0; 316 317 free(as->challenge); 318 as->challenge = NULL; 319 320 auth_call(as, path, as->style, "-s", "challenge", "--", as->name, 321 as->class, (char *)NULL); 322 if (as->state & AUTH_CHALLENGE) 323 as->challenge = auth_getvalue(as, "challenge"); 324 as->state = 0; 325 as->index = 0; /* toss our data */ 326 return (as->challenge); 327 } 328 DEF_WEAK(auth_challenge); 329 330 /* 331 * Set/unset the requested environment variables. 332 * Mark the variables as set so they will not be set a second time. 333 * XXX - should provide a way to detect setenv() failure. 334 */ 335 void 336 auth_setenv(auth_session_t *as) 337 { 338 char *line, *name; 339 340 /* 341 * Set any environment variables we were asked for 342 */ 343 for (line = as->spool; line < as->spool + as->index;) { 344 if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { 345 if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { 346 /* only do it once! */ 347 line[0] = 'd'; line[1] = 'i'; line[2] = 'd'; 348 line += sizeof(BI_SETENV) - 1; 349 for (name = line; 350 isblank((unsigned char)*name); ++name) 351 ; 352 for (line = name; 353 *line && !isblank((unsigned char)*line); 354 ++line) 355 ; 356 if (*line) 357 *line++ = '\0'; 358 for (; isblank((unsigned char)*line); ++line) 359 ; 360 if (*line != '\0' && setenv(name, line, 1)) 361 warn("setenv(%s, %s)", name, line); 362 } 363 } else 364 if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { 365 if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) { 366 /* only do it once! */ 367 line[2] = 'd'; line[3] = 'i'; line[4] = 'd'; 368 line += sizeof(BI_UNSETENV) - 1; 369 for (name = line; 370 isblank((unsigned char)*name); ++name) 371 ; 372 for (line = name; 373 *line && !isblank((unsigned char)*line); 374 ++line) 375 ; 376 if (*line) 377 *line++ = '\0'; 378 unsetenv(name); 379 } 380 } 381 while (*line++) 382 ; 383 } 384 } 385 DEF_WEAK(auth_setenv); 386 387 /* 388 * Clear out any requested environment variables. 389 */ 390 void 391 auth_clrenv(auth_session_t *as) 392 { 393 char *line; 394 395 for (line = as->spool; line < as->spool + as->index;) { 396 if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { 397 if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { 398 line[0] = 'i'; line[1] = 'g'; line[2] = 'n'; 399 } 400 } else 401 if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { 402 if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) { 403 line[2] = 'i'; line[3] = 'g'; line[4] = 'n'; 404 } 405 } 406 while (*line++) 407 ; 408 } 409 } 410 DEF_WEAK(auth_clrenv); 411 412 char * 413 auth_getitem(auth_session_t *as, auth_item_t item) 414 { 415 if (as != NULL) { 416 switch (item) { 417 case AUTHV_CHALLENGE: 418 return (as->challenge); 419 case AUTHV_CLASS: 420 return (as->class); 421 case AUTHV_NAME: 422 return (as->name); 423 case AUTHV_SERVICE: 424 return (as->service ? as->service : defservice); 425 case AUTHV_STYLE: 426 return (as->style); 427 case AUTHV_INTERACTIVE: 428 return ((as->flags & AF_INTERACTIVE) ? "True" : NULL); 429 default: 430 break; 431 } 432 } 433 return (NULL); 434 } 435 DEF_WEAK(auth_getitem); 436 437 int 438 auth_setitem(auth_session_t *as, auth_item_t item, char *value) 439 { 440 if (as == NULL) { 441 errno = EINVAL; 442 return (-1); 443 } 444 445 switch (item) { 446 case AUTHV_ALL: 447 if (value != NULL) { 448 errno = EINVAL; 449 return (-1); 450 } 451 auth_setitem(as, AUTHV_CHALLENGE, NULL); 452 auth_setitem(as, AUTHV_CLASS, NULL); 453 auth_setitem(as, AUTHV_NAME, NULL); 454 auth_setitem(as, AUTHV_SERVICE, NULL); 455 auth_setitem(as, AUTHV_STYLE, NULL); 456 auth_setitem(as, AUTHV_INTERACTIVE, NULL); 457 return (0); 458 459 case AUTHV_CHALLENGE: 460 if (value == as->challenge) 461 return (0); 462 if (value != NULL && (value = strdup(value)) == NULL) 463 return (-1); 464 free(as->challenge); 465 as->challenge = value; 466 return (0); 467 468 case AUTHV_CLASS: 469 if (value == as->class) 470 return (0); 471 if (value != NULL && (value = strdup(value)) == NULL) 472 return (-1); 473 free(as->class); 474 as->class = value; 475 return (0); 476 477 case AUTHV_NAME: 478 if (value == as->name) 479 return (0); 480 if (value != NULL && !_auth_validuser(value)) { 481 errno = EINVAL; 482 return (-1); 483 } 484 if (value != NULL && (value = strdup(value)) == NULL) 485 return (-1); 486 free(as->name); 487 as->name = value; 488 return (0); 489 490 case AUTHV_SERVICE: 491 if (value == as->service) 492 return (0); 493 if (value == NULL || strcmp(value, defservice) == 0) 494 value = defservice; 495 else if ((value = strdup(value)) == NULL) 496 return (-1); 497 if (as->service && as->service != defservice) 498 free(as->service); 499 as->service = value; 500 return (0); 501 502 case AUTHV_STYLE: 503 if (value == as->style) 504 return (0); 505 if (value == NULL || strchr(value, '/') != NULL || 506 (value = strdup(value)) == NULL) 507 return (-1); 508 free(as->style); 509 as->style = value; 510 return (0); 511 512 case AUTHV_INTERACTIVE: 513 if (value == NULL) 514 as->flags &= ~AF_INTERACTIVE; 515 else 516 as->flags |= ~AF_INTERACTIVE; 517 return (0); 518 519 default: 520 errno = EINVAL; 521 return (-1); 522 } 523 } 524 DEF_WEAK(auth_setitem); 525 526 int 527 auth_setoption(auth_session_t *as, char *n, char *v) 528 { 529 struct authopts *opt; 530 size_t len = strlen(n) + strlen(v) + 2; 531 int ret; 532 533 if ((opt = malloc(sizeof(*opt) + len)) == NULL) 534 return (-1); 535 536 opt->opt = (char *)(opt + 1); 537 538 ret = snprintf(opt->opt, len, "%s=%s", n, v); 539 if (ret < 0 || ret >= len) { 540 free(opt); 541 errno = ENAMETOOLONG; 542 return (-1); 543 } 544 opt->next = as->optlist; 545 as->optlist = opt; 546 return(0); 547 } 548 DEF_WEAK(auth_setoption); 549 550 void 551 auth_clroptions(auth_session_t *as) 552 { 553 struct authopts *opt; 554 555 while ((opt = as->optlist) != NULL) { 556 as->optlist = opt->next; 557 free(opt); 558 } 559 } 560 DEF_WEAK(auth_clroptions); 561 562 void 563 auth_clroption(auth_session_t *as, char *option) 564 { 565 struct authopts *opt, *oopt; 566 size_t len; 567 568 len = strlen(option); 569 570 if ((opt = as->optlist) == NULL) 571 return; 572 573 if (strncmp(opt->opt, option, len) == 0 && 574 (opt->opt[len] == '=' || opt->opt[len] == '\0')) { 575 as->optlist = opt->next; 576 free(opt); 577 return; 578 } 579 580 while ((oopt = opt->next) != NULL) { 581 if (strncmp(oopt->opt, option, len) == 0 && 582 (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) { 583 opt->next = oopt->next; 584 free(oopt); 585 return; 586 } 587 opt = oopt; 588 } 589 } 590 DEF_WEAK(auth_clroption); 591 592 int 593 auth_setdata(auth_session_t *as, void *ptr, size_t len) 594 { 595 struct authdata *data, *dp; 596 597 if (len <= 0) 598 return (0); 599 600 if ((data = malloc(sizeof(*data) + len)) == NULL) 601 return (-1); 602 603 data->next = NULL; 604 data->len = len; 605 data->ptr = data + 1; 606 memcpy(data->ptr, ptr, len); 607 608 if (as->data == NULL) 609 as->data = data; 610 else { 611 for (dp = as->data; dp->next != NULL; dp = dp->next) 612 ; 613 dp->next = data; 614 } 615 return (0); 616 } 617 DEF_WEAK(auth_setdata); 618 619 int 620 auth_setpwd(auth_session_t *as, struct passwd *pwd) 621 { 622 struct passwd pwstore; 623 char *instance, pwbuf[_PW_BUF_LEN]; 624 625 if (pwd == NULL && as->pwd == NULL && as->name == NULL) 626 return (-1); /* true failure */ 627 628 if (pwd == NULL) { 629 /* 630 * If we were not passed in a pwd structure we need to 631 * go find one for ourself. Always look up the username 632 * (if it is defined) in the passwd database to see if there 633 * is an entry for the user. If not, either use the current 634 * entry or simply return a 1 which implies there is 635 * no user by that name here. This is not a failure, just 636 * a point of information. 637 */ 638 if (as->name == NULL) 639 return (0); 640 getpwnam_r(as->name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); 641 if (pwd == NULL) { 642 instance = strchr(as->name, '/'); 643 if (instance == NULL) 644 return (as->pwd ? 0 : 1); 645 if (strcmp(instance, "/root") == 0) { 646 getpwnam_r(instance + 1, &pwstore, pwbuf, 647 sizeof(pwbuf), &pwd); 648 } 649 if (pwd == NULL) 650 return (as->pwd ? 0 : 1); 651 } 652 } 653 if ((pwd = pw_dup(pwd)) == NULL) 654 return (-1); /* true failure */ 655 if (as->pwd) { 656 explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); 657 free(as->pwd); 658 } 659 as->pwd = pwd; 660 return (0); 661 } 662 DEF_WEAK(auth_setpwd); 663 664 char * 665 auth_getvalue(auth_session_t *as, char *what) 666 { 667 char *line, *v, *value; 668 int n, len; 669 670 len = strlen(what); 671 672 for (line = as->spool; line < as->spool + as->index;) { 673 if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0) 674 goto next; 675 line += sizeof(BI_VALUE) - 1; 676 677 if (!isblank((unsigned char)*line)) 678 goto next; 679 680 while (isblank((unsigned char)*++line)) 681 ; 682 683 if (strncmp(line, what, len) != 0 || 684 !isblank((unsigned char)line[len])) 685 goto next; 686 line += len; 687 while (isblank((unsigned char)*++line)) 688 ; 689 value = strdup(line); 690 if (value == NULL) 691 return (NULL); 692 693 /* 694 * XXX - There should be a more standardized 695 * routine for doing this sort of thing. 696 */ 697 for (line = v = value; *line; ++line) { 698 if (*line == '\\') { 699 switch (*++line) { 700 case 'r': 701 *v++ = '\r'; 702 break; 703 case 'n': 704 *v++ = '\n'; 705 break; 706 case 't': 707 *v++ = '\t'; 708 break; 709 case '0': case '1': case '2': 710 case '3': case '4': case '5': 711 case '6': case '7': 712 n = *line - '0'; 713 if (isdigit((unsigned char)line[1])) { 714 ++line; 715 n <<= 3; 716 n |= *line-'0'; 717 } 718 if (isdigit((unsigned char)line[1])) { 719 ++line; 720 n <<= 3; 721 n |= *line-'0'; 722 } 723 break; 724 default: 725 *v++ = *line; 726 break; 727 } 728 } else 729 *v++ = *line; 730 } 731 *v = '\0'; 732 return (value); 733 next: 734 while (*line++) 735 ; 736 } 737 return (NULL); 738 } 739 DEF_WEAK(auth_getvalue); 740 741 quad_t 742 auth_check_expire(auth_session_t *as) 743 { 744 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 745 as->state &= ~AUTH_ALLOW; 746 as->state |= AUTH_EXPIRED; /* XXX */ 747 return (-1); 748 } 749 750 if (as->pwd == NULL) 751 return (0); 752 753 if (as->pwd && (quad_t)as->pwd->pw_expire != 0) { 754 if (as->now.tv_sec == 0) 755 WRAP(gettimeofday)(&as->now, NULL); 756 if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) { 757 as->state &= ~AUTH_ALLOW; 758 as->state |= AUTH_EXPIRED; 759 } 760 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire) 761 return (-1); 762 return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec); 763 } 764 return (0); 765 } 766 DEF_WEAK(auth_check_expire); 767 768 quad_t 769 auth_check_change(auth_session_t *as) 770 { 771 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 772 as->state &= ~AUTH_ALLOW; 773 as->state |= AUTH_PWEXPIRED; /* XXX */ 774 return (-1); 775 } 776 777 if (as->pwd == NULL) 778 return (0); 779 780 if (as->pwd && (quad_t)as->pwd->pw_change) { 781 if (as->now.tv_sec == 0) 782 WRAP(gettimeofday)(&as->now, NULL); 783 if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) { 784 as->state &= ~AUTH_ALLOW; 785 as->state |= AUTH_PWEXPIRED; 786 } 787 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change) 788 return (-1); 789 return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec); 790 } 791 return (0); 792 } 793 DEF_WEAK(auth_check_change); 794 795 /* 796 * The down and dirty call to the login script 797 * okay contains the default return value, typically 0 but 798 * is AUTH_OKAY for approval like scripts. 799 * 800 * Internally additional trailing arguments can be read from as->ap 801 * Options will be placed just after the first argument (not including path). 802 * 803 * Any data will be sent to (and freed by) the script 804 */ 805 int 806 auth_call(auth_session_t *as, char *path, ...) 807 { 808 char *line; 809 struct authdata *data; 810 struct authopts *opt; 811 pid_t pid; 812 int status; 813 int okay; 814 int pfd[2]; 815 int argc; 816 char *argv[64]; /* 64 args should be more than enough */ 817 #define Nargc (sizeof(argv)/sizeof(argv[0])) 818 819 va_start(as->ap0, path); 820 821 argc = 0; 822 if ((argv[argc] = _auth_next_arg(as)) != NULL) 823 ++argc; 824 825 if (as->fd != -1) { 826 argv[argc++] = "-v"; 827 argv[argc++] = "fd=4"; /* AUTH_FD, see below */ 828 } 829 /* XXX - fail if out of space in argv */ 830 for (opt = as->optlist; opt != NULL; opt = opt->next) { 831 if (argc < Nargc - 2) { 832 argv[argc++] = "-v"; 833 argv[argc++] = opt->opt; 834 } else { 835 syslog(LOG_ERR, "too many authentication options"); 836 goto fail; 837 } 838 } 839 while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as))) 840 ++argc; 841 842 if (argc >= Nargc - 1 && _auth_next_arg(as)) { 843 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 844 va_end(as->ap0); 845 explicit_bzero(&(as->ap0), sizeof(as->ap0)); 846 } 847 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 848 va_end(as->ap); 849 explicit_bzero(&(as->ap), sizeof(as->ap)); 850 } 851 syslog(LOG_ERR, "too many arguments"); 852 goto fail; 853 } 854 855 argv[argc] = NULL; 856 857 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) == -1) { 858 syslog(LOG_ERR, "unable to create backchannel %m"); 859 warnx("internal resource failure"); 860 goto fail; 861 } 862 863 switch (pid = fork()) { 864 case -1: 865 syslog(LOG_ERR, "%s: %m", path); 866 warnx("internal resource failure"); 867 close(pfd[0]); 868 close(pfd[1]); 869 goto fail; 870 case 0: 871 #define COMM_FD 3 872 #define AUTH_FD 4 873 if (dup2(pfd[1], COMM_FD) == -1) 874 err(1, "dup of backchannel"); 875 if (as->fd != -1) { 876 if (dup2(as->fd, AUTH_FD) == -1) 877 err(1, "dup of auth fd"); 878 closefrom(AUTH_FD + 1); 879 } else 880 closefrom(COMM_FD + 1); 881 execve(path, argv, auth_environ); 882 syslog(LOG_ERR, "%s: %m", path); 883 err(1, "%s", path); 884 default: 885 close(pfd[1]); 886 if (as->fd != -1) { 887 close(as->fd); /* so child has only ref */ 888 as->fd = -1; 889 } 890 while ((data = as->data) != NULL) { 891 as->data = data->next; 892 if (data->len > 0) { 893 write(pfd[0], data->ptr, data->len); 894 explicit_bzero(data->ptr, data->len); 895 } 896 free(data); 897 } 898 as->index = 0; 899 _auth_spool(as, pfd[0]); 900 close(pfd[0]); 901 do { 902 if (waitpid(pid, &status, 0) != -1) { 903 if (!WIFEXITED(status)) 904 goto fail; 905 break; 906 } 907 /* 908 * could get ECHILD if it was waited for by 909 * another thread or from a signal handler 910 */ 911 } while (errno == EINTR); 912 } 913 914 /* 915 * Now scan the spooled data 916 * It is easier to wait for all the data before starting 917 * to scan it. 918 */ 919 for (line = as->spool; line < as->spool + as->index;) { 920 if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) { 921 line += sizeof(BI_REJECT) - 1; 922 if (!*line || *line == ' ' || *line == '\t') { 923 while (*line == ' ' || *line == '\t') 924 ++line; 925 if (!strcasecmp(line, "silent")) { 926 as->state = AUTH_SILENT; 927 break; 928 } 929 if (!strcasecmp(line, "challenge")) { 930 as->state = AUTH_CHALLENGE; 931 break; 932 } 933 if (!strcasecmp(line, "expired")) { 934 as->state = AUTH_EXPIRED; 935 break; 936 } 937 if (!strcasecmp(line, "pwexpired")) { 938 as->state = AUTH_PWEXPIRED; 939 break; 940 } 941 } 942 break; 943 } else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) { 944 line += sizeof(BI_AUTH) - 1; 945 if (!*line || *line == ' ' || *line == '\t') { 946 while (*line == ' ' || *line == '\t') 947 ++line; 948 if (*line == '\0') 949 as->state |= AUTH_OKAY; 950 else if (!strcasecmp(line, "root")) 951 as->state |= AUTH_ROOTOKAY; 952 else if (!strcasecmp(line, "secure")) 953 as->state |= AUTH_SECURE; 954 } 955 } else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) { 956 line += sizeof(BI_REMOVE) - 1; 957 while (*line == ' ' || *line == '\t') 958 ++line; 959 if (*line) 960 _add_rmlist(as, line); 961 } 962 while (*line++) 963 ; 964 } 965 966 if (WEXITSTATUS(status)) 967 as->state &= ~AUTH_ALLOW; 968 969 okay = as->state & AUTH_ALLOW; 970 971 if (!okay) 972 auth_clrenv(as); 973 974 if (0) { 975 fail: 976 auth_clrenv(as); 977 as->state = 0; 978 okay = -1; 979 } 980 981 while ((data = as->data) != NULL) { 982 as->data = data->next; 983 free(data); 984 } 985 986 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 987 va_end(as->ap0); 988 explicit_bzero(&(as->ap0), sizeof(as->ap0)); 989 } 990 991 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 992 va_end(as->ap); 993 explicit_bzero(&(as->ap), sizeof(as->ap)); 994 } 995 return (okay); 996 } 997 DEF_WEAK(auth_call); 998 999 static void 1000 _recv_fd(auth_session_t *as, int fd) 1001 { 1002 struct msghdr msg; 1003 struct cmsghdr *cmp; 1004 union { 1005 struct cmsghdr hdr; 1006 char buf[CMSG_SPACE(sizeof(int))]; 1007 } cmsgbuf; 1008 1009 memset(&msg, 0, sizeof(msg)); 1010 msg.msg_control = &cmsgbuf.buf; 1011 msg.msg_controllen = sizeof(cmsgbuf.buf); 1012 if (recvmsg(fd, &msg, 0) == -1) 1013 syslog(LOG_ERR, "recvmsg: %m"); 1014 else if (msg.msg_flags & MSG_TRUNC) 1015 syslog(LOG_ERR, "message truncated"); 1016 else if (msg.msg_flags & MSG_CTRUNC) 1017 syslog(LOG_ERR, "control message truncated"); 1018 else if ((cmp = CMSG_FIRSTHDR(&msg)) == NULL) 1019 syslog(LOG_ERR, "missing control message"); 1020 else { 1021 if (cmp->cmsg_level != SOL_SOCKET) 1022 syslog(LOG_ERR, "unexpected cmsg_level %d", 1023 cmp->cmsg_level); 1024 else if (cmp->cmsg_type != SCM_RIGHTS) 1025 syslog(LOG_ERR, "unexpected cmsg_type %d", 1026 cmp->cmsg_type); 1027 else if (cmp->cmsg_len != CMSG_LEN(sizeof(int))) 1028 syslog(LOG_ERR, "bad cmsg_len %d", 1029 cmp->cmsg_len); 1030 else { 1031 if (as->fd != -1) 1032 close(as->fd); 1033 as->fd = *(int *)CMSG_DATA(cmp); 1034 } 1035 } 1036 } 1037 1038 static void 1039 _auth_spool(auth_session_t *as, int fd) 1040 { 1041 ssize_t r; 1042 char *b, *s; 1043 1044 for (s = as->spool + as->index; as->index < sizeof(as->spool) - 1; ) { 1045 r = read(fd, as->spool + as->index, 1046 sizeof(as->spool) - as->index); 1047 if (r <= 0) { 1048 as->spool[as->index] = '\0'; 1049 return; 1050 } 1051 b = as->spool + as->index; 1052 as->index += r; 1053 /* 1054 * Convert newlines into NULs to allow easy scanning of the 1055 * file and receive an fd if there is a BI_FDPASS message. 1056 * XXX - checking for BI_FDPASS here is annoying but 1057 * we need to avoid the read() slurping in control data. 1058 */ 1059 while (r-- > 0) { 1060 if (*b++ == '\n') { 1061 b[-1] = '\0'; 1062 if (strcasecmp(s, BI_FDPASS) == 0) 1063 _recv_fd(as, fd); 1064 s = b; 1065 } 1066 } 1067 } 1068 1069 syslog(LOG_ERR, "Overflowed backchannel spool buffer"); 1070 errx(1, "System error in authentication program"); 1071 } 1072 1073 static void 1074 _add_rmlist(auth_session_t *as, char *file) 1075 { 1076 struct rmfiles *rm; 1077 size_t i = strlen(file) + 1; 1078 1079 // XXX should rangecheck i since we are about to add? 1080 1081 if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL) { 1082 syslog(LOG_ERR, "Failed to allocate rmfiles: %m"); 1083 return; 1084 } 1085 rm->file = (char *)(rm + 1); 1086 rm->next = as->rmlist; 1087 strlcpy(rm->file, file, i); 1088 as->rmlist = rm; 1089 } 1090 1091 static char * 1092 _auth_next_arg(auth_session_t *as) 1093 { 1094 char *arg; 1095 1096 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 1097 if ((arg = va_arg(as->ap0, char *)) != NULL) 1098 return (arg); 1099 va_end(as->ap0); 1100 explicit_bzero(&(as->ap0), sizeof(as->ap0)); 1101 } 1102 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 1103 if ((arg = va_arg(as->ap, char *)) != NULL) 1104 return (arg); 1105 va_end(as->ap); 1106 explicit_bzero(&(as->ap), sizeof(as->ap)); 1107 } 1108 return (NULL); 1109 } 1110