121288Sdavidn/*- 221288Sdavidn * Copyright (c) 1996 by 321288Sdavidn * Sean Eric Fagan <sef@kithrup.com> 421288Sdavidn * David Nugent <davidn@blaze.net.au> 521288Sdavidn * All rights reserved. 621288Sdavidn * 725670Sdavidn * Portions copyright (c) 1995,1997 825670Sdavidn * Berkeley Software Design, Inc. 925670Sdavidn * All rights reserved. 1025670Sdavidn * 1121288Sdavidn * Redistribution and use in source and binary forms, with or without 1221288Sdavidn * modification, is permitted provided that the following conditions 1321288Sdavidn * are met: 1421288Sdavidn * 1. Redistributions of source code must retain the above copyright 1521288Sdavidn * notice immediately at the beginning of the file, without modification, 1621288Sdavidn * this list of conditions, and the following disclaimer. 1721288Sdavidn * 2. Redistributions in binary form must reproduce the above copyright 1821288Sdavidn * notice, this list of conditions and the following disclaimer in the 1921288Sdavidn * documentation and/or other materials provided with the distribution. 2021288Sdavidn * 3. This work was done expressly for inclusion into FreeBSD. Other use 2121288Sdavidn * is permitted provided this notation is included. 2221288Sdavidn * 4. Absolutely no warranty of function or purpose is made by the authors. 2321288Sdavidn * 5. Modifications may be freely made to this file providing the above 2421288Sdavidn * conditions are met. 2521288Sdavidn * 2621288Sdavidn * Low-level routines relating to the user capabilities database 2721288Sdavidn */ 2821288Sdavidn 2984225Sdillon#include <sys/cdefs.h> 3084225Sdillon__FBSDID("$FreeBSD$"); 3184225Sdillon 3221288Sdavidn#include <sys/types.h> 3321288Sdavidn#include <sys/time.h> 3421288Sdavidn#include <sys/resource.h> 3521288Sdavidn#include <sys/param.h> 36116344Smarkm#include <errno.h> 37116344Smarkm#include <fcntl.h> 38116344Smarkm#include <libutil.h> 39116344Smarkm#include <login_cap.h> 4021288Sdavidn#include <pwd.h> 41116344Smarkm#include <stdio.h> 42116344Smarkm#include <stdlib.h> 43116344Smarkm#include <string.h> 4425670Sdavidn#include <syslog.h> 45116344Smarkm#include <unistd.h> 4621288Sdavidn 4725670Sdavidn/* 4825670Sdavidn * allocstr() 4925670Sdavidn * Manage a single static pointer for handling a local char* buffer, 5025670Sdavidn * resizing as necessary to contain the string. 5125670Sdavidn * 5225670Sdavidn * allocarray() 5325670Sdavidn * Manage a static array for handling a group of strings, resizing 5425670Sdavidn * when necessary. 5525670Sdavidn */ 5621288Sdavidn 5721288Sdavidnstatic int lc_object_count = 0; 5821288Sdavidn 5921288Sdavidnstatic size_t internal_stringsz = 0; 6021288Sdavidnstatic char * internal_string = NULL; 6121288Sdavidnstatic size_t internal_arraysz = 0; 62121193Smarkmstatic const char ** internal_array = NULL; 6321288Sdavidn 64184633Sdesstatic char path_login_conf[] = _PATH_LOGIN_CONF; 65184633Sdes 6621288Sdavidnstatic char * 67121193Smarkmallocstr(const char *str) 6821288Sdavidn{ 6925670Sdavidn char *p; 7025670Sdavidn 7125670Sdavidn size_t sz = strlen(str) + 1; /* realloc() only if necessary */ 7225670Sdavidn if (sz <= internal_stringsz) 7325670Sdavidn p = strcpy(internal_string, str); 7425670Sdavidn else if ((p = realloc(internal_string, sz)) != NULL) { 7525670Sdavidn internal_stringsz = sz; 7625670Sdavidn internal_string = strcpy(p, str); 7725670Sdavidn } 7825670Sdavidn return p; 7921288Sdavidn} 8021288Sdavidn 8125670Sdavidn 82121193Smarkmstatic const char ** 8321288Sdavidnallocarray(size_t sz) 8421288Sdavidn{ 85121193Smarkm static const char **p; 8625670Sdavidn 8725670Sdavidn if (sz <= internal_arraysz) 8825670Sdavidn p = internal_array; 8925670Sdavidn else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) { 9025670Sdavidn internal_arraysz = sz; 9125670Sdavidn internal_array = p; 9225670Sdavidn } 9325670Sdavidn return p; 9421288Sdavidn} 9521288Sdavidn 9621288Sdavidn 9721288Sdavidn/* 9821288Sdavidn * arrayize() 9972089Sasmodai * Turn a simple string <str> separated by any of 10021288Sdavidn * the set of <chars> into an array. The last element 10121288Sdavidn * of the array will be NULL, as is proper. 10221288Sdavidn * Free using freearraystr() 10321288Sdavidn */ 10421288Sdavidn 105121193Smarkmstatic const char ** 106121193Smarkmarrayize(const char *str, const char *chars, int *size) 10721288Sdavidn{ 10825670Sdavidn int i; 109121530Speter char *ptr; 110121530Speter const char *cptr; 111121193Smarkm const char **res = NULL; 11221288Sdavidn 11325670Sdavidn /* count the sub-strings */ 114121530Speter for (i = 0, cptr = str; *cptr; i++) { 115121530Speter int count = strcspn(cptr, chars); 116121530Speter cptr += count; 117121530Speter if (*cptr) 118121530Speter ++cptr; 11925670Sdavidn } 12021288Sdavidn 12125670Sdavidn /* alloc the array */ 12225670Sdavidn if ((ptr = allocstr(str)) != NULL) { 12325670Sdavidn if ((res = allocarray(++i)) == NULL) 124121530Speter free((void *)(uintptr_t)(const void *)str); 12525670Sdavidn else { 12625670Sdavidn /* now split the string */ 12725702Sdavidn i = 0; 12825670Sdavidn while (*ptr) { 12925670Sdavidn int count = strcspn(ptr, chars); 13025670Sdavidn res[i++] = ptr; 13125670Sdavidn ptr += count; 13225670Sdavidn if (*ptr) 13325670Sdavidn *ptr++ = '\0'; 13425670Sdavidn } 13525670Sdavidn res[i] = NULL; 13625670Sdavidn } 13721288Sdavidn } 13825670Sdavidn 13925670Sdavidn if (size) 14025670Sdavidn *size = i; 14125670Sdavidn 14225670Sdavidn return res; 14321288Sdavidn} 14421288Sdavidn 14525670Sdavidn 14621288Sdavidn/* 14721288Sdavidn * login_close() 14821288Sdavidn * Frees up all resources relating to a login class 14921288Sdavidn * 15021288Sdavidn */ 15121288Sdavidn 15221288Sdavidnvoid 15321288Sdavidnlogin_close(login_cap_t * lc) 15421288Sdavidn{ 15525670Sdavidn if (lc) { 15625670Sdavidn free(lc->lc_style); 15725670Sdavidn free(lc->lc_class); 15860746Shoek free(lc->lc_cap); 15925670Sdavidn free(lc); 16025670Sdavidn if (--lc_object_count == 0) { 16125670Sdavidn free(internal_string); 16225670Sdavidn free(internal_array); 16325670Sdavidn internal_array = NULL; 16425670Sdavidn internal_arraysz = 0; 16525670Sdavidn internal_string = NULL; 16625670Sdavidn internal_stringsz = 0; 16725670Sdavidn cgetclose(); 16825670Sdavidn } 16921288Sdavidn } 17021288Sdavidn} 17121288Sdavidn 17221288Sdavidn 17321288Sdavidn/* 174170713Syar * login_getclassbyname() 175170713Syar * Get the login class by its name. 17621288Sdavidn * If the name given is NULL or empty, the default class 177170713Syar * LOGIN_DEFCLASS (i.e., "default") is fetched. 178170713Syar * If the name given is LOGIN_MECLASS and 179129112Sdds * 'pwd' argument is non-NULL and contains an non-NULL 180129112Sdds * dir entry, then the file _FILE_LOGIN_CONF is picked 181129112Sdds * up from that directory and used before the system 182170713Syar * login database. In that case the system login database 183170713Syar * is looked up using LOGIN_MECLASS, too, which is a bug. 18421288Sdavidn * Return a filled-out login_cap_t structure, including 18521288Sdavidn * class name, and the capability record buffer. 18621288Sdavidn */ 18721288Sdavidn 18821288Sdavidnlogin_cap_t * 18925670Sdavidnlogin_getclassbyname(char const *name, const struct passwd *pwd) 19021288Sdavidn{ 19125670Sdavidn login_cap_t *lc; 19221288Sdavidn 19325670Sdavidn if ((lc = malloc(sizeof(login_cap_t))) != NULL) { 19483923Sache int r, me, i = 0; 19546004Sache uid_t euid = 0; 19646004Sache gid_t egid = 0; 19725670Sdavidn const char *msg = NULL; 19883923Sache const char *dir; 19925670Sdavidn char userpath[MAXPATHLEN]; 20021288Sdavidn 201121530Speter static char *login_dbarray[] = { NULL, NULL, NULL }; 20221288Sdavidn 20383923Sache me = (name != NULL && strcmp(name, LOGIN_MECLASS) == 0); 20483923Sache dir = (!me || pwd == NULL) ? NULL : pwd->pw_dir; 20583516Srwatson /* 20683516Srwatson * Switch to user mode before checking/reading its ~/.login_conf 20783516Srwatson * - some NFSes have root read access disabled. 20883516Srwatson * 20983516Srwatson * XXX: This fails to configure additional groups. 21083516Srwatson */ 21146004Sache if (dir) { 21246004Sache euid = geteuid(); 21346004Sache egid = getegid(); 21446004Sache (void)setegid(pwd->pw_gid); 21546004Sache (void)seteuid(pwd->pw_uid); 21646004Sache } 21746003Sache 21825670Sdavidn if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, 21925670Sdavidn _FILE_LOGIN_CONF) < MAXPATHLEN) { 22025670Sdavidn if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1) 221184633Sdes login_dbarray[i++] = userpath; 22225670Sdavidn } 223170713Syar /* 224170713Syar * XXX: Why to add the system database if the class is `me'? 225170713Syar */ 226184633Sdes if (_secure_path(path_login_conf, 0, 0) != -1) 227184633Sdes login_dbarray[i++] = path_login_conf; 22825670Sdavidn login_dbarray[i] = NULL; 22921288Sdavidn 23025670Sdavidn memset(lc, 0, sizeof(login_cap_t)); 23125670Sdavidn lc->lc_cap = lc->lc_class = lc->lc_style = NULL; 23225670Sdavidn 23325670Sdavidn if (name == NULL || *name == '\0') 23425670Sdavidn name = LOGIN_DEFCLASS; 23525670Sdavidn 236121193Smarkm switch (cgetent(&lc->lc_cap, login_dbarray, name)) { 23725670Sdavidn case -1: /* Failed, entry does not exist */ 23883923Sache if (me) 23925670Sdavidn break; /* Don't retry default on 'me' */ 24025670Sdavidn if (i == 0) 24125670Sdavidn r = -1; 242255007Sjilles else if ((r = open(login_dbarray[0], O_RDONLY | O_CLOEXEC)) >= 0) 24325670Sdavidn close(r); 24425670Sdavidn /* 24525670Sdavidn * If there's at least one login class database, 24625670Sdavidn * and we aren't searching for a default class 24725670Sdavidn * then complain about a non-existent class. 24825670Sdavidn */ 24925670Sdavidn if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0) 25025670Sdavidn syslog(LOG_ERR, "login_getclass: unknown class '%s'", name); 25125670Sdavidn /* fall-back to default class */ 25225670Sdavidn name = LOGIN_DEFCLASS; 25325670Sdavidn msg = "%s: no default/fallback class '%s'"; 254121193Smarkm if (cgetent(&lc->lc_cap, login_dbarray, name) != 0 && r >= 0) 25525670Sdavidn break; 256102411Scharnier /* FALLTHROUGH - just return system defaults */ 25725670Sdavidn case 0: /* success! */ 25825670Sdavidn if ((lc->lc_class = strdup(name)) != NULL) { 25946004Sache if (dir) { 26046004Sache (void)seteuid(euid); 26146004Sache (void)setegid(egid); 26246004Sache } 26325670Sdavidn ++lc_object_count; 26425670Sdavidn return lc; 26525670Sdavidn } 26625670Sdavidn msg = "%s: strdup: %m"; 26725670Sdavidn break; 26825670Sdavidn case -2: 26925670Sdavidn msg = "%s: retrieving class information: %m"; 27025670Sdavidn break; 27125670Sdavidn case -3: 27225670Sdavidn msg = "%s: 'tc=' reference loop '%s'"; 27325670Sdavidn break; 27425670Sdavidn case 1: 27525670Sdavidn msg = "couldn't resolve 'tc=' reference in '%s'"; 27625670Sdavidn break; 27725670Sdavidn default: 27825670Sdavidn msg = "%s: unexpected cgetent() error '%s': %m"; 27925670Sdavidn break; 28025670Sdavidn } 28146004Sache if (dir) { 28246004Sache (void)seteuid(euid); 28346004Sache (void)setegid(egid); 28446004Sache } 28525670Sdavidn if (msg != NULL) 28625670Sdavidn syslog(LOG_ERR, msg, "login_getclass", name); 28721288Sdavidn free(lc); 28821288Sdavidn } 28921288Sdavidn 29025670Sdavidn return NULL; 29121288Sdavidn} 29221288Sdavidn 29321288Sdavidn 29421288Sdavidn 29521288Sdavidn/* 29621288Sdavidn * login_getclass() 29725670Sdavidn * Get the login class for the system (only) login class database. 29825670Sdavidn * Return a filled-out login_cap_t structure, including 29925670Sdavidn * class name, and the capability record buffer. 30025670Sdavidn */ 30125670Sdavidn 30225670Sdavidnlogin_cap_t * 30325670Sdavidnlogin_getclass(const char *cls) 30425670Sdavidn{ 30525670Sdavidn return login_getclassbyname(cls, NULL); 30625670Sdavidn} 30725670Sdavidn 30825670Sdavidn 30925670Sdavidn/* 310170713Syar * login_getpwclass() 31121288Sdavidn * Get the login class for a given password entry from 31221288Sdavidn * the system (only) login class database. 31321288Sdavidn * If the password entry's class field is not set, or 31421288Sdavidn * the class specified does not exist, then use the 315170713Syar * default of LOGIN_DEFCLASS (i.e., "default") for an unprivileged 316170713Syar * user or that of LOGIN_DEFROOTCLASS (i.e., "root") for a super-user. 31721288Sdavidn * Return a filled-out login_cap_t structure, including 31821288Sdavidn * class name, and the capability record buffer. 31921288Sdavidn */ 32021288Sdavidn 32121288Sdavidnlogin_cap_t * 32225670Sdavidnlogin_getpwclass(const struct passwd *pwd) 32321288Sdavidn{ 32425670Sdavidn const char *cls = NULL; 32525670Sdavidn 32625670Sdavidn if (pwd != NULL) { 32725670Sdavidn cls = pwd->pw_class; 32825670Sdavidn if (cls == NULL || *cls == '\0') 32925670Sdavidn cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS; 33025670Sdavidn } 331170713Syar /* 332170713Syar * XXX: pwd should be unused by login_getclassbyname() unless cls is `me', 333170713Syar * so NULL can be passed instead of pwd for more safety. 334170713Syar */ 33525670Sdavidn return login_getclassbyname(cls, pwd); 33621288Sdavidn} 33721288Sdavidn 33821288Sdavidn 33921288Sdavidn/* 34021288Sdavidn * login_getuserclass() 341170713Syar * Get the `me' login class, allowing user overrides via ~/.login_conf. 342170713Syar * Note that user overrides are allowed only in the `me' class. 34321288Sdavidn */ 34421288Sdavidn 34521288Sdavidnlogin_cap_t * 34621288Sdavidnlogin_getuserclass(const struct passwd *pwd) 34721288Sdavidn{ 34825670Sdavidn return login_getclassbyname(LOGIN_MECLASS, pwd); 34921288Sdavidn} 35021288Sdavidn 35121288Sdavidn 35221288Sdavidn/* 35321288Sdavidn * login_getcapstr() 35421288Sdavidn * Given a login_cap entry, and a capability name, return the 355145426Strhodes * value defined for that capability, a default if not found, or 35621288Sdavidn * an error string on error. 35721288Sdavidn */ 35821288Sdavidn 35994202Sruconst char * 36094202Srulogin_getcapstr(login_cap_t *lc, const char *cap, const char *def, const char *error) 36121288Sdavidn{ 36225670Sdavidn char *res; 36325670Sdavidn int ret; 36421288Sdavidn 36525670Sdavidn if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0') 36625670Sdavidn return def; 36721288Sdavidn 368121193Smarkm if ((ret = cgetstr(lc->lc_cap, cap, &res)) == -1) 36925670Sdavidn return def; 37025670Sdavidn return (ret >= 0) ? res : error; 37121288Sdavidn} 37221288Sdavidn 37321288Sdavidn 37421288Sdavidn/* 37521288Sdavidn * login_getcaplist() 37621288Sdavidn * Given a login_cap entry, and a capability name, return the 37721288Sdavidn * value defined for that capability split into an array of 37821288Sdavidn * strings. 37921288Sdavidn */ 38021288Sdavidn 381121193Smarkmconst char ** 38225670Sdavidnlogin_getcaplist(login_cap_t *lc, const char *cap, const char *chars) 38321288Sdavidn{ 384121193Smarkm const char *lstring; 38521288Sdavidn 38625670Sdavidn if (chars == NULL) 38725670Sdavidn chars = ", \t"; 388121193Smarkm if ((lstring = login_getcapstr(lc, cap, NULL, NULL)) != NULL) 38925670Sdavidn return arrayize(lstring, chars, NULL); 39025670Sdavidn return NULL; 39121288Sdavidn} 39221288Sdavidn 39321288Sdavidn 39421288Sdavidn/* 39521288Sdavidn * login_getpath() 39621288Sdavidn * From the login_cap_t <lc>, get the capability <cap> which is 39721288Sdavidn * formatted as either a space or comma delimited list of paths 39821288Sdavidn * and append them all into a string and separate by semicolons. 39921288Sdavidn * If there is an error of any kind, return <error>. 40021288Sdavidn */ 40121288Sdavidn 40294202Sruconst char * 40394202Srulogin_getpath(login_cap_t *lc, const char *cap, const char *error) 40421288Sdavidn{ 40594202Sru const char *str; 406121193Smarkm char *ptr; 407121193Smarkm int count; 40821288Sdavidn 409121193Smarkm str = login_getcapstr(lc, cap, NULL, NULL); 410121193Smarkm if (str == NULL) 411121193Smarkm return error; 412121193Smarkm ptr = __DECONST(char *, str); /* XXXX Yes, very dodgy */ 413121193Smarkm while (*ptr) { 414121193Smarkm count = strcspn(ptr, ", \t"); 415121193Smarkm ptr += count; 416121193Smarkm if (*ptr) 417121193Smarkm *ptr++ = ':'; 41821288Sdavidn } 41925670Sdavidn return str; 42021288Sdavidn} 42121288Sdavidn 42221288Sdavidn 42325670Sdavidnstatic int 42425670Sdavidnisinfinite(const char *s) 42525670Sdavidn{ 42625670Sdavidn static const char *infs[] = { 42725670Sdavidn "infinity", 42825670Sdavidn "inf", 42925670Sdavidn "unlimited", 43025670Sdavidn "unlimit", 43125670Sdavidn "-1", 43225670Sdavidn NULL 43325670Sdavidn }; 43425670Sdavidn const char **i = &infs[0]; 43525670Sdavidn 43625670Sdavidn while (*i != NULL) { 43725670Sdavidn if (strcasecmp(s, *i) == 0) 43825670Sdavidn return 1; 43925670Sdavidn ++i; 44025670Sdavidn } 44125670Sdavidn return 0; 44225670Sdavidn} 44325670Sdavidn 44425670Sdavidn 44525670Sdavidnstatic u_quad_t 44625670Sdavidnrmultiply(u_quad_t n1, u_quad_t n2) 44725670Sdavidn{ 44825670Sdavidn u_quad_t m, r; 44925670Sdavidn int b1, b2; 45025670Sdavidn 45125670Sdavidn static int bpw = 0; 45225670Sdavidn 45325670Sdavidn /* Handle simple cases */ 45425670Sdavidn if (n1 == 0 || n2 == 0) 45525670Sdavidn return 0; 45625670Sdavidn if (n1 == 1) 45725670Sdavidn return n2; 45825670Sdavidn if (n2 == 1) 45925670Sdavidn return n1; 46025670Sdavidn 46125670Sdavidn /* 46225670Sdavidn * sizeof() returns number of bytes needed for storage. 46325670Sdavidn * This may be different from the actual number of useful bits. 46425670Sdavidn */ 46525670Sdavidn if (!bpw) { 46625670Sdavidn bpw = sizeof(u_quad_t) * 8; 46725670Sdavidn while (((u_quad_t)1 << (bpw-1)) == 0) 46825670Sdavidn --bpw; 46925670Sdavidn } 47025670Sdavidn 47125670Sdavidn /* 47225670Sdavidn * First check the magnitude of each number. If the sum of the 47325670Sdavidn * magnatude is way to high, reject the number. (If this test 47425670Sdavidn * is not done then the first multiply below may overflow.) 47525670Sdavidn */ 47625670Sdavidn for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1) 47725670Sdavidn ; 47825670Sdavidn for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2) 47925670Sdavidn ; 48025670Sdavidn if (b1 + b2 - 2 > bpw) { 48125670Sdavidn errno = ERANGE; 48225670Sdavidn return (UQUAD_MAX); 48325670Sdavidn } 48425670Sdavidn 48525670Sdavidn /* 48625670Sdavidn * Decompose the multiplication to be: 48725670Sdavidn * h1 = n1 & ~1 48825670Sdavidn * h2 = n2 & ~1 48925670Sdavidn * l1 = n1 & 1 49025670Sdavidn * l2 = n2 & 1 49125670Sdavidn * (h1 + l1) * (h2 + l2) 49225670Sdavidn * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2) 49325670Sdavidn * 49425670Sdavidn * Since h1 && h2 do not have the low bit set, we can then say: 49525670Sdavidn * 49625670Sdavidn * (h1>>1 * h2>>1 * 4) + ... 49725670Sdavidn * 49825670Sdavidn * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will 49925670Sdavidn * overflow. 50025670Sdavidn * 50125670Sdavidn * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2) 50225670Sdavidn * then adding in residual amout will cause an overflow. 50325670Sdavidn */ 50425670Sdavidn 50525670Sdavidn m = (n1 >> 1) * (n2 >> 1); 50625670Sdavidn if (m >= ((u_quad_t)1 << (bpw-2))) { 50725670Sdavidn errno = ERANGE; 50825670Sdavidn return (UQUAD_MAX); 50925670Sdavidn } 51025670Sdavidn m *= 4; 51125670Sdavidn 51225670Sdavidn r = (n1 & n2 & 1) 51325670Sdavidn + (n2 & 1) * (n1 & ~(u_quad_t)1) 51425670Sdavidn + (n1 & 1) * (n2 & ~(u_quad_t)1); 51525670Sdavidn 51625670Sdavidn if ((u_quad_t)(m + r) < m) { 51725670Sdavidn errno = ERANGE; 51825670Sdavidn return (UQUAD_MAX); 51925670Sdavidn } 52025670Sdavidn m += r; 52125670Sdavidn 52225670Sdavidn return (m); 52325670Sdavidn} 52425670Sdavidn 52525670Sdavidn 52621288Sdavidn/* 52721288Sdavidn * login_getcaptime() 52821288Sdavidn * From the login_cap_t <lc>, get the capability <cap>, which is 52921288Sdavidn * formatted as a time (e.g., "<cap>=10h3m2s"). If <cap> is not 53021288Sdavidn * present in <lc>, return <def>; if there is an error of some kind, 53121288Sdavidn * return <error>. 53221288Sdavidn */ 53321288Sdavidn 53421288Sdavidnrlim_t 53521288Sdavidnlogin_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 53621288Sdavidn{ 53725670Sdavidn char *res, *ep, *oval; 53825670Sdavidn int r; 53925670Sdavidn rlim_t tot; 54021288Sdavidn 54125670Sdavidn errno = 0; 54225670Sdavidn if (lc == NULL || lc->lc_cap == NULL) 54325670Sdavidn return def; 54421288Sdavidn 54525670Sdavidn /* 54625670Sdavidn * Look for <cap> in lc_cap. 54725670Sdavidn * If it's not there (-1), return <def>. 54825670Sdavidn * If there's an error, return <error>. 54925670Sdavidn */ 55021288Sdavidn 551121193Smarkm if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) 55225670Sdavidn return def; 55325670Sdavidn else if (r < 0) { 55425670Sdavidn errno = ERANGE; 55525670Sdavidn return error; 55625670Sdavidn } 55721288Sdavidn 55825670Sdavidn /* "inf" and "infinity" are special cases */ 55925670Sdavidn if (isinfinite(res)) 56025670Sdavidn return RLIM_INFINITY; 56121288Sdavidn 56225670Sdavidn /* 56325670Sdavidn * Now go through the string, turning something like 1h2m3s into 56425670Sdavidn * an integral value. Whee. 56525670Sdavidn */ 56621288Sdavidn 56725670Sdavidn errno = 0; 56825670Sdavidn tot = 0; 56925670Sdavidn oval = res; 57025670Sdavidn while (*res) { 57125670Sdavidn rlim_t tim = strtoq(res, &ep, 0); 57225670Sdavidn rlim_t mult = 1; 57325670Sdavidn 57425670Sdavidn if (ep == NULL || ep == res || errno != 0) { 57525670Sdavidn invalid: 57625670Sdavidn syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s", 57725670Sdavidn lc->lc_class, cap, oval); 57825670Sdavidn errno = ERANGE; 57925670Sdavidn return error; 58025670Sdavidn } 58125670Sdavidn /* Look for suffixes */ 58225670Sdavidn switch (*ep++) { 58325670Sdavidn case 0: 58425670Sdavidn ep--; 58525670Sdavidn break; /* end of string */ 58625670Sdavidn case 's': case 'S': /* seconds */ 58725670Sdavidn break; 58825670Sdavidn case 'm': case 'M': /* minutes */ 58925670Sdavidn mult = 60; 59025670Sdavidn break; 59125670Sdavidn case 'h': case 'H': /* hours */ 59225670Sdavidn mult = 60L * 60L; 59325670Sdavidn break; 59425670Sdavidn case 'd': case 'D': /* days */ 59525670Sdavidn mult = 60L * 60L * 24L; 59625670Sdavidn break; 59725670Sdavidn case 'w': case 'W': /* weeks */ 59825670Sdavidn mult = 60L * 60L * 24L * 7L; 59926621Sdavidn break; 60025670Sdavidn case 'y': case 'Y': /* 365-day years */ 60125670Sdavidn mult = 60L * 60L * 24L * 365L; 60226621Sdavidn break; 60325670Sdavidn default: 60425670Sdavidn goto invalid; 60525670Sdavidn } 60625670Sdavidn res = ep; 60725670Sdavidn tot += rmultiply(tim, mult); 60825670Sdavidn if (errno) 60925670Sdavidn goto invalid; 61021288Sdavidn } 61125670Sdavidn 61225670Sdavidn return tot; 61321288Sdavidn} 61421288Sdavidn 61521288Sdavidn 61621288Sdavidn/* 61721288Sdavidn * login_getcapnum() 61821288Sdavidn * From the login_cap_t <lc>, extract the numerical value <cap>. 61921288Sdavidn * If it is not present, return <def> for a default, and return 62021288Sdavidn * <error> if there is an error. 62121288Sdavidn * Like login_getcaptime(), only it only converts to a number, not 62221288Sdavidn * to a time; "infinity" and "inf" are 'special.' 62321288Sdavidn */ 62421288Sdavidn 62521288Sdavidnrlim_t 62621288Sdavidnlogin_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 62721288Sdavidn{ 62825670Sdavidn char *ep, *res; 62925670Sdavidn int r; 63025670Sdavidn rlim_t val; 63121288Sdavidn 63225670Sdavidn if (lc == NULL || lc->lc_cap == NULL) 63325670Sdavidn return def; 63421288Sdavidn 63521288Sdavidn /* 63625670Sdavidn * For BSDI compatibility, try for the tag=<val> first 63721288Sdavidn */ 638121193Smarkm if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) { 63925670Sdavidn long lval; 64025670Sdavidn /* string capability not present, so try for tag#<val> as numeric */ 641121193Smarkm if ((r = cgetnum(lc->lc_cap, cap, &lval)) == -1) 64225670Sdavidn return def; /* Not there, so return default */ 64325670Sdavidn else if (r >= 0) 64425670Sdavidn return (rlim_t)lval; 64525670Sdavidn } 64621288Sdavidn 64725670Sdavidn if (r < 0) { 64825670Sdavidn errno = ERANGE; 64925670Sdavidn return error; 65025670Sdavidn } 65121288Sdavidn 65225670Sdavidn if (isinfinite(res)) 65325670Sdavidn return RLIM_INFINITY; 65425670Sdavidn 65525670Sdavidn errno = 0; 65625670Sdavidn val = strtoq(res, &ep, 0); 65725670Sdavidn if (ep == NULL || ep == res || errno != 0) { 65825670Sdavidn syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s", 65925670Sdavidn lc->lc_class, cap, res); 66025670Sdavidn errno = ERANGE; 66125670Sdavidn return error; 66225670Sdavidn } 66325670Sdavidn 66425670Sdavidn return val; 66521288Sdavidn} 66621288Sdavidn 66721288Sdavidn 66825670Sdavidn 66921288Sdavidn/* 67021288Sdavidn * login_getcapsize() 67121288Sdavidn * From the login_cap_t <lc>, extract the capability <cap>, which is 67221288Sdavidn * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity". 67321288Sdavidn * If not present, return <def>, or <error> if there is an error of 67421288Sdavidn * some sort. 67521288Sdavidn */ 67621288Sdavidn 67721288Sdavidnrlim_t 67825670Sdavidnlogin_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 67925670Sdavidn{ 68025670Sdavidn char *ep, *res, *oval; 68125670Sdavidn int r; 68225670Sdavidn rlim_t tot; 68321288Sdavidn 68425670Sdavidn if (lc == NULL || lc->lc_cap == NULL) 68525670Sdavidn return def; 68621288Sdavidn 687121193Smarkm if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) 68825670Sdavidn return def; 68925670Sdavidn else if (r < 0) { 69025670Sdavidn errno = ERANGE; 69125670Sdavidn return error; 69225670Sdavidn } 69321288Sdavidn 69425670Sdavidn if (isinfinite(res)) 69525670Sdavidn return RLIM_INFINITY; 69623144Sache 69725670Sdavidn errno = 0; 69825670Sdavidn tot = 0; 69925670Sdavidn oval = res; 70025670Sdavidn while (*res) { 70125670Sdavidn rlim_t siz = strtoq(res, &ep, 0); 70225670Sdavidn rlim_t mult = 1; 70325670Sdavidn 70425670Sdavidn if (ep == NULL || ep == res || errno != 0) { 70525670Sdavidn invalid: 70625670Sdavidn syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s", 70725670Sdavidn lc->lc_class, cap, oval); 70825670Sdavidn errno = ERANGE; 70925670Sdavidn return error; 71025670Sdavidn } 71125670Sdavidn switch (*ep++) { 71225670Sdavidn case 0: /* end of string */ 71325670Sdavidn ep--; 71425670Sdavidn break; 71525670Sdavidn case 'b': case 'B': /* 512-byte blocks */ 71625670Sdavidn mult = 512; 71725670Sdavidn break; 71825670Sdavidn case 'k': case 'K': /* 1024-byte Kilobytes */ 71925670Sdavidn mult = 1024; 72025670Sdavidn break; 72125670Sdavidn case 'm': case 'M': /* 1024-k kbytes */ 72225670Sdavidn mult = 1024 * 1024; 72325670Sdavidn break; 72425670Sdavidn case 'g': case 'G': /* 1Gbyte */ 72525670Sdavidn mult = 1024 * 1024 * 1024; 72625670Sdavidn break; 72725670Sdavidn case 't': case 'T': /* 1TBte */ 72825670Sdavidn mult = 1024LL * 1024LL * 1024LL * 1024LL; 72925670Sdavidn break; 73025670Sdavidn default: 73125670Sdavidn goto invalid; 73225670Sdavidn } 73325670Sdavidn res = ep; 73425670Sdavidn tot += rmultiply(siz, mult); 73525670Sdavidn if (errno) 73625670Sdavidn goto invalid; 73722085Sdavidn } 73825670Sdavidn 73925670Sdavidn return tot; 74021288Sdavidn} 74121288Sdavidn 74221288Sdavidn 74321288Sdavidn/* 74421288Sdavidn * login_getcapbool() 74521288Sdavidn * From the login_cap_t <lc>, check for the existance of the capability 74621288Sdavidn * of <cap>. Return <def> if <lc>->lc_cap is NULL, otherwise return 74721288Sdavidn * the whether or not <cap> exists there. 74821288Sdavidn */ 74921288Sdavidn 75021288Sdavidnint 75121288Sdavidnlogin_getcapbool(login_cap_t *lc, const char *cap, int def) 75221288Sdavidn{ 75325670Sdavidn if (lc == NULL || lc->lc_cap == NULL) 75425670Sdavidn return def; 755121193Smarkm return (cgetcap(lc->lc_cap, cap, ':') != NULL); 75621288Sdavidn} 75721288Sdavidn 75821288Sdavidn 75921288Sdavidn/* 76021288Sdavidn * login_getstyle() 76121288Sdavidn * Given a login_cap entry <lc>, and optionally a type of auth <auth>, 76221288Sdavidn * and optionally a style <style>, find the style that best suits these 76321288Sdavidn * rules: 76421288Sdavidn * 1. If <auth> is non-null, look for an "auth-<auth>=" string 76521288Sdavidn * in the capability; if not present, default to "auth=". 76621288Sdavidn * 2. If there is no auth list found from (1), default to 76721288Sdavidn * "passwd" as an authorization list. 76821288Sdavidn * 3. If <style> is non-null, look for <style> in the list of 76921288Sdavidn * authorization methods found from (2); if <style> is NULL, default 77021288Sdavidn * to LOGIN_DEFSTYLE ("passwd"). 77121288Sdavidn * 4. If the chosen style is found in the chosen list of authorization 77221288Sdavidn * methods, return that; otherwise, return NULL. 77321288Sdavidn * E.g.: 77421288Sdavidn * login_getstyle(lc, NULL, "ftp"); 77521288Sdavidn * login_getstyle(lc, "login", NULL); 77621288Sdavidn * login_getstyle(lc, "skey", "network"); 77721288Sdavidn */ 77821288Sdavidn 77994202Sruconst char * 78094202Srulogin_getstyle(login_cap_t *lc, const char *style, const char *auth) 78121288Sdavidn{ 78225670Sdavidn int i; 783121193Smarkm const char **authtypes = NULL; 78425670Sdavidn char *auths= NULL; 78525670Sdavidn char realauth[64]; 78621288Sdavidn 787121193Smarkm static const char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL }; 78821288Sdavidn 78925670Sdavidn if (auth != NULL && *auth != '\0') { 790121193Smarkm if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < (int)sizeof(realauth)) 79125670Sdavidn authtypes = login_getcaplist(lc, realauth, NULL); 79225670Sdavidn } 79321288Sdavidn 79425670Sdavidn if (authtypes == NULL) 79525670Sdavidn authtypes = login_getcaplist(lc, "auth", NULL); 79621288Sdavidn 79725670Sdavidn if (authtypes == NULL) 79825670Sdavidn authtypes = defauthtypes; 79921288Sdavidn 80025670Sdavidn /* 80172089Sasmodai * We have at least one authtype now; auths is a comma-separated 80225670Sdavidn * (or space-separated) list of authentication types. We have to 80325670Sdavidn * convert from this to an array of char*'s; authtypes then gets this. 80425670Sdavidn */ 80525670Sdavidn i = 0; 80625670Sdavidn if (style != NULL && *style != '\0') { 80725670Sdavidn while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0) 80825670Sdavidn i++; 80925670Sdavidn } 81021288Sdavidn 81125670Sdavidn lc->lc_style = NULL; 81225670Sdavidn if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL) 81325670Sdavidn lc->lc_style = auths; 81421288Sdavidn 81525670Sdavidn if (lc->lc_style != NULL) 81625670Sdavidn lc->lc_style = strdup(lc->lc_style); 81721288Sdavidn 81825670Sdavidn return lc->lc_style; 81925670Sdavidn} 820