11eaf0ac3Slogwang /*-
21eaf0ac3Slogwang * Copyright (c) 1996 by
31eaf0ac3Slogwang * Sean Eric Fagan <[email protected]>
41eaf0ac3Slogwang * David Nugent <[email protected]>
51eaf0ac3Slogwang * All rights reserved.
61eaf0ac3Slogwang *
71eaf0ac3Slogwang * Portions copyright (c) 1995,1997
81eaf0ac3Slogwang * Berkeley Software Design, Inc.
91eaf0ac3Slogwang * All rights reserved.
101eaf0ac3Slogwang *
111eaf0ac3Slogwang * Redistribution and use in source and binary forms, with or without
121eaf0ac3Slogwang * modification, is permitted provided that the following conditions
131eaf0ac3Slogwang * are met:
141eaf0ac3Slogwang * 1. Redistributions of source code must retain the above copyright
151eaf0ac3Slogwang * notice immediately at the beginning of the file, without modification,
161eaf0ac3Slogwang * this list of conditions, and the following disclaimer.
171eaf0ac3Slogwang * 2. Redistributions in binary form must reproduce the above copyright
181eaf0ac3Slogwang * notice, this list of conditions and the following disclaimer in the
191eaf0ac3Slogwang * documentation and/or other materials provided with the distribution.
201eaf0ac3Slogwang * 3. This work was done expressly for inclusion into FreeBSD. Other use
211eaf0ac3Slogwang * is permitted provided this notation is included.
221eaf0ac3Slogwang * 4. Absolutely no warranty of function or purpose is made by the authors.
231eaf0ac3Slogwang * 5. Modifications may be freely made to this file providing the above
241eaf0ac3Slogwang * conditions are met.
251eaf0ac3Slogwang *
261eaf0ac3Slogwang * Low-level routines relating to the user capabilities database
271eaf0ac3Slogwang */
281eaf0ac3Slogwang
291eaf0ac3Slogwang #include <sys/cdefs.h>
301eaf0ac3Slogwang __FBSDID("$FreeBSD$");
311eaf0ac3Slogwang
321eaf0ac3Slogwang #include <sys/types.h>
331eaf0ac3Slogwang #include <sys/time.h>
341eaf0ac3Slogwang #include <sys/resource.h>
351eaf0ac3Slogwang #include <sys/param.h>
361eaf0ac3Slogwang #include <errno.h>
371eaf0ac3Slogwang #include <fcntl.h>
381eaf0ac3Slogwang #include <libutil.h>
391eaf0ac3Slogwang #include <login_cap.h>
401eaf0ac3Slogwang #include <pwd.h>
411eaf0ac3Slogwang #include <stdio.h>
421eaf0ac3Slogwang #include <stdlib.h>
431eaf0ac3Slogwang #include <string.h>
441eaf0ac3Slogwang #include <syslog.h>
451eaf0ac3Slogwang #include <unistd.h>
461eaf0ac3Slogwang
471eaf0ac3Slogwang /*
481eaf0ac3Slogwang * allocstr()
491eaf0ac3Slogwang * Manage a single static pointer for handling a local char* buffer,
501eaf0ac3Slogwang * resizing as necessary to contain the string.
511eaf0ac3Slogwang *
521eaf0ac3Slogwang * allocarray()
531eaf0ac3Slogwang * Manage a static array for handling a group of strings, resizing
541eaf0ac3Slogwang * when necessary.
551eaf0ac3Slogwang */
561eaf0ac3Slogwang
571eaf0ac3Slogwang static int lc_object_count = 0;
581eaf0ac3Slogwang
591eaf0ac3Slogwang static size_t internal_stringsz = 0;
601eaf0ac3Slogwang static char * internal_string = NULL;
611eaf0ac3Slogwang static size_t internal_arraysz = 0;
621eaf0ac3Slogwang static const char ** internal_array = NULL;
631eaf0ac3Slogwang
641eaf0ac3Slogwang static char path_login_conf[] = _PATH_LOGIN_CONF;
651eaf0ac3Slogwang
661eaf0ac3Slogwang static char *
allocstr(const char * str)671eaf0ac3Slogwang allocstr(const char *str)
681eaf0ac3Slogwang {
691eaf0ac3Slogwang char *p;
701eaf0ac3Slogwang
711eaf0ac3Slogwang size_t sz = strlen(str) + 1; /* realloc() only if necessary */
721eaf0ac3Slogwang if (sz <= internal_stringsz)
731eaf0ac3Slogwang p = strcpy(internal_string, str);
741eaf0ac3Slogwang else if ((p = realloc(internal_string, sz)) != NULL) {
751eaf0ac3Slogwang internal_stringsz = sz;
761eaf0ac3Slogwang internal_string = strcpy(p, str);
771eaf0ac3Slogwang }
781eaf0ac3Slogwang return p;
791eaf0ac3Slogwang }
801eaf0ac3Slogwang
811eaf0ac3Slogwang
821eaf0ac3Slogwang static const char **
allocarray(size_t sz)831eaf0ac3Slogwang allocarray(size_t sz)
841eaf0ac3Slogwang {
851eaf0ac3Slogwang static const char **p;
861eaf0ac3Slogwang
871eaf0ac3Slogwang if (sz <= internal_arraysz)
881eaf0ac3Slogwang p = internal_array;
89*22ce4affSfengbojiang else if ((p = reallocarray(internal_array, sz, sizeof(char*))) != NULL) {
901eaf0ac3Slogwang internal_arraysz = sz;
911eaf0ac3Slogwang internal_array = p;
921eaf0ac3Slogwang }
931eaf0ac3Slogwang return p;
941eaf0ac3Slogwang }
951eaf0ac3Slogwang
961eaf0ac3Slogwang
971eaf0ac3Slogwang /*
981eaf0ac3Slogwang * arrayize()
991eaf0ac3Slogwang * Turn a simple string <str> separated by any of
1001eaf0ac3Slogwang * the set of <chars> into an array. The last element
1011eaf0ac3Slogwang * of the array will be NULL, as is proper.
1021eaf0ac3Slogwang * Free using freearraystr()
1031eaf0ac3Slogwang */
1041eaf0ac3Slogwang
1051eaf0ac3Slogwang static const char **
arrayize(const char * str,const char * chars,int * size)1061eaf0ac3Slogwang arrayize(const char *str, const char *chars, int *size)
1071eaf0ac3Slogwang {
1081eaf0ac3Slogwang int i;
1091eaf0ac3Slogwang char *ptr;
1101eaf0ac3Slogwang const char *cptr;
1111eaf0ac3Slogwang const char **res = NULL;
1121eaf0ac3Slogwang
1131eaf0ac3Slogwang /* count the sub-strings */
1141eaf0ac3Slogwang for (i = 0, cptr = str; *cptr; i++) {
1151eaf0ac3Slogwang int count = strcspn(cptr, chars);
1161eaf0ac3Slogwang cptr += count;
1171eaf0ac3Slogwang if (*cptr)
1181eaf0ac3Slogwang ++cptr;
1191eaf0ac3Slogwang }
1201eaf0ac3Slogwang
1211eaf0ac3Slogwang /* alloc the array */
1221eaf0ac3Slogwang if ((ptr = allocstr(str)) != NULL) {
1231eaf0ac3Slogwang if ((res = allocarray(++i)) == NULL)
1241eaf0ac3Slogwang free((void *)(uintptr_t)(const void *)str);
1251eaf0ac3Slogwang else {
1261eaf0ac3Slogwang /* now split the string */
1271eaf0ac3Slogwang i = 0;
1281eaf0ac3Slogwang while (*ptr) {
1291eaf0ac3Slogwang int count = strcspn(ptr, chars);
1301eaf0ac3Slogwang res[i++] = ptr;
1311eaf0ac3Slogwang ptr += count;
1321eaf0ac3Slogwang if (*ptr)
1331eaf0ac3Slogwang *ptr++ = '\0';
1341eaf0ac3Slogwang }
1351eaf0ac3Slogwang res[i] = NULL;
1361eaf0ac3Slogwang }
1371eaf0ac3Slogwang }
1381eaf0ac3Slogwang
1391eaf0ac3Slogwang if (size)
1401eaf0ac3Slogwang *size = i;
1411eaf0ac3Slogwang
1421eaf0ac3Slogwang return res;
1431eaf0ac3Slogwang }
1441eaf0ac3Slogwang
1451eaf0ac3Slogwang
1461eaf0ac3Slogwang /*
1471eaf0ac3Slogwang * login_close()
1481eaf0ac3Slogwang * Frees up all resources relating to a login class
1491eaf0ac3Slogwang *
1501eaf0ac3Slogwang */
1511eaf0ac3Slogwang
1521eaf0ac3Slogwang void
login_close(login_cap_t * lc)1531eaf0ac3Slogwang login_close(login_cap_t * lc)
1541eaf0ac3Slogwang {
1551eaf0ac3Slogwang if (lc) {
1561eaf0ac3Slogwang free(lc->lc_style);
1571eaf0ac3Slogwang free(lc->lc_class);
1581eaf0ac3Slogwang free(lc->lc_cap);
1591eaf0ac3Slogwang free(lc);
1601eaf0ac3Slogwang if (--lc_object_count == 0) {
1611eaf0ac3Slogwang free(internal_string);
1621eaf0ac3Slogwang free(internal_array);
1631eaf0ac3Slogwang internal_array = NULL;
1641eaf0ac3Slogwang internal_arraysz = 0;
1651eaf0ac3Slogwang internal_string = NULL;
1661eaf0ac3Slogwang internal_stringsz = 0;
1671eaf0ac3Slogwang cgetclose();
1681eaf0ac3Slogwang }
1691eaf0ac3Slogwang }
1701eaf0ac3Slogwang }
1711eaf0ac3Slogwang
1721eaf0ac3Slogwang
1731eaf0ac3Slogwang /*
1741eaf0ac3Slogwang * login_getclassbyname()
1751eaf0ac3Slogwang * Get the login class by its name.
1761eaf0ac3Slogwang * If the name given is NULL or empty, the default class
1771eaf0ac3Slogwang * LOGIN_DEFCLASS (i.e., "default") is fetched.
1781eaf0ac3Slogwang * If the name given is LOGIN_MECLASS and
1791eaf0ac3Slogwang * 'pwd' argument is non-NULL and contains an non-NULL
1801eaf0ac3Slogwang * dir entry, then the file _FILE_LOGIN_CONF is picked
1811eaf0ac3Slogwang * up from that directory and used before the system
1821eaf0ac3Slogwang * login database. In that case the system login database
1831eaf0ac3Slogwang * is looked up using LOGIN_MECLASS, too, which is a bug.
1841eaf0ac3Slogwang * Return a filled-out login_cap_t structure, including
1851eaf0ac3Slogwang * class name, and the capability record buffer.
1861eaf0ac3Slogwang */
1871eaf0ac3Slogwang
1881eaf0ac3Slogwang login_cap_t *
login_getclassbyname(char const * name,const struct passwd * pwd)1891eaf0ac3Slogwang login_getclassbyname(char const *name, const struct passwd *pwd)
1901eaf0ac3Slogwang {
1911eaf0ac3Slogwang login_cap_t *lc;
1921eaf0ac3Slogwang
1931eaf0ac3Slogwang if ((lc = malloc(sizeof(login_cap_t))) != NULL) {
1941eaf0ac3Slogwang int r, me, i = 0;
1951eaf0ac3Slogwang uid_t euid = 0;
1961eaf0ac3Slogwang gid_t egid = 0;
1971eaf0ac3Slogwang const char *msg = NULL;
1981eaf0ac3Slogwang const char *dir;
1991eaf0ac3Slogwang char userpath[MAXPATHLEN];
2001eaf0ac3Slogwang
2011eaf0ac3Slogwang static char *login_dbarray[] = { NULL, NULL, NULL };
2021eaf0ac3Slogwang
2031eaf0ac3Slogwang me = (name != NULL && strcmp(name, LOGIN_MECLASS) == 0);
2041eaf0ac3Slogwang dir = (!me || pwd == NULL) ? NULL : pwd->pw_dir;
2051eaf0ac3Slogwang /*
2061eaf0ac3Slogwang * Switch to user mode before checking/reading its ~/.login_conf
2071eaf0ac3Slogwang * - some NFSes have root read access disabled.
2081eaf0ac3Slogwang *
2091eaf0ac3Slogwang * XXX: This fails to configure additional groups.
2101eaf0ac3Slogwang */
2111eaf0ac3Slogwang if (dir) {
2121eaf0ac3Slogwang euid = geteuid();
2131eaf0ac3Slogwang egid = getegid();
2141eaf0ac3Slogwang (void)setegid(pwd->pw_gid);
2151eaf0ac3Slogwang (void)seteuid(pwd->pw_uid);
2161eaf0ac3Slogwang }
2171eaf0ac3Slogwang
2181eaf0ac3Slogwang if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir,
2191eaf0ac3Slogwang _FILE_LOGIN_CONF) < MAXPATHLEN) {
2201eaf0ac3Slogwang if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1)
2211eaf0ac3Slogwang login_dbarray[i++] = userpath;
2221eaf0ac3Slogwang }
2231eaf0ac3Slogwang /*
2241eaf0ac3Slogwang * XXX: Why to add the system database if the class is `me'?
2251eaf0ac3Slogwang */
2261eaf0ac3Slogwang if (_secure_path(path_login_conf, 0, 0) != -1)
2271eaf0ac3Slogwang login_dbarray[i++] = path_login_conf;
2281eaf0ac3Slogwang login_dbarray[i] = NULL;
2291eaf0ac3Slogwang
2301eaf0ac3Slogwang memset(lc, 0, sizeof(login_cap_t));
2311eaf0ac3Slogwang lc->lc_cap = lc->lc_class = lc->lc_style = NULL;
2321eaf0ac3Slogwang
2331eaf0ac3Slogwang if (name == NULL || *name == '\0')
2341eaf0ac3Slogwang name = LOGIN_DEFCLASS;
2351eaf0ac3Slogwang
2361eaf0ac3Slogwang switch (cgetent(&lc->lc_cap, login_dbarray, name)) {
2371eaf0ac3Slogwang case -1: /* Failed, entry does not exist */
2381eaf0ac3Slogwang if (me)
2391eaf0ac3Slogwang break; /* Don't retry default on 'me' */
2401eaf0ac3Slogwang if (i == 0)
2411eaf0ac3Slogwang r = -1;
2421eaf0ac3Slogwang else if ((r = open(login_dbarray[0], O_RDONLY | O_CLOEXEC)) >= 0)
2431eaf0ac3Slogwang close(r);
2441eaf0ac3Slogwang /*
2451eaf0ac3Slogwang * If there's at least one login class database,
2461eaf0ac3Slogwang * and we aren't searching for a default class
2471eaf0ac3Slogwang * then complain about a non-existent class.
2481eaf0ac3Slogwang */
2491eaf0ac3Slogwang if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0)
2501eaf0ac3Slogwang syslog(LOG_ERR, "login_getclass: unknown class '%s'", name);
2511eaf0ac3Slogwang /* fall-back to default class */
2521eaf0ac3Slogwang name = LOGIN_DEFCLASS;
2531eaf0ac3Slogwang msg = "%s: no default/fallback class '%s'";
2541eaf0ac3Slogwang if (cgetent(&lc->lc_cap, login_dbarray, name) != 0 && r >= 0)
2551eaf0ac3Slogwang break;
2561eaf0ac3Slogwang /* FALLTHROUGH - just return system defaults */
2571eaf0ac3Slogwang case 0: /* success! */
2581eaf0ac3Slogwang if ((lc->lc_class = strdup(name)) != NULL) {
2591eaf0ac3Slogwang if (dir) {
2601eaf0ac3Slogwang (void)seteuid(euid);
2611eaf0ac3Slogwang (void)setegid(egid);
2621eaf0ac3Slogwang }
2631eaf0ac3Slogwang ++lc_object_count;
2641eaf0ac3Slogwang return lc;
2651eaf0ac3Slogwang }
2661eaf0ac3Slogwang msg = "%s: strdup: %m";
2671eaf0ac3Slogwang break;
2681eaf0ac3Slogwang case -2:
2691eaf0ac3Slogwang msg = "%s: retrieving class information: %m";
2701eaf0ac3Slogwang break;
2711eaf0ac3Slogwang case -3:
2721eaf0ac3Slogwang msg = "%s: 'tc=' reference loop '%s'";
2731eaf0ac3Slogwang break;
2741eaf0ac3Slogwang case 1:
2751eaf0ac3Slogwang msg = "couldn't resolve 'tc=' reference in '%s'";
2761eaf0ac3Slogwang break;
2771eaf0ac3Slogwang default:
2781eaf0ac3Slogwang msg = "%s: unexpected cgetent() error '%s': %m";
2791eaf0ac3Slogwang break;
2801eaf0ac3Slogwang }
2811eaf0ac3Slogwang if (dir) {
2821eaf0ac3Slogwang (void)seteuid(euid);
2831eaf0ac3Slogwang (void)setegid(egid);
2841eaf0ac3Slogwang }
2851eaf0ac3Slogwang if (msg != NULL)
2861eaf0ac3Slogwang syslog(LOG_ERR, msg, "login_getclass", name);
2871eaf0ac3Slogwang free(lc);
2881eaf0ac3Slogwang }
2891eaf0ac3Slogwang
2901eaf0ac3Slogwang return NULL;
2911eaf0ac3Slogwang }
2921eaf0ac3Slogwang
2931eaf0ac3Slogwang
2941eaf0ac3Slogwang
2951eaf0ac3Slogwang /*
2961eaf0ac3Slogwang * login_getclass()
2971eaf0ac3Slogwang * Get the login class for the system (only) login class database.
2981eaf0ac3Slogwang * Return a filled-out login_cap_t structure, including
2991eaf0ac3Slogwang * class name, and the capability record buffer.
3001eaf0ac3Slogwang */
3011eaf0ac3Slogwang
3021eaf0ac3Slogwang login_cap_t *
login_getclass(const char * cls)3031eaf0ac3Slogwang login_getclass(const char *cls)
3041eaf0ac3Slogwang {
3051eaf0ac3Slogwang return login_getclassbyname(cls, NULL);
3061eaf0ac3Slogwang }
3071eaf0ac3Slogwang
3081eaf0ac3Slogwang
3091eaf0ac3Slogwang /*
3101eaf0ac3Slogwang * login_getpwclass()
3111eaf0ac3Slogwang * Get the login class for a given password entry from
3121eaf0ac3Slogwang * the system (only) login class database.
3131eaf0ac3Slogwang * If the password entry's class field is not set, or
3141eaf0ac3Slogwang * the class specified does not exist, then use the
3151eaf0ac3Slogwang * default of LOGIN_DEFCLASS (i.e., "default") for an unprivileged
3161eaf0ac3Slogwang * user or that of LOGIN_DEFROOTCLASS (i.e., "root") for a super-user.
3171eaf0ac3Slogwang * Return a filled-out login_cap_t structure, including
3181eaf0ac3Slogwang * class name, and the capability record buffer.
3191eaf0ac3Slogwang */
3201eaf0ac3Slogwang
3211eaf0ac3Slogwang login_cap_t *
login_getpwclass(const struct passwd * pwd)3221eaf0ac3Slogwang login_getpwclass(const struct passwd *pwd)
3231eaf0ac3Slogwang {
3241eaf0ac3Slogwang const char *cls = NULL;
3251eaf0ac3Slogwang
3261eaf0ac3Slogwang if (pwd != NULL) {
3271eaf0ac3Slogwang cls = pwd->pw_class;
3281eaf0ac3Slogwang if (cls == NULL || *cls == '\0')
3291eaf0ac3Slogwang cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
3301eaf0ac3Slogwang }
3311eaf0ac3Slogwang /*
3321eaf0ac3Slogwang * XXX: pwd should be unused by login_getclassbyname() unless cls is `me',
3331eaf0ac3Slogwang * so NULL can be passed instead of pwd for more safety.
3341eaf0ac3Slogwang */
3351eaf0ac3Slogwang return login_getclassbyname(cls, pwd);
3361eaf0ac3Slogwang }
3371eaf0ac3Slogwang
3381eaf0ac3Slogwang
3391eaf0ac3Slogwang /*
3401eaf0ac3Slogwang * login_getuserclass()
3411eaf0ac3Slogwang * Get the `me' login class, allowing user overrides via ~/.login_conf.
3421eaf0ac3Slogwang * Note that user overrides are allowed only in the `me' class.
3431eaf0ac3Slogwang */
3441eaf0ac3Slogwang
3451eaf0ac3Slogwang login_cap_t *
login_getuserclass(const struct passwd * pwd)3461eaf0ac3Slogwang login_getuserclass(const struct passwd *pwd)
3471eaf0ac3Slogwang {
3481eaf0ac3Slogwang return login_getclassbyname(LOGIN_MECLASS, pwd);
3491eaf0ac3Slogwang }
3501eaf0ac3Slogwang
3511eaf0ac3Slogwang
3521eaf0ac3Slogwang /*
3531eaf0ac3Slogwang * login_getcapstr()
3541eaf0ac3Slogwang * Given a login_cap entry, and a capability name, return the
3551eaf0ac3Slogwang * value defined for that capability, a default if not found, or
3561eaf0ac3Slogwang * an error string on error.
3571eaf0ac3Slogwang */
3581eaf0ac3Slogwang
3591eaf0ac3Slogwang const char *
login_getcapstr(login_cap_t * lc,const char * cap,const char * def,const char * error)3601eaf0ac3Slogwang login_getcapstr(login_cap_t *lc, const char *cap, const char *def, const char *error)
3611eaf0ac3Slogwang {
3621eaf0ac3Slogwang char *res;
3631eaf0ac3Slogwang int ret;
3641eaf0ac3Slogwang
3651eaf0ac3Slogwang if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0')
3661eaf0ac3Slogwang return def;
3671eaf0ac3Slogwang
3681eaf0ac3Slogwang if ((ret = cgetstr(lc->lc_cap, cap, &res)) == -1)
3691eaf0ac3Slogwang return def;
3701eaf0ac3Slogwang return (ret >= 0) ? res : error;
3711eaf0ac3Slogwang }
3721eaf0ac3Slogwang
3731eaf0ac3Slogwang
3741eaf0ac3Slogwang /*
3751eaf0ac3Slogwang * login_getcaplist()
3761eaf0ac3Slogwang * Given a login_cap entry, and a capability name, return the
3771eaf0ac3Slogwang * value defined for that capability split into an array of
3781eaf0ac3Slogwang * strings.
3791eaf0ac3Slogwang */
3801eaf0ac3Slogwang
3811eaf0ac3Slogwang const char **
login_getcaplist(login_cap_t * lc,const char * cap,const char * chars)3821eaf0ac3Slogwang login_getcaplist(login_cap_t *lc, const char *cap, const char *chars)
3831eaf0ac3Slogwang {
3841eaf0ac3Slogwang const char *lstring;
3851eaf0ac3Slogwang
3861eaf0ac3Slogwang if (chars == NULL)
3871eaf0ac3Slogwang chars = ", \t";
3881eaf0ac3Slogwang if ((lstring = login_getcapstr(lc, cap, NULL, NULL)) != NULL)
3891eaf0ac3Slogwang return arrayize(lstring, chars, NULL);
3901eaf0ac3Slogwang return NULL;
3911eaf0ac3Slogwang }
3921eaf0ac3Slogwang
3931eaf0ac3Slogwang
3941eaf0ac3Slogwang /*
3951eaf0ac3Slogwang * login_getpath()
3961eaf0ac3Slogwang * From the login_cap_t <lc>, get the capability <cap> which is
3971eaf0ac3Slogwang * formatted as either a space or comma delimited list of paths
3981eaf0ac3Slogwang * and append them all into a string and separate by semicolons.
3991eaf0ac3Slogwang * If there is an error of any kind, return <error>.
4001eaf0ac3Slogwang */
4011eaf0ac3Slogwang
4021eaf0ac3Slogwang const char *
login_getpath(login_cap_t * lc,const char * cap,const char * error)4031eaf0ac3Slogwang login_getpath(login_cap_t *lc, const char *cap, const char *error)
4041eaf0ac3Slogwang {
4051eaf0ac3Slogwang const char *str;
4061eaf0ac3Slogwang char *ptr;
4071eaf0ac3Slogwang int count;
4081eaf0ac3Slogwang
4091eaf0ac3Slogwang str = login_getcapstr(lc, cap, NULL, NULL);
4101eaf0ac3Slogwang if (str == NULL)
4111eaf0ac3Slogwang return error;
4121eaf0ac3Slogwang ptr = __DECONST(char *, str); /* XXXX Yes, very dodgy */
4131eaf0ac3Slogwang while (*ptr) {
4141eaf0ac3Slogwang count = strcspn(ptr, ", \t");
4151eaf0ac3Slogwang ptr += count;
4161eaf0ac3Slogwang if (*ptr)
4171eaf0ac3Slogwang *ptr++ = ':';
4181eaf0ac3Slogwang }
4191eaf0ac3Slogwang return str;
4201eaf0ac3Slogwang }
4211eaf0ac3Slogwang
4221eaf0ac3Slogwang
4231eaf0ac3Slogwang static int
isinfinite(const char * s)4241eaf0ac3Slogwang isinfinite(const char *s)
4251eaf0ac3Slogwang {
4261eaf0ac3Slogwang static const char *infs[] = {
4271eaf0ac3Slogwang "infinity",
4281eaf0ac3Slogwang "inf",
4291eaf0ac3Slogwang "unlimited",
4301eaf0ac3Slogwang "unlimit",
4311eaf0ac3Slogwang "-1",
4321eaf0ac3Slogwang NULL
4331eaf0ac3Slogwang };
4341eaf0ac3Slogwang const char **i = &infs[0];
4351eaf0ac3Slogwang
4361eaf0ac3Slogwang while (*i != NULL) {
4371eaf0ac3Slogwang if (strcasecmp(s, *i) == 0)
4381eaf0ac3Slogwang return 1;
4391eaf0ac3Slogwang ++i;
4401eaf0ac3Slogwang }
4411eaf0ac3Slogwang return 0;
4421eaf0ac3Slogwang }
4431eaf0ac3Slogwang
4441eaf0ac3Slogwang
4451eaf0ac3Slogwang static u_quad_t
rmultiply(u_quad_t n1,u_quad_t n2)4461eaf0ac3Slogwang rmultiply(u_quad_t n1, u_quad_t n2)
4471eaf0ac3Slogwang {
4481eaf0ac3Slogwang u_quad_t m, r;
4491eaf0ac3Slogwang int b1, b2;
4501eaf0ac3Slogwang
4511eaf0ac3Slogwang static int bpw = 0;
4521eaf0ac3Slogwang
4531eaf0ac3Slogwang /* Handle simple cases */
4541eaf0ac3Slogwang if (n1 == 0 || n2 == 0)
4551eaf0ac3Slogwang return 0;
4561eaf0ac3Slogwang if (n1 == 1)
4571eaf0ac3Slogwang return n2;
4581eaf0ac3Slogwang if (n2 == 1)
4591eaf0ac3Slogwang return n1;
4601eaf0ac3Slogwang
4611eaf0ac3Slogwang /*
4621eaf0ac3Slogwang * sizeof() returns number of bytes needed for storage.
4631eaf0ac3Slogwang * This may be different from the actual number of useful bits.
4641eaf0ac3Slogwang */
4651eaf0ac3Slogwang if (!bpw) {
4661eaf0ac3Slogwang bpw = sizeof(u_quad_t) * 8;
4671eaf0ac3Slogwang while (((u_quad_t)1 << (bpw-1)) == 0)
4681eaf0ac3Slogwang --bpw;
4691eaf0ac3Slogwang }
4701eaf0ac3Slogwang
4711eaf0ac3Slogwang /*
4721eaf0ac3Slogwang * First check the magnitude of each number. If the sum of the
4731eaf0ac3Slogwang * magnatude is way to high, reject the number. (If this test
4741eaf0ac3Slogwang * is not done then the first multiply below may overflow.)
4751eaf0ac3Slogwang */
4761eaf0ac3Slogwang for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
4771eaf0ac3Slogwang ;
4781eaf0ac3Slogwang for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
4791eaf0ac3Slogwang ;
4801eaf0ac3Slogwang if (b1 + b2 - 2 > bpw) {
4811eaf0ac3Slogwang errno = ERANGE;
4821eaf0ac3Slogwang return (UQUAD_MAX);
4831eaf0ac3Slogwang }
4841eaf0ac3Slogwang
4851eaf0ac3Slogwang /*
4861eaf0ac3Slogwang * Decompose the multiplication to be:
4871eaf0ac3Slogwang * h1 = n1 & ~1
4881eaf0ac3Slogwang * h2 = n2 & ~1
4891eaf0ac3Slogwang * l1 = n1 & 1
4901eaf0ac3Slogwang * l2 = n2 & 1
4911eaf0ac3Slogwang * (h1 + l1) * (h2 + l2)
4921eaf0ac3Slogwang * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
4931eaf0ac3Slogwang *
4941eaf0ac3Slogwang * Since h1 && h2 do not have the low bit set, we can then say:
4951eaf0ac3Slogwang *
4961eaf0ac3Slogwang * (h1>>1 * h2>>1 * 4) + ...
4971eaf0ac3Slogwang *
4981eaf0ac3Slogwang * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
4991eaf0ac3Slogwang * overflow.
5001eaf0ac3Slogwang *
5011eaf0ac3Slogwang * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
5021eaf0ac3Slogwang * then adding in residual amout will cause an overflow.
5031eaf0ac3Slogwang */
5041eaf0ac3Slogwang
5051eaf0ac3Slogwang m = (n1 >> 1) * (n2 >> 1);
5061eaf0ac3Slogwang if (m >= ((u_quad_t)1 << (bpw-2))) {
5071eaf0ac3Slogwang errno = ERANGE;
5081eaf0ac3Slogwang return (UQUAD_MAX);
5091eaf0ac3Slogwang }
5101eaf0ac3Slogwang m *= 4;
5111eaf0ac3Slogwang
5121eaf0ac3Slogwang r = (n1 & n2 & 1)
5131eaf0ac3Slogwang + (n2 & 1) * (n1 & ~(u_quad_t)1)
5141eaf0ac3Slogwang + (n1 & 1) * (n2 & ~(u_quad_t)1);
5151eaf0ac3Slogwang
5161eaf0ac3Slogwang if ((u_quad_t)(m + r) < m) {
5171eaf0ac3Slogwang errno = ERANGE;
5181eaf0ac3Slogwang return (UQUAD_MAX);
5191eaf0ac3Slogwang }
5201eaf0ac3Slogwang m += r;
5211eaf0ac3Slogwang
5221eaf0ac3Slogwang return (m);
5231eaf0ac3Slogwang }
5241eaf0ac3Slogwang
5251eaf0ac3Slogwang
5261eaf0ac3Slogwang /*
5271eaf0ac3Slogwang * login_getcaptime()
5281eaf0ac3Slogwang * From the login_cap_t <lc>, get the capability <cap>, which is
5291eaf0ac3Slogwang * formatted as a time (e.g., "<cap>=10h3m2s"). If <cap> is not
5301eaf0ac3Slogwang * present in <lc>, return <def>; if there is an error of some kind,
5311eaf0ac3Slogwang * return <error>.
5321eaf0ac3Slogwang */
5331eaf0ac3Slogwang
5341eaf0ac3Slogwang rlim_t
login_getcaptime(login_cap_t * lc,const char * cap,rlim_t def,rlim_t error)5351eaf0ac3Slogwang login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
5361eaf0ac3Slogwang {
5371eaf0ac3Slogwang char *res, *ep, *oval;
5381eaf0ac3Slogwang int r;
5391eaf0ac3Slogwang rlim_t tot;
5401eaf0ac3Slogwang
5411eaf0ac3Slogwang errno = 0;
5421eaf0ac3Slogwang if (lc == NULL || lc->lc_cap == NULL)
5431eaf0ac3Slogwang return def;
5441eaf0ac3Slogwang
5451eaf0ac3Slogwang /*
5461eaf0ac3Slogwang * Look for <cap> in lc_cap.
5471eaf0ac3Slogwang * If it's not there (-1), return <def>.
5481eaf0ac3Slogwang * If there's an error, return <error>.
5491eaf0ac3Slogwang */
5501eaf0ac3Slogwang
5511eaf0ac3Slogwang if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1)
5521eaf0ac3Slogwang return def;
5531eaf0ac3Slogwang else if (r < 0) {
5541eaf0ac3Slogwang errno = ERANGE;
5551eaf0ac3Slogwang return error;
5561eaf0ac3Slogwang }
5571eaf0ac3Slogwang
5581eaf0ac3Slogwang /* "inf" and "infinity" are special cases */
5591eaf0ac3Slogwang if (isinfinite(res))
5601eaf0ac3Slogwang return RLIM_INFINITY;
5611eaf0ac3Slogwang
5621eaf0ac3Slogwang /*
5631eaf0ac3Slogwang * Now go through the string, turning something like 1h2m3s into
5641eaf0ac3Slogwang * an integral value. Whee.
5651eaf0ac3Slogwang */
5661eaf0ac3Slogwang
5671eaf0ac3Slogwang errno = 0;
5681eaf0ac3Slogwang tot = 0;
5691eaf0ac3Slogwang oval = res;
5701eaf0ac3Slogwang while (*res) {
5711eaf0ac3Slogwang rlim_t tim = strtoq(res, &ep, 0);
5721eaf0ac3Slogwang rlim_t mult = 1;
5731eaf0ac3Slogwang
5741eaf0ac3Slogwang if (ep == NULL || ep == res || errno != 0) {
5751eaf0ac3Slogwang invalid:
5761eaf0ac3Slogwang syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s",
5771eaf0ac3Slogwang lc->lc_class, cap, oval);
5781eaf0ac3Slogwang errno = ERANGE;
5791eaf0ac3Slogwang return error;
5801eaf0ac3Slogwang }
5811eaf0ac3Slogwang /* Look for suffixes */
5821eaf0ac3Slogwang switch (*ep++) {
5831eaf0ac3Slogwang case 0:
5841eaf0ac3Slogwang ep--;
5851eaf0ac3Slogwang break; /* end of string */
5861eaf0ac3Slogwang case 's': case 'S': /* seconds */
5871eaf0ac3Slogwang break;
5881eaf0ac3Slogwang case 'm': case 'M': /* minutes */
5891eaf0ac3Slogwang mult = 60;
5901eaf0ac3Slogwang break;
5911eaf0ac3Slogwang case 'h': case 'H': /* hours */
5921eaf0ac3Slogwang mult = 60L * 60L;
5931eaf0ac3Slogwang break;
5941eaf0ac3Slogwang case 'd': case 'D': /* days */
5951eaf0ac3Slogwang mult = 60L * 60L * 24L;
5961eaf0ac3Slogwang break;
5971eaf0ac3Slogwang case 'w': case 'W': /* weeks */
5981eaf0ac3Slogwang mult = 60L * 60L * 24L * 7L;
5991eaf0ac3Slogwang break;
6001eaf0ac3Slogwang case 'y': case 'Y': /* 365-day years */
6011eaf0ac3Slogwang mult = 60L * 60L * 24L * 365L;
6021eaf0ac3Slogwang break;
6031eaf0ac3Slogwang default:
6041eaf0ac3Slogwang goto invalid;
6051eaf0ac3Slogwang }
6061eaf0ac3Slogwang res = ep;
6071eaf0ac3Slogwang tot += rmultiply(tim, mult);
6081eaf0ac3Slogwang if (errno)
6091eaf0ac3Slogwang goto invalid;
6101eaf0ac3Slogwang }
6111eaf0ac3Slogwang
6121eaf0ac3Slogwang return tot;
6131eaf0ac3Slogwang }
6141eaf0ac3Slogwang
6151eaf0ac3Slogwang
6161eaf0ac3Slogwang /*
6171eaf0ac3Slogwang * login_getcapnum()
6181eaf0ac3Slogwang * From the login_cap_t <lc>, extract the numerical value <cap>.
6191eaf0ac3Slogwang * If it is not present, return <def> for a default, and return
6201eaf0ac3Slogwang * <error> if there is an error.
6211eaf0ac3Slogwang * Like login_getcaptime(), only it only converts to a number, not
6221eaf0ac3Slogwang * to a time; "infinity" and "inf" are 'special.'
6231eaf0ac3Slogwang */
6241eaf0ac3Slogwang
6251eaf0ac3Slogwang rlim_t
login_getcapnum(login_cap_t * lc,const char * cap,rlim_t def,rlim_t error)6261eaf0ac3Slogwang login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
6271eaf0ac3Slogwang {
6281eaf0ac3Slogwang char *ep, *res;
6291eaf0ac3Slogwang int r;
6301eaf0ac3Slogwang rlim_t val;
6311eaf0ac3Slogwang
6321eaf0ac3Slogwang if (lc == NULL || lc->lc_cap == NULL)
6331eaf0ac3Slogwang return def;
6341eaf0ac3Slogwang
6351eaf0ac3Slogwang /*
6361eaf0ac3Slogwang * For BSDI compatibility, try for the tag=<val> first
6371eaf0ac3Slogwang */
6381eaf0ac3Slogwang if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) {
6391eaf0ac3Slogwang long lval;
6401eaf0ac3Slogwang /* string capability not present, so try for tag#<val> as numeric */
6411eaf0ac3Slogwang if ((r = cgetnum(lc->lc_cap, cap, &lval)) == -1)
6421eaf0ac3Slogwang return def; /* Not there, so return default */
6431eaf0ac3Slogwang else if (r >= 0)
6441eaf0ac3Slogwang return (rlim_t)lval;
6451eaf0ac3Slogwang }
6461eaf0ac3Slogwang
6471eaf0ac3Slogwang if (r < 0) {
6481eaf0ac3Slogwang errno = ERANGE;
6491eaf0ac3Slogwang return error;
6501eaf0ac3Slogwang }
6511eaf0ac3Slogwang
6521eaf0ac3Slogwang if (isinfinite(res))
6531eaf0ac3Slogwang return RLIM_INFINITY;
6541eaf0ac3Slogwang
6551eaf0ac3Slogwang errno = 0;
6561eaf0ac3Slogwang val = strtoq(res, &ep, 0);
6571eaf0ac3Slogwang if (ep == NULL || ep == res || errno != 0) {
6581eaf0ac3Slogwang syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s",
6591eaf0ac3Slogwang lc->lc_class, cap, res);
6601eaf0ac3Slogwang errno = ERANGE;
6611eaf0ac3Slogwang return error;
6621eaf0ac3Slogwang }
6631eaf0ac3Slogwang
6641eaf0ac3Slogwang return val;
6651eaf0ac3Slogwang }
6661eaf0ac3Slogwang
6671eaf0ac3Slogwang
6681eaf0ac3Slogwang
6691eaf0ac3Slogwang /*
6701eaf0ac3Slogwang * login_getcapsize()
6711eaf0ac3Slogwang * From the login_cap_t <lc>, extract the capability <cap>, which is
6721eaf0ac3Slogwang * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity".
6731eaf0ac3Slogwang * If not present, return <def>, or <error> if there is an error of
6741eaf0ac3Slogwang * some sort.
6751eaf0ac3Slogwang */
6761eaf0ac3Slogwang
6771eaf0ac3Slogwang rlim_t
login_getcapsize(login_cap_t * lc,const char * cap,rlim_t def,rlim_t error)6781eaf0ac3Slogwang login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
6791eaf0ac3Slogwang {
6801eaf0ac3Slogwang char *ep, *res, *oval;
6811eaf0ac3Slogwang int r;
6821eaf0ac3Slogwang rlim_t tot;
6831eaf0ac3Slogwang
6841eaf0ac3Slogwang if (lc == NULL || lc->lc_cap == NULL)
6851eaf0ac3Slogwang return def;
6861eaf0ac3Slogwang
6871eaf0ac3Slogwang if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1)
6881eaf0ac3Slogwang return def;
6891eaf0ac3Slogwang else if (r < 0) {
6901eaf0ac3Slogwang errno = ERANGE;
6911eaf0ac3Slogwang return error;
6921eaf0ac3Slogwang }
6931eaf0ac3Slogwang
6941eaf0ac3Slogwang if (isinfinite(res))
6951eaf0ac3Slogwang return RLIM_INFINITY;
6961eaf0ac3Slogwang
6971eaf0ac3Slogwang errno = 0;
6981eaf0ac3Slogwang tot = 0;
6991eaf0ac3Slogwang oval = res;
7001eaf0ac3Slogwang while (*res) {
7011eaf0ac3Slogwang rlim_t siz = strtoq(res, &ep, 0);
7021eaf0ac3Slogwang rlim_t mult = 1;
7031eaf0ac3Slogwang
7041eaf0ac3Slogwang if (ep == NULL || ep == res || errno != 0) {
7051eaf0ac3Slogwang invalid:
7061eaf0ac3Slogwang syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s",
7071eaf0ac3Slogwang lc->lc_class, cap, oval);
7081eaf0ac3Slogwang errno = ERANGE;
7091eaf0ac3Slogwang return error;
7101eaf0ac3Slogwang }
7111eaf0ac3Slogwang switch (*ep++) {
7121eaf0ac3Slogwang case 0: /* end of string */
7131eaf0ac3Slogwang ep--;
7141eaf0ac3Slogwang break;
7151eaf0ac3Slogwang case 'b': case 'B': /* 512-byte blocks */
7161eaf0ac3Slogwang mult = 512;
7171eaf0ac3Slogwang break;
7181eaf0ac3Slogwang case 'k': case 'K': /* 1024-byte Kilobytes */
7191eaf0ac3Slogwang mult = 1024;
7201eaf0ac3Slogwang break;
7211eaf0ac3Slogwang case 'm': case 'M': /* 1024-k kbytes */
7221eaf0ac3Slogwang mult = 1024 * 1024;
7231eaf0ac3Slogwang break;
7241eaf0ac3Slogwang case 'g': case 'G': /* 1Gbyte */
7251eaf0ac3Slogwang mult = 1024 * 1024 * 1024;
7261eaf0ac3Slogwang break;
7271eaf0ac3Slogwang case 't': case 'T': /* 1TBte */
7281eaf0ac3Slogwang mult = 1024LL * 1024LL * 1024LL * 1024LL;
7291eaf0ac3Slogwang break;
7301eaf0ac3Slogwang default:
7311eaf0ac3Slogwang goto invalid;
7321eaf0ac3Slogwang }
7331eaf0ac3Slogwang res = ep;
7341eaf0ac3Slogwang tot += rmultiply(siz, mult);
7351eaf0ac3Slogwang if (errno)
7361eaf0ac3Slogwang goto invalid;
7371eaf0ac3Slogwang }
7381eaf0ac3Slogwang
7391eaf0ac3Slogwang return tot;
7401eaf0ac3Slogwang }
7411eaf0ac3Slogwang
7421eaf0ac3Slogwang
7431eaf0ac3Slogwang /*
7441eaf0ac3Slogwang * login_getcapbool()
7451eaf0ac3Slogwang * From the login_cap_t <lc>, check for the existence of the capability
7461eaf0ac3Slogwang * of <cap>. Return <def> if <lc>->lc_cap is NULL, otherwise return
7471eaf0ac3Slogwang * the whether or not <cap> exists there.
7481eaf0ac3Slogwang */
7491eaf0ac3Slogwang
7501eaf0ac3Slogwang int
login_getcapbool(login_cap_t * lc,const char * cap,int def)7511eaf0ac3Slogwang login_getcapbool(login_cap_t *lc, const char *cap, int def)
7521eaf0ac3Slogwang {
7531eaf0ac3Slogwang if (lc == NULL || lc->lc_cap == NULL)
7541eaf0ac3Slogwang return def;
7551eaf0ac3Slogwang return (cgetcap(lc->lc_cap, cap, ':') != NULL);
7561eaf0ac3Slogwang }
7571eaf0ac3Slogwang
7581eaf0ac3Slogwang
7591eaf0ac3Slogwang /*
7601eaf0ac3Slogwang * login_getstyle()
7611eaf0ac3Slogwang * Given a login_cap entry <lc>, and optionally a type of auth <auth>,
7621eaf0ac3Slogwang * and optionally a style <style>, find the style that best suits these
7631eaf0ac3Slogwang * rules:
7641eaf0ac3Slogwang * 1. If <auth> is non-null, look for an "auth-<auth>=" string
7651eaf0ac3Slogwang * in the capability; if not present, default to "auth=".
7661eaf0ac3Slogwang * 2. If there is no auth list found from (1), default to
7671eaf0ac3Slogwang * "passwd" as an authorization list.
7681eaf0ac3Slogwang * 3. If <style> is non-null, look for <style> in the list of
7691eaf0ac3Slogwang * authorization methods found from (2); if <style> is NULL, default
7701eaf0ac3Slogwang * to LOGIN_DEFSTYLE ("passwd").
7711eaf0ac3Slogwang * 4. If the chosen style is found in the chosen list of authorization
7721eaf0ac3Slogwang * methods, return that; otherwise, return NULL.
7731eaf0ac3Slogwang * E.g.:
7741eaf0ac3Slogwang * login_getstyle(lc, NULL, "ftp");
7751eaf0ac3Slogwang * login_getstyle(lc, "login", NULL);
7761eaf0ac3Slogwang * login_getstyle(lc, "skey", "network");
7771eaf0ac3Slogwang */
7781eaf0ac3Slogwang
7791eaf0ac3Slogwang const char *
login_getstyle(login_cap_t * lc,const char * style,const char * auth)7801eaf0ac3Slogwang login_getstyle(login_cap_t *lc, const char *style, const char *auth)
7811eaf0ac3Slogwang {
7821eaf0ac3Slogwang int i;
7831eaf0ac3Slogwang const char **authtypes = NULL;
7841eaf0ac3Slogwang char *auths= NULL;
7851eaf0ac3Slogwang char realauth[64];
7861eaf0ac3Slogwang
7871eaf0ac3Slogwang static const char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL };
7881eaf0ac3Slogwang
7891eaf0ac3Slogwang if (auth != NULL && *auth != '\0') {
7901eaf0ac3Slogwang if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < (int)sizeof(realauth))
7911eaf0ac3Slogwang authtypes = login_getcaplist(lc, realauth, NULL);
7921eaf0ac3Slogwang }
7931eaf0ac3Slogwang
7941eaf0ac3Slogwang if (authtypes == NULL)
7951eaf0ac3Slogwang authtypes = login_getcaplist(lc, "auth", NULL);
7961eaf0ac3Slogwang
7971eaf0ac3Slogwang if (authtypes == NULL)
7981eaf0ac3Slogwang authtypes = defauthtypes;
7991eaf0ac3Slogwang
8001eaf0ac3Slogwang /*
8011eaf0ac3Slogwang * We have at least one authtype now; auths is a comma-separated
8021eaf0ac3Slogwang * (or space-separated) list of authentication types. We have to
8031eaf0ac3Slogwang * convert from this to an array of char*'s; authtypes then gets this.
8041eaf0ac3Slogwang */
8051eaf0ac3Slogwang i = 0;
8061eaf0ac3Slogwang if (style != NULL && *style != '\0') {
8071eaf0ac3Slogwang while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0)
8081eaf0ac3Slogwang i++;
8091eaf0ac3Slogwang }
8101eaf0ac3Slogwang
8111eaf0ac3Slogwang lc->lc_style = NULL;
8121eaf0ac3Slogwang if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL)
8131eaf0ac3Slogwang lc->lc_style = auths;
8141eaf0ac3Slogwang
8151eaf0ac3Slogwang if (lc->lc_style != NULL)
8161eaf0ac3Slogwang lc->lc_style = strdup(lc->lc_style);
8171eaf0ac3Slogwang
8181eaf0ac3Slogwang return lc->lc_style;
8191eaf0ac3Slogwang }
820