login_cap.c revision 83516
1/*- 2 * Copyright (c) 1996 by 3 * Sean Eric Fagan <sef@kithrup.com> 4 * David Nugent <davidn@blaze.net.au> 5 * All rights reserved. 6 * 7 * Portions copyright (c) 1995,1997 8 * Berkeley Software Design, Inc. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, is permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice immediately at the beginning of the file, without modification, 16 * this list of conditions, and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. This work was done expressly for inclusion into FreeBSD. Other use 21 * is permitted provided this notation is included. 22 * 4. Absolutely no warranty of function or purpose is made by the authors. 23 * 5. Modifications may be freely made to this file providing the above 24 * conditions are met. 25 * 26 * Low-level routines relating to the user capabilities database 27 * 28 * $FreeBSD: head/lib/libutil/login_cap.c 83516 2001-09-15 16:12:56Z rwatson $ 29 */ 30 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <unistd.h> 37 38#include <sys/types.h> 39#include <sys/time.h> 40#include <sys/resource.h> 41#include <sys/param.h> 42#include <pwd.h> 43#include <libutil.h> 44#include <syslog.h> 45#include <login_cap.h> 46 47/* 48 * allocstr() 49 * Manage a single static pointer for handling a local char* buffer, 50 * resizing as necessary to contain the string. 51 * 52 * allocarray() 53 * Manage a static array for handling a group of strings, resizing 54 * when necessary. 55 */ 56 57static int lc_object_count = 0; 58 59static size_t internal_stringsz = 0; 60static char * internal_string = NULL; 61static size_t internal_arraysz = 0; 62static char ** internal_array = NULL; 63 64static char * 65allocstr(char *str) 66{ 67 char *p; 68 69 size_t sz = strlen(str) + 1; /* realloc() only if necessary */ 70 if (sz <= internal_stringsz) 71 p = strcpy(internal_string, str); 72 else if ((p = realloc(internal_string, sz)) != NULL) { 73 internal_stringsz = sz; 74 internal_string = strcpy(p, str); 75 } 76 return p; 77} 78 79 80static char ** 81allocarray(size_t sz) 82{ 83 char **p; 84 85 if (sz <= internal_arraysz) 86 p = internal_array; 87 else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) { 88 internal_arraysz = sz; 89 internal_array = p; 90 } 91 return p; 92} 93 94 95/* 96 * arrayize() 97 * Turn a simple string <str> separated by any of 98 * the set of <chars> into an array. The last element 99 * of the array will be NULL, as is proper. 100 * Free using freearraystr() 101 */ 102 103static char ** 104arrayize(char *str, const char *chars, int *size) 105{ 106 int i; 107 char *ptr; 108 char **res = NULL; 109 110 /* count the sub-strings */ 111 for (i = 0, ptr = str; *ptr; i++) { 112 int count = strcspn(ptr, chars); 113 ptr += count; 114 if (*ptr) 115 ++ptr; 116 } 117 118 /* alloc the array */ 119 if ((ptr = allocstr(str)) != NULL) { 120 if ((res = allocarray(++i)) == NULL) 121 free(str); 122 else { 123 /* now split the string */ 124 i = 0; 125 while (*ptr) { 126 int count = strcspn(ptr, chars); 127 res[i++] = ptr; 128 ptr += count; 129 if (*ptr) 130 *ptr++ = '\0'; 131 } 132 res[i] = NULL; 133 } 134 } 135 136 if (size) 137 *size = i; 138 139 return res; 140} 141 142 143/* 144 * login_close() 145 * Frees up all resources relating to a login class 146 * 147 */ 148 149void 150login_close(login_cap_t * lc) 151{ 152 if (lc) { 153 free(lc->lc_style); 154 free(lc->lc_class); 155 free(lc->lc_cap); 156 free(lc); 157 if (--lc_object_count == 0) { 158 free(internal_string); 159 free(internal_array); 160 internal_array = NULL; 161 internal_arraysz = 0; 162 internal_string = NULL; 163 internal_stringsz = 0; 164 cgetclose(); 165 } 166 } 167} 168 169 170/* 171 * login_getclassbyname() get the login class by its name. 172 * If the name given is NULL or empty, the default class 173 * LOGIN_DEFCLASS (ie. "default") is fetched. If the 174 * 'dir' argument contains a non-NULL non-empty string, 175 * then the file _FILE_LOGIN_CONF is picked up from that 176 * directory instead of the system login database. 177 * Return a filled-out login_cap_t structure, including 178 * class name, and the capability record buffer. 179 */ 180 181login_cap_t * 182login_getclassbyname(char const *name, const struct passwd *pwd) 183{ 184 login_cap_t *lc; 185 186 if ((lc = malloc(sizeof(login_cap_t))) != NULL) { 187 int r, i = 0; 188 uid_t euid = 0; 189 gid_t egid = 0; 190 const char *msg = NULL; 191 const char *dir = (pwd == NULL) ? NULL : pwd->pw_dir; 192 char userpath[MAXPATHLEN]; 193 194 static char *login_dbarray[] = { NULL, NULL, NULL }; 195 196 /* 197 * Switch to user mode before checking/reading its ~/.login_conf 198 * - some NFSes have root read access disabled. 199 * 200 * XXX: This fails to configure additional groups. 201 */ 202 if (dir) { 203 euid = geteuid(); 204 egid = getegid(); 205 (void)setegid(pwd->pw_gid); 206 (void)seteuid(pwd->pw_uid); 207 } 208 209 if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, 210 _FILE_LOGIN_CONF) < MAXPATHLEN) { 211 login_dbarray[i] = userpath; 212 if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1) 213 i++; /* only use 'secure' data */ 214 } 215 if (_secure_path(_PATH_LOGIN_CONF, 0, 0) != -1) 216 login_dbarray[i++] = _PATH_LOGIN_CONF; 217 login_dbarray[i] = NULL; 218 219 memset(lc, 0, sizeof(login_cap_t)); 220 lc->lc_cap = lc->lc_class = lc->lc_style = NULL; 221 222 if (name == NULL || *name == '\0') 223 name = LOGIN_DEFCLASS; 224 225 switch (cgetent(&lc->lc_cap, login_dbarray, (char*)name)) { 226 case -1: /* Failed, entry does not exist */ 227 if (strcmp(name, LOGIN_MECLASS) == 0) 228 break; /* Don't retry default on 'me' */ 229 if (i == 0) 230 r = -1; 231 else if ((r = open(login_dbarray[0], O_RDONLY)) >= 0) 232 close(r); 233 /* 234 * If there's at least one login class database, 235 * and we aren't searching for a default class 236 * then complain about a non-existent class. 237 */ 238 if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0) 239 syslog(LOG_ERR, "login_getclass: unknown class '%s'", name); 240 /* fall-back to default class */ 241 name = LOGIN_DEFCLASS; 242 msg = "%s: no default/fallback class '%s'"; 243 if (cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0 && r >= 0) 244 break; 245 /* Fallthru - just return system defaults */ 246 case 0: /* success! */ 247 if ((lc->lc_class = strdup(name)) != NULL) { 248 if (dir) { 249 (void)seteuid(euid); 250 (void)setegid(egid); 251 } 252 ++lc_object_count; 253 return lc; 254 } 255 msg = "%s: strdup: %m"; 256 break; 257 case -2: 258 msg = "%s: retrieving class information: %m"; 259 break; 260 case -3: 261 msg = "%s: 'tc=' reference loop '%s'"; 262 break; 263 case 1: 264 msg = "couldn't resolve 'tc=' reference in '%s'"; 265 break; 266 default: 267 msg = "%s: unexpected cgetent() error '%s': %m"; 268 break; 269 } 270 if (dir) { 271 (void)seteuid(euid); 272 (void)setegid(egid); 273 } 274 if (msg != NULL) 275 syslog(LOG_ERR, msg, "login_getclass", name); 276 free(lc); 277 } 278 279 return NULL; 280} 281 282 283 284/* 285 * login_getclass() 286 * Get the login class for the system (only) login class database. 287 * Return a filled-out login_cap_t structure, including 288 * class name, and the capability record buffer. 289 */ 290 291login_cap_t * 292login_getclass(const char *cls) 293{ 294 return login_getclassbyname(cls, NULL); 295} 296 297 298/* 299 * login_getclass() 300 * Get the login class for a given password entry from 301 * the system (only) login class database. 302 * If the password entry's class field is not set, or 303 * the class specified does not exist, then use the 304 * default of LOGIN_DEFCLASS (ie. "default"). 305 * Return a filled-out login_cap_t structure, including 306 * class name, and the capability record buffer. 307 */ 308 309login_cap_t * 310login_getpwclass(const struct passwd *pwd) 311{ 312 const char *cls = NULL; 313 314 if (pwd != NULL) { 315 cls = pwd->pw_class; 316 if (cls == NULL || *cls == '\0') 317 cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS; 318 } 319 return login_getclassbyname(cls, pwd); 320} 321 322 323/* 324 * login_getuserclass() 325 * Get the login class for a given password entry, allowing user 326 * overrides via ~/.login_conf. 327 */ 328 329login_cap_t * 330login_getuserclass(const struct passwd *pwd) 331{ 332 return login_getclassbyname(LOGIN_MECLASS, pwd); 333} 334 335 336 337/* 338 * login_getcapstr() 339 * Given a login_cap entry, and a capability name, return the 340 * value defined for that capability, a defualt if not found, or 341 * an error string on error. 342 */ 343 344char * 345login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *error) 346{ 347 char *res; 348 int ret; 349 350 if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0') 351 return def; 352 353 if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) 354 return def; 355 return (ret >= 0) ? res : error; 356} 357 358 359/* 360 * login_getcaplist() 361 * Given a login_cap entry, and a capability name, return the 362 * value defined for that capability split into an array of 363 * strings. 364 */ 365 366char ** 367login_getcaplist(login_cap_t *lc, const char *cap, const char *chars) 368{ 369 char *lstring; 370 371 if (chars == NULL) 372 chars = ", \t"; 373 if ((lstring = login_getcapstr(lc, (char*)cap, NULL, NULL)) != NULL) 374 return arrayize(lstring, chars, NULL); 375 return NULL; 376} 377 378 379/* 380 * login_getpath() 381 * From the login_cap_t <lc>, get the capability <cap> which is 382 * formatted as either a space or comma delimited list of paths 383 * and append them all into a string and separate by semicolons. 384 * If there is an error of any kind, return <error>. 385 */ 386 387char * 388login_getpath(login_cap_t *lc, const char *cap, char * error) 389{ 390 char *str; 391 392 if ((str = login_getcapstr(lc, (char*)cap, NULL, NULL)) == NULL) 393 str = error; 394 else { 395 char *ptr = str; 396 397 while (*ptr) { 398 int count = strcspn(ptr, ", \t"); 399 ptr += count; 400 if (*ptr) 401 *ptr++ = ':'; 402 } 403 } 404 return str; 405} 406 407 408static int 409isinfinite(const char *s) 410{ 411 static const char *infs[] = { 412 "infinity", 413 "inf", 414 "unlimited", 415 "unlimit", 416 "-1", 417 NULL 418 }; 419 const char **i = &infs[0]; 420 421 while (*i != NULL) { 422 if (strcasecmp(s, *i) == 0) 423 return 1; 424 ++i; 425 } 426 return 0; 427} 428 429 430static u_quad_t 431rmultiply(u_quad_t n1, u_quad_t n2) 432{ 433 u_quad_t m, r; 434 int b1, b2; 435 436 static int bpw = 0; 437 438 /* Handle simple cases */ 439 if (n1 == 0 || n2 == 0) 440 return 0; 441 if (n1 == 1) 442 return n2; 443 if (n2 == 1) 444 return n1; 445 446 /* 447 * sizeof() returns number of bytes needed for storage. 448 * This may be different from the actual number of useful bits. 449 */ 450 if (!bpw) { 451 bpw = sizeof(u_quad_t) * 8; 452 while (((u_quad_t)1 << (bpw-1)) == 0) 453 --bpw; 454 } 455 456 /* 457 * First check the magnitude of each number. If the sum of the 458 * magnatude is way to high, reject the number. (If this test 459 * is not done then the first multiply below may overflow.) 460 */ 461 for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1) 462 ; 463 for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2) 464 ; 465 if (b1 + b2 - 2 > bpw) { 466 errno = ERANGE; 467 return (UQUAD_MAX); 468 } 469 470 /* 471 * Decompose the multiplication to be: 472 * h1 = n1 & ~1 473 * h2 = n2 & ~1 474 * l1 = n1 & 1 475 * l2 = n2 & 1 476 * (h1 + l1) * (h2 + l2) 477 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2) 478 * 479 * Since h1 && h2 do not have the low bit set, we can then say: 480 * 481 * (h1>>1 * h2>>1 * 4) + ... 482 * 483 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will 484 * overflow. 485 * 486 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2) 487 * then adding in residual amout will cause an overflow. 488 */ 489 490 m = (n1 >> 1) * (n2 >> 1); 491 if (m >= ((u_quad_t)1 << (bpw-2))) { 492 errno = ERANGE; 493 return (UQUAD_MAX); 494 } 495 m *= 4; 496 497 r = (n1 & n2 & 1) 498 + (n2 & 1) * (n1 & ~(u_quad_t)1) 499 + (n1 & 1) * (n2 & ~(u_quad_t)1); 500 501 if ((u_quad_t)(m + r) < m) { 502 errno = ERANGE; 503 return (UQUAD_MAX); 504 } 505 m += r; 506 507 return (m); 508} 509 510 511/* 512 * login_getcaptime() 513 * From the login_cap_t <lc>, get the capability <cap>, which is 514 * formatted as a time (e.g., "<cap>=10h3m2s"). If <cap> is not 515 * present in <lc>, return <def>; if there is an error of some kind, 516 * return <error>. 517 */ 518 519rlim_t 520login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 521{ 522 char *res, *ep, *oval; 523 int r; 524 rlim_t tot; 525 526 errno = 0; 527 if (lc == NULL || lc->lc_cap == NULL) 528 return def; 529 530 /* 531 * Look for <cap> in lc_cap. 532 * If it's not there (-1), return <def>. 533 * If there's an error, return <error>. 534 */ 535 536 if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) 537 return def; 538 else if (r < 0) { 539 errno = ERANGE; 540 return error; 541 } 542 543 /* "inf" and "infinity" are special cases */ 544 if (isinfinite(res)) 545 return RLIM_INFINITY; 546 547 /* 548 * Now go through the string, turning something like 1h2m3s into 549 * an integral value. Whee. 550 */ 551 552 errno = 0; 553 tot = 0; 554 oval = res; 555 while (*res) { 556 rlim_t tim = strtoq(res, &ep, 0); 557 rlim_t mult = 1; 558 559 if (ep == NULL || ep == res || errno != 0) { 560 invalid: 561 syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s", 562 lc->lc_class, cap, oval); 563 errno = ERANGE; 564 return error; 565 } 566 /* Look for suffixes */ 567 switch (*ep++) { 568 case 0: 569 ep--; 570 break; /* end of string */ 571 case 's': case 'S': /* seconds */ 572 break; 573 case 'm': case 'M': /* minutes */ 574 mult = 60; 575 break; 576 case 'h': case 'H': /* hours */ 577 mult = 60L * 60L; 578 break; 579 case 'd': case 'D': /* days */ 580 mult = 60L * 60L * 24L; 581 break; 582 case 'w': case 'W': /* weeks */ 583 mult = 60L * 60L * 24L * 7L; 584 break; 585 case 'y': case 'Y': /* 365-day years */ 586 mult = 60L * 60L * 24L * 365L; 587 break; 588 default: 589 goto invalid; 590 } 591 res = ep; 592 tot += rmultiply(tim, mult); 593 if (errno) 594 goto invalid; 595 } 596 597 return tot; 598} 599 600 601/* 602 * login_getcapnum() 603 * From the login_cap_t <lc>, extract the numerical value <cap>. 604 * If it is not present, return <def> for a default, and return 605 * <error> if there is an error. 606 * Like login_getcaptime(), only it only converts to a number, not 607 * to a time; "infinity" and "inf" are 'special.' 608 */ 609 610rlim_t 611login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 612{ 613 char *ep, *res; 614 int r; 615 rlim_t val; 616 617 if (lc == NULL || lc->lc_cap == NULL) 618 return def; 619 620 /* 621 * For BSDI compatibility, try for the tag=<val> first 622 */ 623 if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) { 624 long lval; 625 /* string capability not present, so try for tag#<val> as numeric */ 626 if ((r = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1) 627 return def; /* Not there, so return default */ 628 else if (r >= 0) 629 return (rlim_t)lval; 630 } 631 632 if (r < 0) { 633 errno = ERANGE; 634 return error; 635 } 636 637 if (isinfinite(res)) 638 return RLIM_INFINITY; 639 640 errno = 0; 641 val = strtoq(res, &ep, 0); 642 if (ep == NULL || ep == res || errno != 0) { 643 syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s", 644 lc->lc_class, cap, res); 645 errno = ERANGE; 646 return error; 647 } 648 649 return val; 650} 651 652 653 654/* 655 * login_getcapsize() 656 * From the login_cap_t <lc>, extract the capability <cap>, which is 657 * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity". 658 * If not present, return <def>, or <error> if there is an error of 659 * some sort. 660 */ 661 662rlim_t 663login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 664{ 665 char *ep, *res, *oval; 666 int r; 667 rlim_t tot; 668 669 if (lc == NULL || lc->lc_cap == NULL) 670 return def; 671 672 if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) 673 return def; 674 else if (r < 0) { 675 errno = ERANGE; 676 return error; 677 } 678 679 if (isinfinite(res)) 680 return RLIM_INFINITY; 681 682 errno = 0; 683 tot = 0; 684 oval = res; 685 while (*res) { 686 rlim_t siz = strtoq(res, &ep, 0); 687 rlim_t mult = 1; 688 689 if (ep == NULL || ep == res || errno != 0) { 690 invalid: 691 syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s", 692 lc->lc_class, cap, oval); 693 errno = ERANGE; 694 return error; 695 } 696 switch (*ep++) { 697 case 0: /* end of string */ 698 ep--; 699 break; 700 case 'b': case 'B': /* 512-byte blocks */ 701 mult = 512; 702 break; 703 case 'k': case 'K': /* 1024-byte Kilobytes */ 704 mult = 1024; 705 break; 706 case 'm': case 'M': /* 1024-k kbytes */ 707 mult = 1024 * 1024; 708 break; 709 case 'g': case 'G': /* 1Gbyte */ 710 mult = 1024 * 1024 * 1024; 711 break; 712 case 't': case 'T': /* 1TBte */ 713 mult = 1024LL * 1024LL * 1024LL * 1024LL; 714 break; 715 default: 716 goto invalid; 717 } 718 res = ep; 719 tot += rmultiply(siz, mult); 720 if (errno) 721 goto invalid; 722 } 723 724 return tot; 725} 726 727 728/* 729 * login_getcapbool() 730 * From the login_cap_t <lc>, check for the existance of the capability 731 * of <cap>. Return <def> if <lc>->lc_cap is NULL, otherwise return 732 * the whether or not <cap> exists there. 733 */ 734 735int 736login_getcapbool(login_cap_t *lc, const char *cap, int def) 737{ 738 if (lc == NULL || lc->lc_cap == NULL) 739 return def; 740 return (cgetcap(lc->lc_cap, (char *)cap, ':') != NULL); 741} 742 743 744/* 745 * login_getstyle() 746 * Given a login_cap entry <lc>, and optionally a type of auth <auth>, 747 * and optionally a style <style>, find the style that best suits these 748 * rules: 749 * 1. If <auth> is non-null, look for an "auth-<auth>=" string 750 * in the capability; if not present, default to "auth=". 751 * 2. If there is no auth list found from (1), default to 752 * "passwd" as an authorization list. 753 * 3. If <style> is non-null, look for <style> in the list of 754 * authorization methods found from (2); if <style> is NULL, default 755 * to LOGIN_DEFSTYLE ("passwd"). 756 * 4. If the chosen style is found in the chosen list of authorization 757 * methods, return that; otherwise, return NULL. 758 * E.g.: 759 * login_getstyle(lc, NULL, "ftp"); 760 * login_getstyle(lc, "login", NULL); 761 * login_getstyle(lc, "skey", "network"); 762 */ 763 764char * 765login_getstyle(login_cap_t *lc, char *style, const char *auth) 766{ 767 int i; 768 char **authtypes = NULL; 769 char *auths= NULL; 770 char realauth[64]; 771 772 static char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL }; 773 774 if (auth != NULL && *auth != '\0') { 775 if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < sizeof realauth) 776 authtypes = login_getcaplist(lc, realauth, NULL); 777 } 778 779 if (authtypes == NULL) 780 authtypes = login_getcaplist(lc, "auth", NULL); 781 782 if (authtypes == NULL) 783 authtypes = defauthtypes; 784 785 /* 786 * We have at least one authtype now; auths is a comma-separated 787 * (or space-separated) list of authentication types. We have to 788 * convert from this to an array of char*'s; authtypes then gets this. 789 */ 790 i = 0; 791 if (style != NULL && *style != '\0') { 792 while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0) 793 i++; 794 } 795 796 lc->lc_style = NULL; 797 if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL) 798 lc->lc_style = auths; 799 800 if (lc->lc_style != NULL) 801 lc->lc_style = strdup(lc->lc_style); 802 803 return lc->lc_style; 804} 805