1 /* $OpenBSD: auth_subr.c,v 1.40 2014/05/25 17:47:04 tedu Exp $ */ 2 3 /* 4 * Copyright (c) 2000-2002,2004 Todd C. Miller <Todd.Miller@courtesan.com> 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 *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) { 152 #if defined(__GNUC__) && __GNUC__ >= 3 153 va_copy(as->ap, ap); 154 #else 155 as->ap = ap; 156 #endif 157 } 158 int auth_getstate(auth_session_t *as) { return (as->state); } 159 struct passwd *auth_getpwd(auth_session_t *as) { return (as->pwd); } 160 161 /* 162 * Open a new BSD Authentication session with the default service 163 * (which can be changed later). 164 */ 165 auth_session_t * 166 auth_open(void) 167 { 168 auth_session_t *as; 169 170 if ((as = calloc(1, sizeof(auth_session_t))) != NULL) { 171 as->service = defservice; 172 as->fd = -1; 173 } 174 175 return (as); 176 } 177 178 /* 179 * Clean the specified BSD Authentication session. 180 */ 181 void 182 auth_clean(auth_session_t *as) 183 { 184 struct rmfiles *rm; 185 struct authdata *data; 186 187 as->state = 0; 188 189 auth_clrenv(as); 190 191 /* 192 * Clean out the rmlist and remove specified files 193 */ 194 while ((rm = as->rmlist) != NULL) { 195 as->rmlist = rm->next; 196 unlink(rm->file); 197 free(rm); 198 } 199 200 /* 201 * Clean out data 202 */ 203 while ((data = as->data) != NULL) { 204 if (as->data->len) 205 memset(as->data->ptr, 0, as->data->len); 206 as->data = data->next; 207 free(data); 208 } 209 210 auth_setitem(as, AUTHV_ALL, NULL); 211 212 if (as->pwd != NULL) { 213 memset(as->pwd->pw_passwd, 0, strlen(as->pwd->pw_passwd)); 214 free(as->pwd); 215 as->pwd = NULL; 216 } 217 218 if (as->fd != -1) { 219 close(as->fd); 220 as->fd = -1; 221 } 222 } 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 memset(as->data->ptr, 0, as->data->len); 272 as->data = data->next; 273 free(data); 274 } 275 276 if (as->pwd != NULL) { 277 memset(as->pwd->pw_passwd, 0, 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 if (as->challenge) 288 free(as->challenge); 289 if (as->class) 290 free(as->class); 291 if (as->style) 292 free(as->style); 293 if (as->name) 294 free(as->name); 295 296 free(as); 297 return (s); 298 } 299 300 /* 301 * Request a challenge for the session. 302 * The name and style must have already been specified 303 */ 304 char * 305 auth_challenge(auth_session_t *as) 306 { 307 char path[PATH_MAX]; 308 int len; 309 310 if (as == NULL || as->style == NULL || as->name == NULL) 311 return (NULL); 312 313 len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); 314 if (len < 0 || len >= sizeof(path)) 315 return (NULL); 316 317 as->state = 0; 318 319 if (as->challenge) { 320 free(as->challenge); 321 as->challenge = NULL; 322 } 323 324 auth_call(as, path, as->style, "-s", "challenge", as->name, 325 as->class, (char *)NULL); 326 if (as->state & AUTH_CHALLENGE) 327 as->challenge = auth_getvalue(as, "challenge"); 328 as->state = 0; 329 as->index = 0; /* toss our data */ 330 return (as->challenge); 331 } 332 333 /* 334 * Set/unset the requested environment variables. 335 * Mark the variables as set so they will not be set a second time. 336 * XXX - should provide a way to detect setenv() failure. 337 */ 338 void 339 auth_setenv(auth_session_t *as) 340 { 341 char *line, *name; 342 343 /* 344 * Set any environment variables we were asked for 345 */ 346 for (line = as->spool; line < as->spool + as->index;) { 347 if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { 348 if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { 349 /* only do it once! */ 350 line[0] = 'd'; line[1] = 'i'; line[2] = 'd'; 351 line += sizeof(BI_SETENV) - 1; 352 for (name = line; 353 isblank((unsigned char)*name); ++name) 354 ; 355 for (line = name; 356 *line && !isblank((unsigned char)*line); 357 ++line) 358 ; 359 if (*line) 360 *line++ = '\0'; 361 for (; isblank((unsigned char)*line); ++line) 362 ; 363 if (*line != '\0' && setenv(name, line, 1)) 364 _warn("setenv(%s, %s)", name, line); 365 } 366 } else 367 if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { 368 if (isblank(line[sizeof(BI_UNSETENV) - 1])) { 369 /* only do it once! */ 370 line[2] = 'd'; line[3] = 'i'; line[4] = 'd'; 371 line += sizeof(BI_UNSETENV) - 1; 372 for (name = line; 373 isblank((unsigned char)*name); ++name) 374 ; 375 for (line = name; 376 *line && !isblank((unsigned char)*line); 377 ++line) 378 ; 379 if (*line) 380 *line++ = '\0'; 381 unsetenv(name); 382 } 383 } 384 while (*line++) 385 ; 386 } 387 } 388 389 /* 390 * Clear out any requested environment variables. 391 */ 392 void 393 auth_clrenv(auth_session_t *as) 394 { 395 char *line; 396 397 for (line = as->spool; line < as->spool + as->index;) { 398 if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { 399 if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { 400 line[0] = 'i'; line[1] = 'g'; line[2] = 'n'; 401 } 402 } else 403 if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { 404 if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) { 405 line[2] = 'i'; line[3] = 'g'; line[4] = 'n'; 406 } 407 } 408 while (*line++) 409 ; 410 } 411 } 412 413 char * 414 auth_getitem(auth_session_t *as, auth_item_t item) 415 { 416 if (as != NULL) { 417 switch (item) { 418 case AUTHV_CHALLENGE: 419 return (as->challenge); 420 case AUTHV_CLASS: 421 return (as->class); 422 case AUTHV_NAME: 423 return (as->name); 424 case AUTHV_SERVICE: 425 return (as->service ? as->service : defservice); 426 case AUTHV_STYLE: 427 return (as->style); 428 case AUTHV_INTERACTIVE: 429 return ((as->flags & AF_INTERACTIVE) ? "True" : NULL); 430 default: 431 break; 432 } 433 } 434 return (NULL); 435 } 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 if (as->challenge) 465 free(as->challenge); 466 as->challenge = value; 467 return (0); 468 469 case AUTHV_CLASS: 470 if (value == as->class) 471 return (0); 472 if (value != NULL && (value = strdup(value)) == NULL) 473 return (-1); 474 if (as->class) 475 free(as->class); 476 as->class = value; 477 return (0); 478 479 case AUTHV_NAME: 480 if (value == as->name) 481 return (0); 482 if (value != NULL && (value = strdup(value)) == NULL) 483 return (-1); 484 if (as->name) 485 free(as->name); 486 as->name = value; 487 return (0); 488 489 case AUTHV_SERVICE: 490 if (value == as->service) 491 return (0); 492 if (value == NULL || strcmp(value, defservice) == 0) 493 value = defservice; 494 else if ((value = strdup(value)) == NULL) 495 return (-1); 496 if (as->service && as->service != defservice) 497 free(as->service); 498 as->service = value; 499 return (0); 500 501 case AUTHV_STYLE: 502 if (value == as->style) 503 return (0); 504 if (value == NULL || strchr(value, '/') != NULL || 505 (value = strdup(value)) == NULL) 506 return (-1); 507 if (as->style) 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 525 int 526 auth_setoption(auth_session_t *as, char *n, char *v) 527 { 528 struct authopts *opt; 529 size_t len = strlen(n) + strlen(v) + 2; 530 int ret; 531 532 if ((opt = malloc(sizeof(*opt) + len)) == NULL) 533 return (-1); 534 535 opt->opt = (char *)(opt + 1); 536 537 ret = snprintf(opt->opt, len, "%s=%s", n, v); 538 if (ret < 0 || ret >= len) { 539 free(opt); 540 errno = ENAMETOOLONG; 541 return (-1); 542 } 543 opt->next = as->optlist; 544 as->optlist = opt; 545 return(0); 546 } 547 548 void 549 auth_clroptions(auth_session_t *as) 550 { 551 struct authopts *opt; 552 553 while ((opt = as->optlist) != NULL) { 554 as->optlist = opt->next; 555 free(opt); 556 } 557 } 558 559 void 560 auth_clroption(auth_session_t *as, char *option) 561 { 562 struct authopts *opt, *oopt; 563 size_t len; 564 565 len = strlen(option); 566 567 if ((opt = as->optlist) == NULL) 568 return; 569 570 if (strncmp(opt->opt, option, len) == 0 && 571 (opt->opt[len] == '=' || opt->opt[len] == '\0')) { 572 as->optlist = opt->next; 573 free(opt); 574 return; 575 } 576 577 while ((oopt = opt->next) != NULL) { 578 if (strncmp(oopt->opt, option, len) == 0 && 579 (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) { 580 opt->next = oopt->next; 581 free(oopt); 582 return; 583 } 584 opt = oopt; 585 } 586 } 587 588 int 589 auth_setdata(auth_session_t *as, void *ptr, size_t len) 590 { 591 struct authdata *data, *dp; 592 593 if (len <= 0) 594 return (0); 595 596 if ((data = malloc(sizeof(*data) + len)) == NULL) 597 return (-1); 598 599 data->next = NULL; 600 data->len = len; 601 data->ptr = data + 1; 602 memcpy(data->ptr, ptr, len); 603 604 if (as->data == NULL) 605 as->data = data; 606 else { 607 for (dp = as->data; dp->next != NULL; dp = dp->next) 608 ; 609 dp->next = data; 610 } 611 return (0); 612 } 613 614 int 615 auth_setpwd(auth_session_t *as, struct passwd *pwd) 616 { 617 char *instance; 618 619 if (pwd == NULL && as->pwd == NULL && as->name == NULL) 620 return (-1); /* true failure */ 621 622 if (pwd == NULL) { 623 /* 624 * If we were not passed in a pwd structure we need to 625 * go find one for ourself. Always look up the username 626 * (if it is defined) in the passwd database to see if there 627 * is an entry for the user. If not, either use the current 628 * entry or simply return a 1 which implies there is 629 * no user by that name here. This is not a failure, just 630 * a point of information. 631 */ 632 if (as->name == NULL) 633 return (0); 634 if ((pwd = getpwnam(as->name)) == NULL) { 635 instance = strchr(as->name, '/'); 636 if (instance == NULL) 637 return (as->pwd ? 0 : 1); 638 if (strcmp(instance, "/root") == 0) 639 pwd = getpwnam(instance + 1); 640 if (pwd == NULL) 641 return (as->pwd ? 0 : 1); 642 } 643 } 644 if ((pwd = pw_dup(pwd)) == NULL) 645 return (-1); /* true failure */ 646 if (as->pwd) { 647 memset(as->pwd->pw_passwd, 0, strlen(as->pwd->pw_passwd)); 648 free(as->pwd); 649 } 650 as->pwd = pwd; 651 return (0); 652 } 653 654 char * 655 auth_getvalue(auth_session_t *as, char *what) 656 { 657 char *line, *v, *value; 658 int n, len; 659 660 len = strlen(what); 661 662 for (line = as->spool; line < as->spool + as->index;) { 663 if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0) 664 goto next; 665 line += sizeof(BI_VALUE) - 1; 666 667 if (!isblank((unsigned char)*line)) 668 goto next; 669 670 while (isblank((unsigned char)*++line)) 671 ; 672 673 if (strncmp(line, what, len) != 0 || 674 !isblank((unsigned char)line[len])) 675 goto next; 676 line += len; 677 while (isblank((unsigned char)*++line)) 678 ; 679 value = strdup(line); 680 if (value == NULL) 681 return (NULL); 682 683 /* 684 * XXX - There should be a more standardized 685 * routine for doing this sort of thing. 686 */ 687 for (line = v = value; *line; ++line) { 688 if (*line == '\\') { 689 switch (*++line) { 690 case 'r': 691 *v++ = '\r'; 692 break; 693 case 'n': 694 *v++ = '\n'; 695 break; 696 case 't': 697 *v++ = '\t'; 698 break; 699 case '0': case '1': case '2': 700 case '3': case '4': case '5': 701 case '6': case '7': 702 n = *line - '0'; 703 if (isdigit((unsigned char)line[1])) { 704 ++line; 705 n <<= 3; 706 n |= *line-'0'; 707 } 708 if (isdigit((unsigned char)line[1])) { 709 ++line; 710 n <<= 3; 711 n |= *line-'0'; 712 } 713 break; 714 default: 715 *v++ = *line; 716 break; 717 } 718 } else 719 *v++ = *line; 720 } 721 *v = '\0'; 722 return (value); 723 next: 724 while (*line++) 725 ; 726 } 727 return (NULL); 728 } 729 730 quad_t 731 auth_check_expire(auth_session_t *as) 732 { 733 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 734 as->state &= ~AUTH_ALLOW; 735 as->state |= AUTH_EXPIRED; /* XXX */ 736 return (-1); 737 } 738 739 if (as->pwd == NULL) 740 return (0); 741 742 if (as->pwd && (quad_t)as->pwd->pw_expire != 0) { 743 if (as->now.tv_sec == 0) 744 gettimeofday(&as->now, (struct timezone *)NULL); 745 if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) { 746 as->state &= ~AUTH_ALLOW; 747 as->state |= AUTH_EXPIRED; 748 } 749 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire) 750 return (-1); 751 return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec); 752 } 753 return (0); 754 } 755 756 quad_t 757 auth_check_change(auth_session_t *as) 758 { 759 if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { 760 as->state &= ~AUTH_ALLOW; 761 as->state |= AUTH_PWEXPIRED; /* XXX */ 762 return (-1); 763 } 764 765 if (as->pwd == NULL) 766 return (0); 767 768 if (as->pwd && (quad_t)as->pwd->pw_change) { 769 if (as->now.tv_sec == 0) 770 gettimeofday(&as->now, (struct timezone *)NULL); 771 if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) { 772 as->state &= ~AUTH_ALLOW; 773 as->state |= AUTH_PWEXPIRED; 774 } 775 if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change) 776 return (-1); 777 return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec); 778 } 779 return (0); 780 } 781 782 /* 783 * The down and dirty call to the login script 784 * okay contains the default return value, typically 0 but 785 * is AUTH_OKAY for approval like scripts. 786 * 787 * Internally additional trailing arguments can be read from as->ap 788 * Options will be placed just after the first argument (not including path). 789 * 790 * Any data will be sent to (and freed by) the script 791 */ 792 int 793 auth_call(auth_session_t *as, char *path, ...) 794 { 795 char *line; 796 struct authdata *data; 797 struct authopts *opt; 798 pid_t pid; 799 int status; 800 int okay; 801 int pfd[2]; 802 int argc; 803 char *argv[64]; /* 64 args should be more than enough */ 804 #define Nargc (sizeof(argv)/sizeof(argv[0])) 805 806 va_start(as->ap0, path); 807 808 argc = 0; 809 if ((argv[argc] = _auth_next_arg(as)) != NULL) 810 ++argc; 811 812 if (as->fd != -1) { 813 argv[argc++] = "-v"; 814 argv[argc++] = "fd=4"; /* AUTH_FD, see below */ 815 } 816 for (opt = as->optlist; opt != NULL; opt = opt->next) { 817 if (argc < Nargc - 2) { 818 argv[argc++] = "-v"; 819 argv[argc++] = opt->opt; 820 } else { 821 syslog(LOG_ERR, "too many authentication options"); 822 goto fail; 823 } 824 } 825 while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as))) 826 ++argc; 827 828 if (argc >= Nargc - 1 && _auth_next_arg(as)) { 829 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 830 va_end(as->ap0); 831 memset(&(as->ap0), 0, sizeof(as->ap0)); 832 } 833 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 834 va_end(as->ap); 835 memset(&(as->ap), 0, sizeof(as->ap)); 836 } 837 syslog(LOG_ERR, "too many arguments"); 838 goto fail; 839 } 840 841 argv[argc] = NULL; 842 843 if (secure_path(path) < 0) { 844 syslog(LOG_ERR, "%s: path not secure", path); 845 _warnx("invalid script: %s", path); 846 goto fail; 847 } 848 849 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) < 0) { 850 syslog(LOG_ERR, "unable to create backchannel %m"); 851 _warnx("internal resource failure"); 852 goto fail; 853 } 854 855 switch (pid = fork()) { 856 case -1: 857 syslog(LOG_ERR, "%s: %m", path); 858 _warnx("internal resource failure"); 859 close(pfd[0]); 860 close(pfd[1]); 861 goto fail; 862 case 0: 863 #define COMM_FD 3 864 #define AUTH_FD 4 865 if (dup2(pfd[1], COMM_FD) < 0) 866 err(1, "dup of backchannel"); 867 if (as->fd != -1) { 868 if (dup2(as->fd, AUTH_FD) < 0) 869 err(1, "dup of auth fd"); 870 closefrom(AUTH_FD + 1); 871 } else 872 closefrom(COMM_FD + 1); 873 execve(path, argv, auth_environ); 874 syslog(LOG_ERR, "%s: %m", path); 875 err(1, "%s", path); 876 default: 877 close(pfd[1]); 878 if (as->fd != -1) { 879 close(as->fd); /* so child has only ref */ 880 as->fd = -1; 881 } 882 while ((data = as->data) != NULL) { 883 as->data = data->next; 884 if (data->len > 0) { 885 write(pfd[0], data->ptr, data->len); 886 memset(data->ptr, 0, data->len); 887 } 888 free(data); 889 } 890 as->index = 0; 891 _auth_spool(as, pfd[0]); 892 close(pfd[0]); 893 status = 0; 894 while (waitpid(pid, &status, 0) == -1 && errno == EINTR) 895 ; 896 if (pid < 0) { 897 if (errno != ECHILD) { 898 syslog(LOG_ERR, "%s: waitpid: %m", path); 899 _warnx("internal failure"); 900 goto fail; 901 } 902 } else if (!WIFEXITED(status)) 903 goto fail; 904 } 905 906 /* 907 * Now scan the spooled data 908 * It is easier to wait for all the data before starting 909 * to scan it. 910 */ 911 for (line = as->spool; line < as->spool + as->index;) { 912 if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) { 913 line += sizeof(BI_REJECT) - 1; 914 if (!*line || *line == ' ' || *line == '\t') { 915 while (*line == ' ' || *line == '\t') 916 ++line; 917 if (!strcasecmp(line, "silent")) { 918 as->state = AUTH_SILENT; 919 break; 920 } 921 if (!strcasecmp(line, "challenge")) { 922 as->state = AUTH_CHALLENGE; 923 break; 924 } 925 if (!strcasecmp(line, "expired")) { 926 as->state = AUTH_EXPIRED; 927 break; 928 } 929 if (!strcasecmp(line, "pwexpired")) { 930 as->state = AUTH_PWEXPIRED; 931 break; 932 } 933 } 934 break; 935 } else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) { 936 line += sizeof(BI_AUTH) - 1; 937 if (!*line || *line == ' ' || *line == '\t') { 938 while (*line == ' ' || *line == '\t') 939 ++line; 940 if (*line == '\0') 941 as->state |= AUTH_OKAY; 942 else if (!strcasecmp(line, "root")) 943 as->state |= AUTH_ROOTOKAY; 944 else if (!strcasecmp(line, "secure")) 945 as->state |= AUTH_SECURE; 946 } 947 } else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) { 948 line += sizeof(BI_REMOVE) - 1; 949 while (*line == ' ' || *line == '\t') 950 ++line; 951 if (*line) 952 _add_rmlist(as, line); 953 } 954 while (*line++) 955 ; 956 } 957 958 if (WEXITSTATUS(status)) 959 as->state &= ~AUTH_ALLOW; 960 961 okay = as->state & AUTH_ALLOW; 962 963 if (!okay) 964 auth_clrenv(as); 965 966 if (0) { 967 fail: 968 auth_clrenv(as); 969 as->state = 0; 970 okay = -1; 971 } 972 973 while ((data = as->data) != NULL) { 974 as->data = data->next; 975 free(data); 976 } 977 978 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 979 va_end(as->ap0); 980 memset(&(as->ap0), 0, sizeof(as->ap0)); 981 } 982 983 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 984 va_end(as->ap); 985 memset(&(as->ap), 0, sizeof(as->ap)); 986 } 987 return (okay); 988 } 989 990 static void 991 _recv_fd(auth_session_t *as, int fd) 992 { 993 struct msghdr msg; 994 struct cmsghdr *cmp; 995 union { 996 struct cmsghdr hdr; 997 char buf[CMSG_SPACE(sizeof(int))]; 998 } cmsgbuf; 999 1000 memset(&msg, 0, sizeof(msg)); 1001 msg.msg_control = &cmsgbuf.buf; 1002 msg.msg_controllen = sizeof(cmsgbuf.buf); 1003 if (recvmsg(fd, &msg, 0) < 0) 1004 syslog(LOG_ERR, "recvmsg: %m"); 1005 else if (msg.msg_flags & MSG_TRUNC) 1006 syslog(LOG_ERR, "message truncated"); 1007 else if (msg.msg_flags & MSG_CTRUNC) 1008 syslog(LOG_ERR, "control message truncated"); 1009 else if ((cmp = CMSG_FIRSTHDR(&msg)) == NULL) 1010 syslog(LOG_ERR, "missing control message"); 1011 else { 1012 if (cmp->cmsg_level != SOL_SOCKET) 1013 syslog(LOG_ERR, "unexpected cmsg_level %d", 1014 cmp->cmsg_level); 1015 else if (cmp->cmsg_type != SCM_RIGHTS) 1016 syslog(LOG_ERR, "unexpected cmsg_type %d", 1017 cmp->cmsg_type); 1018 else if (cmp->cmsg_len != CMSG_LEN(sizeof(int))) 1019 syslog(LOG_ERR, "bad cmsg_len %d", 1020 cmp->cmsg_len); 1021 else { 1022 if (as->fd != -1) 1023 close(as->fd); 1024 as->fd = *(int *)CMSG_DATA(cmp); 1025 } 1026 } 1027 } 1028 1029 static void 1030 _auth_spool(auth_session_t *as, int fd) 1031 { 1032 ssize_t r; 1033 char *b, *s; 1034 1035 for (s = as->spool + as->index; as->index < sizeof(as->spool) - 1; ) { 1036 r = read(fd, as->spool + as->index, 1037 sizeof(as->spool) - as->index); 1038 if (r <= 0) { 1039 as->spool[as->index] = '\0'; 1040 return; 1041 } 1042 b = as->spool + as->index; 1043 as->index += r; 1044 /* 1045 * Convert newlines into NULs to allow easy scanning of the 1046 * file and receive an fd if there is a BI_FDPASS message. 1047 * XXX - checking for BI_FDPASS here is annoying but 1048 * we need to avoid the read() slurping in control data. 1049 */ 1050 while (r-- > 0) { 1051 if (*b++ == '\n') { 1052 b[-1] = '\0'; 1053 if (strcasecmp(s, BI_FDPASS) == 0) 1054 _recv_fd(as, fd); 1055 s = b; 1056 } 1057 } 1058 } 1059 1060 syslog(LOG_ERR, "Overflowed backchannel spool buffer"); 1061 errx(1, "System error in authentication program"); 1062 } 1063 1064 static void 1065 _add_rmlist(auth_session_t *as, char *file) 1066 { 1067 struct rmfiles *rm; 1068 size_t i = strlen(file) + 1; 1069 1070 // XXX should rangecheck i since we are about to add? 1071 1072 if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL) { 1073 syslog(LOG_ERR, "Failed to allocate rmfiles: %m"); 1074 return; 1075 } 1076 rm->file = (char *)(rm + 1); 1077 rm->next = as->rmlist; 1078 strlcpy(rm->file, file, i); 1079 as->rmlist = rm; 1080 } 1081 1082 static char * 1083 _auth_next_arg(auth_session_t *as) 1084 { 1085 char *arg; 1086 1087 if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { 1088 if ((arg = va_arg(as->ap0, char *)) != NULL) 1089 return (arg); 1090 va_end(as->ap0); 1091 memset(&(as->ap0), 0, sizeof(as->ap0)); 1092 } 1093 if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { 1094 if ((arg = va_arg(as->ap, char *)) != NULL) 1095 return (arg); 1096 va_end(as->ap); 1097 memset(&(as->ap), 0, sizeof(as->ap)); 1098 } 1099 return (NULL); 1100 } 1101