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