1 /* $OpenBSD: auth_subr.c,v 1.6 2001/07/02 19:22:34 millert 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() 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 authdata *data; 161 162 as->state = 0; 163 164 auth_clrenv(as); 165 166 /* 167 * Clean out the rmlist and remove specified files 168 */ 169 while ((rm = as->rmlist) != NULL) { 170 as->rmlist = rm->next; 171 unlink(rm->file); 172 free(rm); 173 } 174 175 /* 176 * Clean out data 177 */ 178 while ((data = as->data) != NULL) { 179 if (as->data->len) 180 memset(as->data->ptr, 0, as->data->len); 181 as->data = data->next; 182 free(data); 183 } 184 185 auth_setitem(as, AUTHV_ALL, NULL); 186 187 if (as->pwd != NULL) { 188 memset(as->pwd->pw_passwd, 0, strlen(as->pwd->pw_passwd)); 189 free(as->pwd); 190 as->pwd = NULL; 191 } 192 } 193 194 /* 195 * Close the specified BSD Authentication session. 196 * Return 0 if not authenticated. 197 */ 198 int 199 auth_close(auth_session_t *as) 200 { 201 struct rmfiles *rm; 202 struct authopts *opt; 203 struct authdata *data; 204 int s; 205 206 /* 207 * Save our return value 208 */ 209 s = as->state & AUTH_ALLOW; 210 211 if (s == 0) 212 as->index = 0; 213 214 auth_setenv(as); 215 216 217 /* 218 * Clean out the rmlist and remove specified files if the 219 * authentication failed 220 */ 221 while ((rm = as->rmlist) != NULL) { 222 as->rmlist = rm->next; 223 if (s == 0) 224 unlink(rm->file); 225 free(rm); 226 } 227 228 /* 229 * Clean out the opt list 230 */ 231 while ((opt = as->optlist) != NULL) { 232 as->optlist = opt->next; 233 free(opt); 234 } 235 236 /* 237 * Clean out data 238 */ 239 while ((data = as->data) != NULL) { 240 if (as->data->len) 241 memset(as->data->ptr, 0, as->data->len); 242 as->data = data->next; 243 free(data); 244 } 245 246 /* 247 * Clean up random variables 248 */ 249 if (as->service && as->service != defservice) 250 free(as->service); 251 if (as->challenge) 252 free(as->challenge); 253 if (as->class) 254 free(as->class); 255 if (as->style) 256 free(as->style); 257 if (as->name) 258 free(as->name); 259 260 free(as); 261 return (s); 262 } 263 264 /* 265 * Request a challange for the session. 266 * The name and style must have already been specified 267 */ 268 char * 269 auth_challenge(auth_session_t *as) 270 { 271 char path[MAXPATHLEN]; 272 273 as->state = 0; 274 275 if (as == NULL || as->style == NULL || as->name == NULL) 276 return (NULL); 277 278 if (as->challenge) { 279 free(as->challenge); 280 as->challenge = NULL; 281 } 282 283 snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); 284 auth_call(as, path, as->style, "-s", "challenge", as->name, 285 as->class, NULL); 286 if (as->state & AUTH_CHALLENGE) 287 as->challenge = auth_getvalue(as, "challenge"); 288 as->state = 0; 289 as->index = 0; /* toss our data */ 290 return (as->challenge); 291 } 292 293 /* 294 * Set/unset the requested environment variables. 295 * Mark the variables as set so they will not be set a second time. 296 * XXX - should provide a way to detect setenv() failure. 297 */ 298 void 299 auth_setenv(auth_session_t *as) 300 { 301 char *line, *name; 302 303 /* 304 * Set any environment variables we were asked for 305 */ 306 for (line = as->spool; line < as->spool + as->index;) { 307 if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { 308 if (isblank(line[sizeof(BI_SETENV) - 1])) { 309 /* only do it once! */ 310 line[0] = 'd'; line[1] = 'i'; line[2] = 'd'; 311 line += sizeof(BI_SETENV) - 1; 312 for (name = line; isblank(*name); ++name) 313 ; 314 for (line = name; *line && !isblank(*line); 315 ++line) 316 ; 317 if (*line) 318 *line++ = '\0'; 319 for (; isblank(*line); ++line) 320 ; 321 if (*line != '\0' && setenv(name, line, 1)) 322 _warn("setenv(%s, %s)", name, line); 323 } 324 } else 325 if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { 326 if (isblank(line[sizeof(BI_UNSETENV) - 1])) { 327 /* only do it once! */ 328 line[2] = 'd'; line[3] = 'i'; line[4] = 'd'; 329 line += sizeof(BI_UNSETENV) - 1; 330 for (name = line; isblank(*name); ++name) 331 ; 332 for (line = name; *line && !isblank(*line); 333 ++line) 334 ; 335 if (*line) 336 *line++ = '\0'; 337 unsetenv(name); 338 } 339 } 340 while (*line++) 341 ; 342 } 343 } 344 345 /* 346 * Clear out any requested environment variables. 347 */ 348 void 349 auth_clrenv(auth_session_t *as) 350 { 351 char *line; 352 353 for (line = as->spool; line < as->spool + as->index;) { 354 if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { 355 if (isblank(line[sizeof(BI_SETENV) - 1])) { 356 line[0] = 'i'; line[1] = 'g'; line[2] = 'n'; 357 } 358 } else 359 if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { 360 if (isblank(line[sizeof(BI_UNSETENV) - 1])) { 361 line[2] = 'i'; line[3] = 'g'; line[4] = 'n'; 362 } 363 } 364 while (*line++) 365 ; 366 } 367 } 368 369 char * 370 auth_getitem(auth_session_t *as, auth_item_t item) 371 { 372 if (as != NULL) { 373 switch (item) { 374 case AUTHV_CHALLENGE: 375 return (as->challenge); 376 case AUTHV_CLASS: 377 return (as->class); 378 case AUTHV_NAME: 379 return (as->name); 380 case AUTHV_SERVICE: 381 return (as->service ? as->service : defservice); 382 case AUTHV_STYLE: 383 return (as->style); 384 case AUTHV_INTERACTIVE: 385 return ((as->flags & AF_INTERACTIVE) ? "True" : NULL); 386 default: 387 break; 388 } 389 } 390 return (NULL); 391 } 392 393 int 394 auth_setitem(auth_session_t *as, auth_item_t item, char *value) 395 { 396 if (as == NULL) { 397 errno = EINVAL; 398 return (-1); 399 } 400 401 switch (item) { 402 case AUTHV_ALL: 403 if (value != NULL) { 404 errno = EINVAL; 405 return (-1); 406 } 407 auth_setitem(as, AUTHV_CHALLENGE, NULL); 408 auth_setitem(as, AUTHV_CLASS, NULL); 409 auth_setitem(as, AUTHV_NAME, NULL); 410 auth_setitem(as, AUTHV_SERVICE, NULL); 411 auth_setitem(as, AUTHV_STYLE, NULL); 412 auth_setitem(as, AUTHV_INTERACTIVE, NULL); 413 return (0); 414 415 case AUTHV_CHALLENGE: 416 if (value != NULL && (value = strdup(value)) == NULL) 417 return (-1); 418 if (as->challenge) 419 free(as->challenge); 420 as->challenge = value; 421 return (0); 422 423 case AUTHV_CLASS: 424 if (value != NULL && (value = strdup(value)) == NULL) 425 return (-1); 426 427 if (as->class) 428 free(as->class); 429 430 as->class = value; 431 return (0); 432 433 case AUTHV_NAME: 434 if (value != NULL && (value = strdup(value)) == NULL) 435 return (-1); 436 437 if (as->name) 438 free(as->name); 439 440 as->name = value; 441 return (0); 442 443 case AUTHV_SERVICE: 444 if (value == NULL || strcmp(value, defservice) == 0) 445 value = defservice; 446 else if ((value = strdup(value)) == NULL) 447 return (-1); 448 449 if (as->service && as->service != defservice) 450 free(as->service); 451 452 as->service = value; 453 return (0); 454 455 case AUTHV_STYLE: 456 if (value == NULL || strchr(value, '/') != NULL || 457 (value = strdup(value)) == NULL) 458 return (-1); 459 460 if (as->style) 461 free(as->style); 462 463 as->style = value; 464 return (0); 465 466 case AUTHV_INTERACTIVE: 467 if (value == NULL) 468 as->flags &= ~AF_INTERACTIVE; 469 else 470 as->flags |= ~AF_INTERACTIVE; 471 return (0); 472 473 default: 474 errno = EINVAL; 475 return (-1); 476 } 477 } 478 479 int 480 auth_setoption(auth_session_t *as, char *n, char *v) 481 { 482 struct authopts *opt; 483 int i = strlen(n) + strlen(v) + 2; 484 485 if ((opt = malloc(sizeof(*opt) + i)) == NULL) 486 return (-1); 487 488 opt->opt = (char *)(opt + 1); 489 490 sprintf(opt->opt, "%s=%s", n, v); 491 opt->next = as->optlist; 492 as->optlist = opt; 493 return(0); 494 } 495 496 void 497 auth_clroptions(auth_session_t *as) 498 { 499 struct authopts *opt; 500 501 while ((opt = as->optlist) != NULL) { 502 as->optlist = opt->next; 503 free(opt); 504 } 505 } 506 507 void 508 auth_clroption(auth_session_t *as, char *option) 509 { 510 struct authopts *opt, *oopt; 511 int len; 512 513 len = strlen(option); 514 515 if ((opt = as->optlist) == NULL) 516 return; 517 518 if (strncmp(opt->opt, option, len) == 0 && 519 (opt->opt[len] == '=' || opt->opt[len] == '\0')) { 520 as->optlist = opt->next; 521 free(opt); 522 return; 523 } 524 525 while ((oopt = opt->next) != NULL) { 526 if (strncmp(oopt->opt, option, len) == 0 && 527 (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) { 528 opt->next = oopt->next; 529 free(oopt); 530 return; 531 } 532 opt = oopt; 533 } 534 } 535 536 int 537 auth_setdata(auth_session_t *as, void *ptr, size_t len) 538 { 539 struct authdata *data, *dp; 540 541 if (len <= 0) 542 return (0); 543 544 if ((data = malloc(sizeof(*data) + len)) == NULL) 545 return (-1); 546 547 data->next = NULL; 548 data->len = len; 549 data->ptr = data + 1; 550 memcpy(data->ptr, ptr, len); 551 552 if (as->data == NULL) 553 as->data = data; 554 else { 555 for (dp = as->data; dp->next != NULL; dp = dp->next) 556 ; 557 dp->next = data; 558 } 559 return (0); 560 } 561 562 int 563 auth_setpwd(auth_session_t *as, struct passwd *pwd) 564 { 565 char *instance; 566 567 if (pwd == NULL && as->pwd == NULL && as->name == NULL) 568 return (-1); /* true failure */ 569 570 if (pwd == NULL) { 571 /* 572 * If we were not passed in a pwd structure we need to 573 * go find one for ourself. Always look up the username 574 * (if it is defined) in the passwd database to see if there 575 * is an entry for the user. If not, either use the current 576 * entry or simply return a 1 which implies there is 577 * no user by that name here. This is not a failure, just 578 * a point of information. 579 */ 580 if (as->name == NULL) 581 return (0); 582 if ((pwd = getpwnam(as->name)) == NULL) { 583 instance = strpbrk(as->name, "./"); 584 if (instance++ == NULL) 585 return (as->pwd ? 0 : 1); 586 if (strcmp(instance, "root") == 0) 587 pwd = getpwnam(instance); 588 if (pwd == NULL) 589 return (as->pwd ? 0 : 1); 590 } 591 } 592 if ((pwd = pw_dup(pwd)) == NULL) 593 return (-1); /* true failure */ 594 if (as->pwd) { 595 memset(as->pwd->pw_passwd, 0, strlen(as->pwd->pw_passwd)); 596 free(as->pwd); 597 } 598 as->pwd = pwd; 599 return (0); 600 } 601 602 char * 603 auth_getvalue(auth_session_t *as, char *what) 604 { 605 char *line, *v, *value; 606 int n, len; 607 608 len = strlen(what); 609 610 for (line = as->spool; line < as->spool + as->index;) { 611 if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0) 612 goto next; 613 line += sizeof(BI_VALUE) - 1; 614 615 if (!isblank(*line)) 616 goto next; 617 618 while (isblank(*++line)) 619 ; 620 621 if (strncmp(line, what, len) != 0 || 622 !isblank(line[len])) 623 goto next; 624 line += len; 625 while (isblank(*++line)) 626 ; 627 value = strdup(line); 628 if (value == NULL) 629 return (NULL); 630 631 /* 632 * XXX - There should be a more standardized 633 * routine for doing this sort of thing. 634 */ 635 for (line = v = value; *line; ++line) { 636 if (*line == '\\') { 637 switch (*++line) { 638 case 'r': 639 *v++ = '\r'; 640 break; 641 case 'n': 642 *v++ = '\n'; 643 break; 644 case 't': 645 *v++ = '\t'; 646 break; 647 case '0': case '1': case '2': 648 case '3': case '4': case '5': 649 case '6': case '7': 650 n = *line - '0'; 651 if (isdigit(line[1])) { 652 ++line; 653 n <<= 3; 654 n |= *line-'0'; 655 } 656 if (isdigit(line[1])) { 657 ++line; 658 n <<= 3; 659 n |= *line-'0'; 660 } 661 break; 662 default: 663 *v++ = *line; 664 break; 665 } 666 } else 667 *v++ = *line; 668 } 669 *v = '\0'; 670 return (value); 671 next: 672 while (*line++) 673 ; 674 } 675 return (NULL); 676 } 677 678 quad_t 679 auth_check_expire(auth_session_t *as) 680 { 681 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 682 as->state &= ~AUTH_ALLOW; 683 as->state |= AUTH_EXPIRED; /* XXX */ 684 return (-1); 685 } 686 687 if (as->pwd == NULL) 688 return (0); 689 690 if (as->pwd && (quad_t)as->pwd->pw_expire != 0) { 691 if (as->now.tv_sec == 0) 692 gettimeofday(&as->now, (struct timezone *)NULL); 693 if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) { 694 as->state &= ~AUTH_ALLOW; 695 as->state |= AUTH_EXPIRED; 696 } 697 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire) 698 return (-1); 699 return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec); 700 } 701 return (0); 702 } 703 704 quad_t 705 auth_check_change(auth_session_t *as) 706 { 707 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 708 as->state &= ~AUTH_ALLOW; 709 as->state |= AUTH_PWEXPIRED; /* XXX */ 710 return (-1); 711 } 712 713 if (as->pwd == NULL) 714 return (0); 715 716 if (as->pwd && (quad_t)as->pwd->pw_change) { 717 if (as->now.tv_sec == 0) 718 gettimeofday(&as->now, (struct timezone *)NULL); 719 if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) { 720 as->state &= ~AUTH_ALLOW; 721 as->state |= AUTH_PWEXPIRED; 722 } 723 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change) 724 return (-1); 725 return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec); 726 } 727 return (0); 728 } 729 730 /* 731 * The down and dirty call to the login script 732 * okay contains the default return value, typically 0 but 733 * is AUTH_OKAY for approval like scripts. 734 * 735 * Internally additional trailing arguments can be read from as->ap 736 * Options will be placed be placed just after the first argument 737 * (not including path). 738 * 739 * Any data will sent (and freed) to the script 740 */ 741 int 742 auth_call(auth_session_t *as, char *path, ...) 743 { 744 char *line; 745 struct authdata *data; 746 struct authopts *opt; 747 pid_t pid; 748 int status; 749 int okay; 750 int pfd[2]; 751 int argc; 752 char *argv[64]; /* 64 args should more than enough */ 753 #define Nargc (sizeof(argv)/sizeof(argv[0])) 754 755 va_start(as->ap0, path); 756 757 argc = 0; 758 if ((argv[argc] = _auth_next_arg(as)) != NULL) 759 ++argc; 760 761 for (opt = as->optlist; opt != NULL; opt = opt->next) { 762 if (argc < Nargc - 2) { 763 argv[argc++] = "-v"; 764 argv[argc++] = opt->opt; 765 } else { 766 syslog(LOG_ERR, "too many authentication options"); 767 goto fail; 768 } 769 } 770 while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as))) 771 ++argc; 772 773 if (argc >= Nargc - 1 && _auth_next_arg(as)) { 774 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 775 va_end(as->ap0); 776 memset(&(as->ap0), 0, sizeof(as->ap0)); 777 } 778 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 779 va_end(as->ap); 780 memset(&(as->ap), 0, sizeof(as->ap)); 781 } 782 syslog(LOG_ERR, "too many arguments"); 783 goto fail; 784 } 785 786 argv[argc] = NULL; 787 788 if (secure_path(path) < 0) { 789 syslog(LOG_ERR, "%s: path not secure", path); 790 _warnx("invalid script: %s", path); 791 goto fail; 792 } 793 794 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) < 0) { 795 syslog(LOG_ERR, "unable to create backchannel %m"); 796 _warnx("internal resource failure"); 797 goto fail; 798 } 799 800 switch (pid = fork()) { 801 case -1: 802 close(pfd[0]); 803 close(pfd[1]); 804 syslog(LOG_ERR, "%s: %m", path); 805 _warnx("internal resource failure"); 806 goto fail; 807 case 0: 808 #define COMM_FD 3 809 close(pfd[0]); 810 if (pfd[1] != COMM_FD) { 811 if (dup2(pfd[1], COMM_FD) < 0) 812 err(1, "dup of backchannel"); 813 close(pfd[1]); 814 } 815 816 for (status = getdtablesize() - 1; status > COMM_FD; status--) 817 close(status); 818 819 execve(path, argv, auth_environ); 820 syslog(LOG_ERR, "%s: %m", path); 821 err(1, "%s", path); 822 default: 823 close(pfd[1]); 824 while ((data = as->data) != NULL) { 825 as->data = data->next; 826 if (data->len > 0) { 827 write(pfd[0], data->ptr, data->len); 828 memset(data->ptr, 0, data->len); 829 } 830 free(data); 831 } 832 as->index = 0; 833 _auth_spool(as, pfd[0]); 834 close(pfd[0]); 835 if (waitpid(pid, &status, 0) < 0) { 836 syslog(LOG_ERR, "%s: waitpid: %m", path); 837 _warnx("internal failure"); 838 goto fail; 839 } 840 841 if (!WIFEXITED(status)) 842 goto fail; 843 } 844 845 /* 846 * Now scan the spooled data 847 * It is easier to wait for all the data before starting 848 * to scan it. 849 */ 850 for (line = as->spool; line < as->spool + as->index;) { 851 if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) { 852 line += sizeof(BI_REJECT) - 1; 853 if (!*line || *line == ' ' || *line == '\t') { 854 while (*line == ' ' || *line == '\t') 855 ++line; 856 if (!strcasecmp(line, "silent")) { 857 as->state = AUTH_SILENT; 858 break; 859 } 860 if (!strcasecmp(line, "challenge")) { 861 as->state = AUTH_CHALLENGE; 862 break; 863 } 864 if (!strcasecmp(line, "expired")) { 865 as->state = AUTH_EXPIRED; 866 break; 867 } 868 if (!strcasecmp(line, "pwexpired")) { 869 as->state = AUTH_PWEXPIRED; 870 break; 871 } 872 } 873 break; 874 } else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) { 875 line += sizeof(BI_AUTH) - 1; 876 if (!*line || *line == ' ' || *line == '\t') { 877 while (*line == ' ' || *line == '\t') 878 ++line; 879 if (*line == '\0') 880 as->state |= AUTH_OKAY; 881 else if (!strcasecmp(line, "root")) 882 as->state |= AUTH_ROOTOKAY; 883 else if (!strcasecmp(line, "secure")) 884 as->state |= AUTH_SECURE; 885 } 886 } else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) { 887 line += sizeof(BI_REMOVE) - 1; 888 while (*line == ' ' || *line == '\t') 889 ++line; 890 if (*line) 891 _add_rmlist(as, line); 892 } 893 while (*line++) 894 ; 895 } 896 897 if (WEXITSTATUS(status)) 898 as->state &= ~AUTH_ALLOW; 899 900 okay = as->state & AUTH_ALLOW; 901 902 if (!okay) 903 auth_clrenv(as); 904 905 if (0) { 906 fail: 907 auth_clrenv(as); 908 as->state = 0; 909 okay = -1; 910 } 911 912 while ((data = as->data) != NULL) { 913 as->data = data->next; 914 free(data); 915 } 916 917 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 918 va_end(as->ap0); 919 memset(&(as->ap0), 0, sizeof(as->ap0)); 920 } 921 922 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 923 va_end(as->ap); 924 memset(&(as->ap), 0, sizeof(as->ap)); 925 } 926 return (okay); 927 } 928 929 static void 930 _auth_spool(auth_session_t *as, int fd) 931 { 932 int r; 933 char *b; 934 935 while (as->index < sizeof(as->spool) - 1) { 936 r = read(fd, as->spool + as->index, 937 sizeof(as->spool) - as->index); 938 if (r <= 0) { 939 as->spool[as->index] = '\0'; 940 return; 941 } 942 b = as->spool + as->index; 943 as->index += r; 944 /* 945 * Go ahead and convert newlines into NULs to allow 946 * easy scanning of the file. 947 */ 948 while(r-- > 0) 949 if (*b++ == '\n') 950 b[-1] = '\0'; 951 } 952 953 syslog(LOG_ERR, "Overflowed backchannel spool buffer"); 954 errx(1, "System error in authentication program"); 955 } 956 957 static void 958 _add_rmlist(auth_session_t *as, char *file) 959 { 960 struct rmfiles *rm; 961 int i = strlen(file) + 1; 962 963 if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL) { 964 syslog(LOG_ERR, "Failed to allocate rmfiles: %m"); 965 return; 966 } 967 rm->file = (char *)(rm + 1); 968 rm->next = as->rmlist; 969 strcpy(rm->file, file); 970 as->rmlist = rm; 971 } 972 973 static char * 974 _auth_next_arg(auth_session_t *as) 975 { 976 char *arg; 977 978 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 979 if ((arg = va_arg(as->ap0, char *)) != NULL) 980 return (arg); 981 va_end(as->ap0); 982 memset(&(as->ap0), 0, sizeof(as->ap0)); 983 } 984 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 985 if ((arg = va_arg(as->ap, char *)) != NULL) 986 return (arg); 987 va_end(as->ap); 988 memset(&(as->ap), 0, sizeof(as->ap)); 989 } 990 return (NULL); 991 } 992