/*- * Copyright (c) 1996 by * Sean Eric Fagan * David Nugent * All rights reserved. * * Portions copyright (c) 1995,1997 * Berkeley Software Design, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. This work was done expressly for inclusion into FreeBSD. Other use * is permitted provided this notation is included. * 4. Absolutely no warranty of function or purpose is made by the authors. * 5. Modifications may be freely made to this file providing the above * conditions are met. * * Low-level routines relating to the user capabilities database * * $FreeBSD: src/lib/libutil/login_cap.c,v 1.17.2.4 2001/10/21 19:42:13 ache Exp $ * $DragonFly: src/lib/libutil/login_cap.c,v 1.4 2008/10/29 22:03:12 swildner Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "libutil.h" #include "login_cap.h" /* * allocstr() * Manage a single static pointer for handling a local char* buffer, * resizing as necessary to contain the string. * * allocarray() * Manage a static array for handling a group of strings, resizing * when necessary. */ static int lc_object_count = 0; static size_t internal_stringsz = 0; static char * internal_string = NULL; static size_t internal_arraysz = 0; static char ** internal_array = NULL; static char * allocstr(char *str) { char *p; size_t sz = strlen(str) + 1; /* realloc() only if necessary */ if (sz <= internal_stringsz) p = strcpy(internal_string, str); else if ((p = realloc(internal_string, sz)) != NULL) { internal_stringsz = sz; internal_string = strcpy(p, str); } return p; } static char ** allocarray(size_t sz) { char **p; if (sz <= internal_arraysz) p = internal_array; else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) { internal_arraysz = sz; internal_array = p; } return p; } /* * arrayize() * Turn a simple string separated by any of * the set of into an array. The last element * of the array will be NULL, as is proper. * Free using freearraystr() */ static char ** arrayize(char *str, const char *chars, int *size) { int i; char *ptr; char **res = NULL; /* count the sub-strings */ for (i = 0, ptr = str; *ptr; i++) { int count = strcspn(ptr, chars); ptr += count; if (*ptr) ++ptr; } /* alloc the array */ if ((ptr = allocstr(str)) != NULL) { if ((res = allocarray(++i)) == NULL) free(str); else { /* now split the string */ i = 0; while (*ptr) { int count = strcspn(ptr, chars); res[i++] = ptr; ptr += count; if (*ptr) *ptr++ = '\0'; } res[i] = NULL; } } if (size) *size = i; return res; } /* * login_close() * Frees up all resources relating to a login class * */ void login_close(login_cap_t * lc) { if (lc) { free(lc->lc_style); free(lc->lc_class); free(lc->lc_cap); free(lc); if (--lc_object_count == 0) { free(internal_string); free(internal_array); internal_array = NULL; internal_arraysz = 0; internal_string = NULL; internal_stringsz = 0; cgetclose(); } } } /* * login_getclassbyname() get the login class by its name. * If the name given is NULL or empty, the default class * LOGIN_DEFCLASS (ie. "default") is fetched. If the * 'dir' argument contains a non-NULL non-empty string, * then the file _FILE_LOGIN_CONF is picked up from that * directory instead of the system login database. * Return a filled-out login_cap_t structure, including * class name, and the capability record buffer. */ login_cap_t * login_getclassbyname(char const *name, const struct passwd *pwd) { login_cap_t *lc; if ((lc = malloc(sizeof(login_cap_t))) != NULL) { int r, me, i = 0; uid_t euid = 0; gid_t egid = 0; const char *msg = NULL; const char *dir; char userpath[MAXPATHLEN]; static char *login_dbarray[] = { NULL, NULL, NULL }; me = (name != NULL && strcmp(name, LOGIN_MECLASS) == 0); dir = (!me || pwd == NULL) ? NULL : pwd->pw_dir; /* * Switch to user mode before checking/reading its ~/.login_conf * - some NFSes have root read access disabled. * * XXX: This fails to configure additional groups. */ if (dir) { euid = geteuid(); egid = getegid(); (void)setegid(pwd->pw_gid); (void)seteuid(pwd->pw_uid); } if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, _FILE_LOGIN_CONF) < MAXPATHLEN) { login_dbarray[i] = userpath; if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1) i++; /* only use 'secure' data */ } if (_secure_path(_PATH_LOGIN_CONF, 0, 0) != -1) login_dbarray[i++] = _PATH_LOGIN_CONF; login_dbarray[i] = NULL; memset(lc, 0, sizeof(login_cap_t)); lc->lc_cap = lc->lc_class = lc->lc_style = NULL; if (name == NULL || *name == '\0') name = LOGIN_DEFCLASS; switch (cgetent(&lc->lc_cap, login_dbarray, (char*)name)) { case -1: /* Failed, entry does not exist */ if (me) break; /* Don't retry default on 'me' */ if (i == 0) r = -1; else if ((r = open(login_dbarray[0], O_RDONLY)) >= 0) close(r); /* * If there's at least one login class database, * and we aren't searching for a default class * then complain about a non-existent class. */ if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0) syslog(LOG_ERR, "login_getclass: unknown class '%s'", name); /* fall-back to default class */ name = LOGIN_DEFCLASS; msg = "%s: no default/fallback class '%s'"; if (cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0 && r >= 0) break; /* Fallthru - just return system defaults */ case 0: /* success! */ if ((lc->lc_class = strdup(name)) != NULL) { if (dir) { (void)seteuid(euid); (void)setegid(egid); } ++lc_object_count; return lc; } msg = "%s: strdup: %m"; break; case -2: msg = "%s: retrieving class information: %m"; break; case -3: msg = "%s: 'tc=' reference loop '%s'"; break; case 1: msg = "couldn't resolve 'tc=' reference in '%s'"; break; default: msg = "%s: unexpected cgetent() error '%s': %m"; break; } if (dir) { (void)seteuid(euid); (void)setegid(egid); } if (msg != NULL) syslog(LOG_ERR, msg, "login_getclass", name); free(lc); } return NULL; } /* * login_getclass() * Get the login class for the system (only) login class database. * Return a filled-out login_cap_t structure, including * class name, and the capability record buffer. */ login_cap_t * login_getclass(const char *cls) { return login_getclassbyname(cls, NULL); } /* * login_getclass() * Get the login class for a given password entry from * the system (only) login class database. * If the password entry's class field is not set, or * the class specified does not exist, then use the * default of LOGIN_DEFCLASS (ie. "default"). * Return a filled-out login_cap_t structure, including * class name, and the capability record buffer. */ login_cap_t * login_getpwclass(const struct passwd *pwd) { const char *cls = NULL; if (pwd != NULL) { cls = pwd->pw_class; if (cls == NULL || *cls == '\0') cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS; } return login_getclassbyname(cls, pwd); } /* * login_getuserclass() * Get the login class for a given password entry, allowing user * overrides via ~/.login_conf. */ login_cap_t * login_getuserclass(const struct passwd *pwd) { return login_getclassbyname(LOGIN_MECLASS, pwd); } /* * login_getcapstr() * Given a login_cap entry, and a capability name, return the * value defined for that capability, a defualt if not found, or * an error string on error. */ char * login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *error) { char *res; int ret; if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0') return def; if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) return def; return (ret >= 0) ? res : error; } /* * login_getcaplist() * Given a login_cap entry, and a capability name, return the * value defined for that capability split into an array of * strings. */ char ** login_getcaplist(login_cap_t *lc, const char *cap, const char *chars) { char *lstring; if (chars == NULL) chars = ", \t"; if ((lstring = login_getcapstr(lc, (char*)cap, NULL, NULL)) != NULL) return arrayize(lstring, chars, NULL); return NULL; } /* * login_getpath() * From the login_cap_t , get the capability which is * formatted as either a space or comma delimited list of paths * and append them all into a string and separate by semicolons. * If there is an error of any kind, return . */ char * login_getpath(login_cap_t *lc, const char *cap, char * error) { char *str; if ((str = login_getcapstr(lc, (char*)cap, NULL, NULL)) == NULL) str = error; else { char *ptr = str; while (*ptr) { int count = strcspn(ptr, ", \t"); ptr += count; if (*ptr) *ptr++ = ':'; } } return str; } static int isinfinite(const char *s) { static const char *infs[] = { "infinity", "inf", "unlimited", "unlimit", "-1", NULL }; const char **i = &infs[0]; while (*i != NULL) { if (strcasecmp(s, *i) == 0) return 1; ++i; } return 0; } static u_quad_t rmultiply(u_quad_t n1, u_quad_t n2) { u_quad_t m, r; int b1, b2; static int bpw = 0; /* Handle simple cases */ if (n1 == 0 || n2 == 0) return 0; if (n1 == 1) return n2; if (n2 == 1) return n1; /* * sizeof() returns number of bytes needed for storage. * This may be different from the actual number of useful bits. */ if (!bpw) { bpw = sizeof(u_quad_t) * 8; while (((u_quad_t)1 << (bpw-1)) == 0) --bpw; } /* * First check the magnitude of each number. If the sum of the * magnatude is way to high, reject the number. (If this test * is not done then the first multiply below may overflow.) */ for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1) ; for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2) ; if (b1 + b2 - 2 > bpw) { errno = ERANGE; return (UQUAD_MAX); } /* * Decompose the multiplication to be: * h1 = n1 & ~1 * h2 = n2 & ~1 * l1 = n1 & 1 * l2 = n2 & 1 * (h1 + l1) * (h2 + l2) * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2) * * Since h1 && h2 do not have the low bit set, we can then say: * * (h1>>1 * h2>>1 * 4) + ... * * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will * overflow. * * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2) * then adding in residual amout will cause an overflow. */ m = (n1 >> 1) * (n2 >> 1); if (m >= ((u_quad_t)1 << (bpw-2))) { errno = ERANGE; return (UQUAD_MAX); } m *= 4; r = (n1 & n2 & 1) + (n2 & 1) * (n1 & ~(u_quad_t)1) + (n1 & 1) * (n2 & ~(u_quad_t)1); if ((u_quad_t)(m + r) < m) { errno = ERANGE; return (UQUAD_MAX); } m += r; return (m); } /* * login_getcaptime() * From the login_cap_t , get the capability , which is * formatted as a time (e.g., "=10h3m2s"). If is not * present in , return ; if there is an error of some kind, * return . */ rlim_t login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) { char *res, *ep, *oval; int r; rlim_t tot; errno = 0; if (lc == NULL || lc->lc_cap == NULL) return def; /* * Look for in lc_cap. * If it's not there (-1), return . * If there's an error, return . */ if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) return def; else if (r < 0) { errno = ERANGE; return error; } /* "inf" and "infinity" are special cases */ if (isinfinite(res)) return RLIM_INFINITY; /* * Now go through the string, turning something like 1h2m3s into * an integral value. Whee. */ errno = 0; tot = 0; oval = res; while (*res) { rlim_t tim = strtoq(res, &ep, 0); rlim_t mult = 1; if (ep == NULL || ep == res || errno != 0) { invalid: syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s", lc->lc_class, cap, oval); errno = ERANGE; return error; } /* Look for suffixes */ switch (*ep++) { case 0: ep--; break; /* end of string */ case 's': case 'S': /* seconds */ break; case 'm': case 'M': /* minutes */ mult = 60; break; case 'h': case 'H': /* hours */ mult = 60L * 60L; break; case 'd': case 'D': /* days */ mult = 60L * 60L * 24L; break; case 'w': case 'W': /* weeks */ mult = 60L * 60L * 24L * 7L; break; case 'y': case 'Y': /* 365-day years */ mult = 60L * 60L * 24L * 365L; break; default: goto invalid; } res = ep; tot += rmultiply(tim, mult); if (errno) goto invalid; } return tot; } /* * login_getcapnum() * From the login_cap_t , extract the numerical value . * If it is not present, return for a default, and return * if there is an error. * Like login_getcaptime(), only it only converts to a number, not * to a time; "infinity" and "inf" are 'special.' */ rlim_t login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) { char *ep, *res; int r; rlim_t val; if (lc == NULL || lc->lc_cap == NULL) return def; /* * For BSDI compatibility, try for the tag= first */ if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) { long lval; /* string capability not present, so try for tag# as numeric */ if ((r = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1) return def; /* Not there, so return default */ else if (r >= 0) return (rlim_t)lval; } if (r < 0) { errno = ERANGE; return error; } if (isinfinite(res)) return RLIM_INFINITY; errno = 0; val = strtoq(res, &ep, 0); if (ep == NULL || ep == res || errno != 0) { syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s", lc->lc_class, cap, res); errno = ERANGE; return error; } return val; } /* * login_getcapsize() * From the login_cap_t , extract the capability , which is * formatted as a size (e.g., "=10M"); it can also be "infinity". * If not present, return , or if there is an error of * some sort. */ rlim_t login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) { char *ep, *res, *oval; int r; rlim_t tot; if (lc == NULL || lc->lc_cap == NULL) return def; if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) return def; else if (r < 0) { errno = ERANGE; return error; } if (isinfinite(res)) return RLIM_INFINITY; errno = 0; tot = 0; oval = res; while (*res) { rlim_t siz = strtoq(res, &ep, 0); rlim_t mult = 1; if (ep == NULL || ep == res || errno != 0) { invalid: syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s", lc->lc_class, cap, oval); errno = ERANGE; return error; } switch (*ep++) { case 0: /* end of string */ ep--; break; case 'b': case 'B': /* 512-byte blocks */ mult = 512; break; case 'k': case 'K': /* 1024-byte Kilobytes */ mult = 1024; break; case 'm': case 'M': /* 1024-k kbytes */ mult = 1024 * 1024; break; case 'g': case 'G': /* 1Gbyte */ mult = 1024 * 1024 * 1024; break; case 't': case 'T': /* 1TBte */ mult = 1024LL * 1024LL * 1024LL * 1024LL; break; default: goto invalid; } res = ep; tot += rmultiply(siz, mult); if (errno) goto invalid; } return tot; } /* * login_getcapbool() * From the login_cap_t , check for the existance of the capability * of . Return if ->lc_cap is NULL, otherwise return * the whether or not exists there. */ int login_getcapbool(login_cap_t *lc, const char *cap, int def) { if (lc == NULL || lc->lc_cap == NULL) return def; return (cgetcap(lc->lc_cap, (char *)cap, ':') != NULL); } /* * login_getstyle() * Given a login_cap entry , and optionally a type of auth , * and optionally a style