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