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