/*- * Copyright (c) 1996 by * Sean Eric Fagan * David Nugent * 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 * * $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #ifdef RLIM_LONG # define STRTOV strtol #else # define STRTOV strtoq #endif 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 seperated 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; for (i = 0, ptr = str; *ptr; i++) { int count = strcspn(ptr, chars); ptr += count; if (*ptr) ++ptr; } if ((ptr = allocstr(str)) == NULL) { res = NULL; i = 0; } else if ((res = allocarray(++i)) == NULL) { free(str); i = 0; } else { for (i = 0; *ptr; i++) { int count = strcspn(ptr, chars); res[i] = ptr; ptr += count; if (*ptr) *ptr++ = '\0'; } res[i] = 0; } 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); 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, char const * dir) { login_cap_t *lc = malloc(sizeof(login_cap_t)); if (lc != NULL) { int i = 0; char userpath[MAXPATHLEN]; static char *login_dbarray[] = { NULL, NULL, NULL }; if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, _FILE_LOGIN_CONF) < MAXPATHLEN) login_dbarray[i++] = userpath; else login_dbarray[i++] = _PATH_LOGIN_CONF; login_dbarray[i ] = NULL; lc->lc_cap = lc->lc_class = lc->lc_style = NULL; if ((name == NULL || cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0) && cgetent(&lc->lc_cap, login_dbarray, (char*)(name = LOGIN_DEFCLASS)) != 0) { free(lc); lc = NULL; } else { ++lc_object_count; lc->lc_class = strdup(name); } } return lc; } /* * 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_getclass(const struct passwd *pwd) { const char * class = NULL; if (pwd != NULL) { if ((class = pwd->pw_class) == NULL || *class == '\0') class = (pwd->pw_uid == 0) ? "root" : NULL; } return login_getclassbyname(class, 0); } /* * login_getuserclass() * Get the login class for a given password entry, allowing user * overrides via ~/.login_conf. * ### WAS: If the password entry's class field is not set, * ####### or the class specified does not exist, then use * If an entry with the recordid "me" 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_getuserclass(const struct passwd *pwd) { const char * class = "me"; /* (pwd == NULL) ? NULL : pwd->pw_class; */ const char * home = (pwd == NULL) ? NULL : pwd->pw_dir; return login_getclassbyname(class, home); } /* * 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; } else if (ret >= 0) return res; else return 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 = login_getcapstr(lc, (char*)cap, NULL, NULL); if (str == NULL) str = error; else { char *ptr = str; while (*ptr) { int count = strcspn(ptr, ", \t"); ptr += count; if (*ptr) *ptr++ = ':'; } } return str; } /* * 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; int ret; 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 ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) return def; else if (ret < 0) return error; /* * "inf" and "infinity" are two special cases for this. */ if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf")) return RLIM_INFINITY; /* * Now go through the string, turning something like 1h2m3s into * an integral value. Whee. */ errno = 0; tot = 0; while (*res) { rlim_t tim = STRTOV(res, &ep, 0); if ((ep == NULL) || (ep == res) || errno) { 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 */ tim *= 60L; break; case 'h': case 'H': /* hours */ tim *= (60L * 60L); break; case 'd': case 'D': /* days */ tim *= (60L * 60L * 24L); break; case 'w': case 'W': /* weeks */ tim *= (60L * 60L * 24L * 7L); case 'y': case 'Y': /* Years */ /* I refuse to take leap years into account here. Sue me. */ tim *= (60L * 60L * 24L * 365L); default: return error; } res = ep; tot += tim; } 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 ret; rlim_t val; if (lc == NULL || lc->lc_cap == NULL) return def; /* * For BSDI compatibility, try for the tag= first */ if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) { long lval; /* * String capability not present, so try for tag# as numeric */ if ((ret = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1) return def; /* Not there, so return default */ else if (ret < 0) return error; return (rlim_t)lval; } else if (ret < 0) return error; if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf")) return RLIM_INFINITY; errno = 0; val = STRTOV(res, &ep, 0); if ((ep == NULL) || (ep == res) || errno) 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; int ret; rlim_t tot, mult; if (lc == NULL || lc->lc_cap == NULL) return def; if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) return def; else if (ret < 0) return error; errno = 0; tot = 0; while (*res) { rlim_t val = STRTOV(res, &ep, 0); if ((res == NULL) || (res == ep) || errno) return error; switch (*ep++) { case 0: /* end of string */ ep--; mult = 1; 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; #ifndef RLIM_LONG case 't': case 'T': /* 1TBte */ mult = 1024LL * 1024LL * 1024LL * 1024LL; break; #endif default: return error; } res = ep; tot += (val * mult); } 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