1 /* $OpenBSD: auth_subr.c,v 1.8 2002/02/05 07:51:52 mpech 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 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 challange 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 as->state = 0; 289 290 if (as == NULL || as->style == NULL || as->name == NULL) 291 return (NULL); 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, 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 != NULL && (value = strdup(value)) == NULL) 432 return (-1); 433 if (as->challenge) 434 free(as->challenge); 435 as->challenge = value; 436 return (0); 437 438 case AUTHV_CLASS: 439 if (value != NULL && (value = strdup(value)) == NULL) 440 return (-1); 441 442 if (as->class) 443 free(as->class); 444 445 as->class = value; 446 return (0); 447 448 case AUTHV_NAME: 449 if (value != NULL && (value = strdup(value)) == NULL) 450 return (-1); 451 452 if (as->name) 453 free(as->name); 454 455 as->name = value; 456 return (0); 457 458 case AUTHV_SERVICE: 459 if (value == NULL || strcmp(value, defservice) == 0) 460 value = defservice; 461 else if ((value = strdup(value)) == NULL) 462 return (-1); 463 464 if (as->service && as->service != defservice) 465 free(as->service); 466 467 as->service = value; 468 return (0); 469 470 case AUTHV_STYLE: 471 if (value == NULL || strchr(value, '/') != NULL || 472 (value = strdup(value)) == NULL) 473 return (-1); 474 475 if (as->style) 476 free(as->style); 477 478 as->style = value; 479 return (0); 480 481 case AUTHV_INTERACTIVE: 482 if (value == NULL) 483 as->flags &= ~AF_INTERACTIVE; 484 else 485 as->flags |= ~AF_INTERACTIVE; 486 return (0); 487 488 default: 489 errno = EINVAL; 490 return (-1); 491 } 492 } 493 494 int 495 auth_setoption(auth_session_t *as, char *n, char *v) 496 { 497 struct authopts *opt; 498 int i = strlen(n) + strlen(v) + 2; 499 500 if ((opt = malloc(sizeof(*opt) + i)) == NULL) 501 return (-1); 502 503 opt->opt = (char *)(opt + 1); 504 505 sprintf(opt->opt, "%s=%s", n, v); 506 opt->next = as->optlist; 507 as->optlist = opt; 508 return(0); 509 } 510 511 void 512 auth_clroptions(auth_session_t *as) 513 { 514 struct authopts *opt; 515 516 while ((opt = as->optlist) != NULL) { 517 as->optlist = opt->next; 518 free(opt); 519 } 520 } 521 522 void 523 auth_clroption(auth_session_t *as, char *option) 524 { 525 struct authopts *opt, *oopt; 526 int len; 527 528 len = strlen(option); 529 530 if ((opt = as->optlist) == NULL) 531 return; 532 533 if (strncmp(opt->opt, option, len) == 0 && 534 (opt->opt[len] == '=' || opt->opt[len] == '\0')) { 535 as->optlist = opt->next; 536 free(opt); 537 return; 538 } 539 540 while ((oopt = opt->next) != NULL) { 541 if (strncmp(oopt->opt, option, len) == 0 && 542 (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) { 543 opt->next = oopt->next; 544 free(oopt); 545 return; 546 } 547 opt = oopt; 548 } 549 } 550 551 int 552 auth_setdata(auth_session_t *as, void *ptr, size_t len) 553 { 554 struct authdata *data, *dp; 555 556 if (len <= 0) 557 return (0); 558 559 if ((data = malloc(sizeof(*data) + len)) == NULL) 560 return (-1); 561 562 data->next = NULL; 563 data->len = len; 564 data->ptr = data + 1; 565 memcpy(data->ptr, ptr, len); 566 567 if (as->data == NULL) 568 as->data = data; 569 else { 570 for (dp = as->data; dp->next != NULL; dp = dp->next) 571 ; 572 dp->next = data; 573 } 574 return (0); 575 } 576 577 int 578 auth_setpwd(auth_session_t *as, struct passwd *pwd) 579 { 580 char *instance; 581 582 if (pwd == NULL && as->pwd == NULL && as->name == NULL) 583 return (-1); /* true failure */ 584 585 if (pwd == NULL) { 586 /* 587 * If we were not passed in a pwd structure we need to 588 * go find one for ourself. Always look up the username 589 * (if it is defined) in the passwd database to see if there 590 * is an entry for the user. If not, either use the current 591 * entry or simply return a 1 which implies there is 592 * no user by that name here. This is not a failure, just 593 * a point of information. 594 */ 595 if (as->name == NULL) 596 return (0); 597 if ((pwd = getpwnam(as->name)) == NULL) { 598 instance = strpbrk(as->name, "./"); 599 if (instance++ == NULL) 600 return (as->pwd ? 0 : 1); 601 if (strcmp(instance, "root") == 0) 602 pwd = getpwnam(instance); 603 if (pwd == NULL) 604 return (as->pwd ? 0 : 1); 605 } 606 } 607 if ((pwd = pw_dup(pwd)) == NULL) 608 return (-1); /* true failure */ 609 if (as->pwd) { 610 memset(as->pwd->pw_passwd, 0, strlen(as->pwd->pw_passwd)); 611 free(as->pwd); 612 } 613 as->pwd = pwd; 614 return (0); 615 } 616 617 char * 618 auth_getvalue(auth_session_t *as, char *what) 619 { 620 char *line, *v, *value; 621 int n, len; 622 623 len = strlen(what); 624 625 for (line = as->spool; line < as->spool + as->index;) { 626 if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0) 627 goto next; 628 line += sizeof(BI_VALUE) - 1; 629 630 if (!isblank(*line)) 631 goto next; 632 633 while (isblank(*++line)) 634 ; 635 636 if (strncmp(line, what, len) != 0 || 637 !isblank(line[len])) 638 goto next; 639 line += len; 640 while (isblank(*++line)) 641 ; 642 value = strdup(line); 643 if (value == NULL) 644 return (NULL); 645 646 /* 647 * XXX - There should be a more standardized 648 * routine for doing this sort of thing. 649 */ 650 for (line = v = value; *line; ++line) { 651 if (*line == '\\') { 652 switch (*++line) { 653 case 'r': 654 *v++ = '\r'; 655 break; 656 case 'n': 657 *v++ = '\n'; 658 break; 659 case 't': 660 *v++ = '\t'; 661 break; 662 case '0': case '1': case '2': 663 case '3': case '4': case '5': 664 case '6': case '7': 665 n = *line - '0'; 666 if (isdigit(line[1])) { 667 ++line; 668 n <<= 3; 669 n |= *line-'0'; 670 } 671 if (isdigit(line[1])) { 672 ++line; 673 n <<= 3; 674 n |= *line-'0'; 675 } 676 break; 677 default: 678 *v++ = *line; 679 break; 680 } 681 } else 682 *v++ = *line; 683 } 684 *v = '\0'; 685 return (value); 686 next: 687 while (*line++) 688 ; 689 } 690 return (NULL); 691 } 692 693 quad_t 694 auth_check_expire(auth_session_t *as) 695 { 696 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 697 as->state &= ~AUTH_ALLOW; 698 as->state |= AUTH_EXPIRED; /* XXX */ 699 return (-1); 700 } 701 702 if (as->pwd == NULL) 703 return (0); 704 705 if (as->pwd && (quad_t)as->pwd->pw_expire != 0) { 706 if (as->now.tv_sec == 0) 707 gettimeofday(&as->now, (struct timezone *)NULL); 708 if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) { 709 as->state &= ~AUTH_ALLOW; 710 as->state |= AUTH_EXPIRED; 711 } 712 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire) 713 return (-1); 714 return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec); 715 } 716 return (0); 717 } 718 719 quad_t 720 auth_check_change(auth_session_t *as) 721 { 722 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 723 as->state &= ~AUTH_ALLOW; 724 as->state |= AUTH_PWEXPIRED; /* XXX */ 725 return (-1); 726 } 727 728 if (as->pwd == NULL) 729 return (0); 730 731 if (as->pwd && (quad_t)as->pwd->pw_change) { 732 if (as->now.tv_sec == 0) 733 gettimeofday(&as->now, (struct timezone *)NULL); 734 if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) { 735 as->state &= ~AUTH_ALLOW; 736 as->state |= AUTH_PWEXPIRED; 737 } 738 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change) 739 return (-1); 740 return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec); 741 } 742 return (0); 743 } 744 745 /* 746 * The down and dirty call to the login script 747 * okay contains the default return value, typically 0 but 748 * is AUTH_OKAY for approval like scripts. 749 * 750 * Internally additional trailing arguments can be read from as->ap 751 * Options will be placed be placed just after the first argument 752 * (not including path). 753 * 754 * Any data will sent (and freed) to the script 755 */ 756 int 757 auth_call(auth_session_t *as, char *path, ...) 758 { 759 char *line; 760 struct authdata *data; 761 struct authopts *opt; 762 pid_t pid; 763 int status; 764 int okay; 765 int pfd[2]; 766 int argc; 767 char *argv[64]; /* 64 args should more than enough */ 768 #define Nargc (sizeof(argv)/sizeof(argv[0])) 769 770 va_start(as->ap0, path); 771 772 argc = 0; 773 if ((argv[argc] = _auth_next_arg(as)) != NULL) 774 ++argc; 775 776 for (opt = as->optlist; opt != NULL; opt = opt->next) { 777 if (argc < Nargc - 2) { 778 argv[argc++] = "-v"; 779 argv[argc++] = opt->opt; 780 } else { 781 syslog(LOG_ERR, "too many authentication options"); 782 goto fail; 783 } 784 } 785 while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as))) 786 ++argc; 787 788 if (argc >= Nargc - 1 && _auth_next_arg(as)) { 789 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 790 va_end(as->ap0); 791 memset(&(as->ap0), 0, sizeof(as->ap0)); 792 } 793 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 794 va_end(as->ap); 795 memset(&(as->ap), 0, sizeof(as->ap)); 796 } 797 syslog(LOG_ERR, "too many arguments"); 798 goto fail; 799 } 800 801 argv[argc] = NULL; 802 803 if (secure_path(path) < 0) { 804 syslog(LOG_ERR, "%s: path not secure", path); 805 _warnx("invalid script: %s", path); 806 goto fail; 807 } 808 809 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) < 0) { 810 syslog(LOG_ERR, "unable to create backchannel %m"); 811 _warnx("internal resource failure"); 812 goto fail; 813 } 814 815 switch (pid = fork()) { 816 case -1: 817 close(pfd[0]); 818 close(pfd[1]); 819 syslog(LOG_ERR, "%s: %m", path); 820 _warnx("internal resource failure"); 821 goto fail; 822 case 0: 823 #define COMM_FD 3 824 close(pfd[0]); 825 if (pfd[1] != COMM_FD) { 826 if (dup2(pfd[1], COMM_FD) < 0) 827 err(1, "dup of backchannel"); 828 close(pfd[1]); 829 } 830 831 for (status = getdtablesize() - 1; status > COMM_FD; status--) 832 close(status); 833 834 execve(path, argv, auth_environ); 835 syslog(LOG_ERR, "%s: %m", path); 836 err(1, "%s", path); 837 default: 838 close(pfd[1]); 839 while ((data = as->data) != NULL) { 840 as->data = data->next; 841 if (data->len > 0) { 842 write(pfd[0], data->ptr, data->len); 843 memset(data->ptr, 0, data->len); 844 } 845 free(data); 846 } 847 as->index = 0; 848 _auth_spool(as, pfd[0]); 849 close(pfd[0]); 850 if (waitpid(pid, &status, 0) < 0) { 851 if (errno != ECHILD) { 852 syslog(LOG_ERR, "%s: waitpid: %m", path); 853 _warnx("internal failure"); 854 goto fail; 855 } 856 } else if (!WIFEXITED(status)) 857 goto fail; 858 } 859 860 /* 861 * Now scan the spooled data 862 * It is easier to wait for all the data before starting 863 * to scan it. 864 */ 865 for (line = as->spool; line < as->spool + as->index;) { 866 if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) { 867 line += sizeof(BI_REJECT) - 1; 868 if (!*line || *line == ' ' || *line == '\t') { 869 while (*line == ' ' || *line == '\t') 870 ++line; 871 if (!strcasecmp(line, "silent")) { 872 as->state = AUTH_SILENT; 873 break; 874 } 875 if (!strcasecmp(line, "challenge")) { 876 as->state = AUTH_CHALLENGE; 877 break; 878 } 879 if (!strcasecmp(line, "expired")) { 880 as->state = AUTH_EXPIRED; 881 break; 882 } 883 if (!strcasecmp(line, "pwexpired")) { 884 as->state = AUTH_PWEXPIRED; 885 break; 886 } 887 } 888 break; 889 } else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) { 890 line += sizeof(BI_AUTH) - 1; 891 if (!*line || *line == ' ' || *line == '\t') { 892 while (*line == ' ' || *line == '\t') 893 ++line; 894 if (*line == '\0') 895 as->state |= AUTH_OKAY; 896 else if (!strcasecmp(line, "root")) 897 as->state |= AUTH_ROOTOKAY; 898 else if (!strcasecmp(line, "secure")) 899 as->state |= AUTH_SECURE; 900 } 901 } else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) { 902 line += sizeof(BI_REMOVE) - 1; 903 while (*line == ' ' || *line == '\t') 904 ++line; 905 if (*line) 906 _add_rmlist(as, line); 907 } 908 while (*line++) 909 ; 910 } 911 912 if (WEXITSTATUS(status)) 913 as->state &= ~AUTH_ALLOW; 914 915 okay = as->state & AUTH_ALLOW; 916 917 if (!okay) 918 auth_clrenv(as); 919 920 if (0) { 921 fail: 922 auth_clrenv(as); 923 as->state = 0; 924 okay = -1; 925 } 926 927 while ((data = as->data) != NULL) { 928 as->data = data->next; 929 free(data); 930 } 931 932 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 933 va_end(as->ap0); 934 memset(&(as->ap0), 0, sizeof(as->ap0)); 935 } 936 937 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 938 va_end(as->ap); 939 memset(&(as->ap), 0, sizeof(as->ap)); 940 } 941 return (okay); 942 } 943 944 static void 945 _auth_spool(auth_session_t *as, int fd) 946 { 947 int r; 948 char *b; 949 950 while (as->index < sizeof(as->spool) - 1) { 951 r = read(fd, as->spool + as->index, 952 sizeof(as->spool) - as->index); 953 if (r <= 0) { 954 as->spool[as->index] = '\0'; 955 return; 956 } 957 b = as->spool + as->index; 958 as->index += r; 959 /* 960 * Go ahead and convert newlines into NULs to allow 961 * easy scanning of the file. 962 */ 963 while(r-- > 0) 964 if (*b++ == '\n') 965 b[-1] = '\0'; 966 } 967 968 syslog(LOG_ERR, "Overflowed backchannel spool buffer"); 969 errx(1, "System error in authentication program"); 970 } 971 972 static void 973 _add_rmlist(auth_session_t *as, char *file) 974 { 975 struct rmfiles *rm; 976 int i = strlen(file) + 1; 977 978 if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL) { 979 syslog(LOG_ERR, "Failed to allocate rmfiles: %m"); 980 return; 981 } 982 rm->file = (char *)(rm + 1); 983 rm->next = as->rmlist; 984 strcpy(rm->file, file); 985 as->rmlist = rm; 986 } 987 988 static char * 989 _auth_next_arg(auth_session_t *as) 990 { 991 char *arg; 992 993 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 994 if ((arg = va_arg(as->ap0, char *)) != NULL) 995 return (arg); 996 va_end(as->ap0); 997 memset(&(as->ap0), 0, sizeof(as->ap0)); 998 } 999 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 1000 if ((arg = va_arg(as->ap, char *)) != NULL) 1001 return (arg); 1002 va_end(as->ap); 1003 memset(&(as->ap), 0, sizeof(as->ap)); 1004 } 1005 return (NULL); 1006 } 1007