From c10188736860f567cedd85948284a1f0db4f446c Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Mon, 13 May 2013 18:13:50 +0000 Subject: mdoc sweep --- pw/pw.conf.5 | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 pw/pw.conf.5 diff --git a/pw/pw.conf.5 b/pw/pw.conf.5 new file mode 100644 index 0000000..61c40e8 --- /dev/null +++ b/pw/pw.conf.5 @@ -0,0 +1,318 @@ +.\" Copyright (C) 1996 +.\" David L. Nugent. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 30, 2007 +.Dt PW.CONF 5 +.Os +.Sh NAME +.Nm pw.conf +.Nd format of the pw.conf configuration file +.Sh DESCRIPTION +The file +.Pa /etc/pw.conf +contains configuration data for the +.Xr pw 8 +utility. +The +.Xr pw 8 +utility is used for maintenance of the system password and group +files, allowing users and groups to be added, deleted and changed. +This file may be modified via the +.Xr pw 8 +command using the +.Ar useradd +command and the +.Fl D +option, or by editing it directly with a text editor. +.Pp +Each line in +.Pa /etc/pw.conf +is treated either a comment or as configuration data; +blank lines and lines commencing with a +.Ql \&# +character are considered comments, and any remaining lines are +examined for a leading keyword, followed by corresponding data. +.Pp +Keywords recognized by +.Xr pw 8 +are: +.Bl -tag -width password_days -offset indent -compact +.It defaultpasswd +affect passwords generated for new users +.It reuseuids +reuse gaps in uid sequences +.It reusegids +reuse gaps in gid sequences +.It nispasswd +path to the +.Tn NIS +passwd database +.It skeleton +where to obtain default home contents +.It newmail +mail to send to new users +.It logfile +log user/group modifications to this file +.It home +root directory for home directories +.It homemode +permissions for home directory +.It shellpath +paths in which to locate shell programs +.It shells +list of valid shells (without path) +.It defaultshell +default shell (without path) +.It defaultgroup +default group +.It extragroups +add new users to this groups +.It defaultclass +place new users in this login class +.It minuid +.It maxuid +range of valid default user ids +.It mingid +.It maxgid +range of valid default group ids +.It expire_days +days after which account expires +.It password_days +days after which password expires +.El +.Pp +Valid values for +.Ar defaultpasswd +are: +.Bl -tag -width password_days -offset indent -compact +.It no +disable login on newly created accounts +.It yes +force the password to be the account name +.It none +force a blank password +.It random +generate a random password +.El +.Pp +The second and third options are insecure and should be avoided if +possible on a publicly accessible system. +The first option requires that the superuser run +.Xr passwd 1 +to set a password before the account may be used. +This may also be useful for creating administrative accounts. +The final option causes +.Xr pw 8 +to respond by printing a randomly generated password on stdout. +This is the preferred and most secure option. +The +.Xr pw 8 +utility also provides a method of setting a specific password for the new +user via a filehandle (command lines are not secure). +.Pp +Both +.Ar reuseuids +and +.Ar reusegids +determine the method by which new user and group id numbers are +generated. +A +.Ql \&yes +in this field will cause +.Xr pw 8 +to search for the first unused user or group id within the allowed +range, whereas a +.Ql \&no +will ensure that no other existing user or group id within the range +is numerically lower than the new one generated, and therefore avoids +reusing gaps in the user or group id sequence that are caused by +previous user or group deletions. +Note that if the default group is not specified using the +.Ar defaultgroup +keyword, +.Xr pw 8 +will create a new group for the user and attempt to keep the new +user's uid and gid the same. +If the new user's uid is currently in use as a group id, then the next +available group id is chosen instead. +.Pp +On +.Tn NIS +servers which maintain a separate passwd database to +.Pa /etc/master.passwd , +this option allows the additional file to be concurrently updated +as user records are added, modified or removed. +If blank or set to 'no', no additional database is updated. +An absolute pathname must be used. +.Pp +The +.Ar skeleton +keyword nominates a directory from which the contents of a user's +new home directory is constructed. +This is +.Pa /usr/share/skel +by default. +The +.Xr pw 8 Ns 's +.Fl m +option causes the user's home directory to be created and populated +using the files contained in the +.Ar skeleton +directory. +.Pp +To send an initial email to new users, the +.Ar newmail +keyword may be used to specify a path name to a file containing +the message body of the message to be sent. +To avoid sending mail when accounts are created, leave this entry +blank or specify +.Ql \&no . +.Pp +The +.Ar logfile +option allows logging of password file modifications into the +nominated log file. +To avoid creating or adding to such a logfile, then leave this +field blank or specify +.Ql \&no . +.Pp +The +.Ar home +keyword is mandatory. +This specifies the location of the directory in which all new user +home directories are created. +.Pp +The +.Ar homemode +keyword is optional. +It specifies the creation mask of the user's home directory and is modified by +.Xr umask 2 . +.Pp +The +.Ar shellpath +keyword specifies a list of directories - separated by colons +.Ql \&: +- which contain the programs used by the login shells. +.Pp +The +.Ar shells +keyword specifies a list of programs available for use as login +shells. +This list is a comma-separated list of shell names which should +not contain a path. +These shells must exist in one of the directories nominated by +.Ar shellpath . +.Pp +The +.Ar defaultshell +keyword nominates which shell program to use for new users when +none is specified on the +.Xr pw 8 +command line. +.Pp +The +.Ar defaultgroup +keyword defines the primary group (the group id number in the +password file) used for new accounts. +If left blank, or the word +.Ql \&no +is used, then each new user will have a corresponding group of +their own created automatically. +This is the recommended procedure for new users as it best secures each +user's files against interference by other users of the system +irrespective of the +.Em umask +normally used by the user. +.Pp +The +.Ar extragroups +keyword provides an automatic means of placing new users into groups within +the +.Pa /etc/groups +file. +This is useful where all users share some resources, and is preferable +to placing users into the same primary group. +The effect of this keyword can be overridden using the +.Fl G +option on the +.Xr pw 8 +command line. +.Pp +The +.Ar defaultclass +field determines the login class (See +.Xr login.conf 5 ) +that new users will be allocated unless overwritten by +.Xr pw 8 . +.Pp +The +.Ar minuid , +.Ar maxuid , +.Ar mingid , +.Ar maxgid +keywords determine the allowed ranges of automatically allocated user +and group id numbers. +The default values for both user and group ids are 1000 and 32000 as +minimum and maximum respectively. +The user and group id's actually used when creating an account with +.Xr pw 8 +may be overridden using the +.Fl u +and +.Fl g +command line options. +.Pp +The +.Ar expire_days +and +.Ar password_days +are used to automatically calculate the number of days from the date +on which an account is created when the account will expire or the +user will be forced to change the account's password. +A value of +.Ql \&0 +in either field will disable the corresponding (account or password) +expiration date. +.Sh LIMITS +The maximum line length of +.Pa /etc/pw.conf +is 1024 characters. +Longer lines will be skipped and treated +as comments. +.Sh FILES +.Bl -tag -width /etc/master.passwd -compact +.It Pa /etc/pw.conf +.It Pa /etc/passwd +.It Pa /etc/master.passwd +.It Pa /etc/group +.El +.Sh SEE ALSO +.Xr passwd 1 , +.Xr umask 2 , +.Xr group 5 , +.Xr login.conf 5 , +.Xr passwd 5 , +.Xr pw 8 -- cgit v1.2.3 From ef6308ed8bafbe7e21c5367aae7f8d2844b34c14 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Sat, 29 Jun 2013 15:54:17 +0000 Subject: Fix -Wunsequenced warning Submitted by: dt71@gmx.com --- pw/pw_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pw/pw_user.c b/pw/pw_user.c index 5f4d7a9..b49cfce 100644 --- a/pw/pw_user.c +++ b/pw/pw_user.c @@ -200,7 +200,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) strlcpy(dbuf, cnf->home, sizeof(dbuf)); p = dbuf; if (stat(dbuf, &st) == -1) { - while ((p = strchr(++p, '/')) != NULL) { + while ((p = strchr(p + 1, '/')) != NULL) { *p = '\0'; if (stat(dbuf, &st) == -1) { if (mkdir(dbuf, _DEF_DIRMODE) == -1) -- cgit v1.2.3 From 6864202685c1b4296896ae4f1aa0cf1e30a4d9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Wed, 3 Jul 2013 09:48:24 +0000 Subject: There is no reason to disallow setting the password or account expiry date to the current date. MFC after: 3 days --- pw/pw_user.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pw/pw_user.c b/pw/pw_user.c index b49cfce..4d7e7ce 100644 --- a/pw/pw_user.c +++ b/pw/pw_user.c @@ -513,8 +513,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) time_t now = time(NULL); time_t expire = parse_date(now, arg->val); - if (now == expire) - errx(EX_DATAERR, "invalid password change date `%s'", arg->val); if (pwd->pw_change != expire) { pwd->pw_change = expire; edited = 1; @@ -533,8 +531,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) time_t now = time(NULL); time_t expire = parse_date(now, arg->val); - if (now == expire) - errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val); if (pwd->pw_expire != expire) { pwd->pw_expire = expire; edited = 1; -- cgit v1.2.3 From 78ed20543de7f07e92480d9d65202a25b4abcbcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Thu, 4 Jul 2013 07:59:11 +0000 Subject: Fall back to sha512 if passwd_format is not set. MFC after: 3 days --- pw/pw_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pw/pw_user.c b/pw/pw_user.c index 4d7e7ce..def238c 100644 --- a/pw/pw_user.c +++ b/pw/pw_user.c @@ -573,7 +573,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) lc = login_getpwclass(pwd); if (lc == NULL || - login_setcryptfmt(lc, "md5", NULL) == NULL) + login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name); -- cgit v1.2.3 From f562b1ad762e5de5ae3be4a985032a6bb56fb394 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Wed, 28 Aug 2013 21:10:37 +0000 Subject: libutil: Use O_CLOEXEC for internal file descriptors from open(). --- libutil/login_cap.c | 819 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 819 insertions(+) create mode 100644 libutil/login_cap.c diff --git a/libutil/login_cap.c b/libutil/login_cap.c new file mode 100644 index 0000000..8915d0a --- /dev/null +++ b/libutil/login_cap.c @@ -0,0 +1,819 @@ +/*- + * 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 + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 const char ** internal_array = NULL; + +static char path_login_conf[] = _PATH_LOGIN_CONF; + +static char * +allocstr(const 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 const char ** +allocarray(size_t sz) +{ + static const 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 const char ** +arrayize(const char *str, const char *chars, int *size) +{ + int i; + char *ptr; + const char *cptr; + const char **res = NULL; + + /* count the sub-strings */ + for (i = 0, cptr = str; *cptr; i++) { + int count = strcspn(cptr, chars); + cptr += count; + if (*cptr) + ++cptr; + } + + /* alloc the array */ + if ((ptr = allocstr(str)) != NULL) { + if ((res = allocarray(++i)) == NULL) + free((void *)(uintptr_t)(const void *)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 (i.e., "default") is fetched. + * If the name given is LOGIN_MECLASS and + * 'pwd' argument is non-NULL and contains an non-NULL + * dir entry, then the file _FILE_LOGIN_CONF is picked + * up from that directory and used before the system + * login database. In that case the system login database + * is looked up using LOGIN_MECLASS, too, which is a bug. + * 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) { + if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1) + login_dbarray[i++] = userpath; + } + /* + * XXX: Why to add the system database if the class is `me'? + */ + 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, 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 | O_CLOEXEC)) >= 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, name) != 0 && r >= 0) + break; + /* FALLTHROUGH - 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_getpwclass() + * 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 (i.e., "default") for an unprivileged + * user or that of LOGIN_DEFROOTCLASS (i.e., "root") for a super-user. + * 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; + } + /* + * XXX: pwd should be unused by login_getclassbyname() unless cls is `me', + * so NULL can be passed instead of pwd for more safety. + */ + return login_getclassbyname(cls, pwd); +} + + +/* + * login_getuserclass() + * Get the `me' login class, allowing user overrides via ~/.login_conf. + * Note that user overrides are allowed only in the `me' class. + */ + +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 default if not found, or + * an error string on error. + */ + +const char * +login_getcapstr(login_cap_t *lc, const char *cap, const char *def, const 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, 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. + */ + +const char ** +login_getcaplist(login_cap_t *lc, const char *cap, const char *chars) +{ + const char *lstring; + + if (chars == NULL) + chars = ", \t"; + if ((lstring = login_getcapstr(lc, 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 . + */ + +const char * +login_getpath(login_cap_t *lc, const char *cap, const char *error) +{ + const char *str; + char *ptr; + int count; + + str = login_getcapstr(lc, cap, NULL, NULL); + if (str == NULL) + return error; + ptr = __DECONST(char *, str); /* XXXX Yes, very dodgy */ + while (*ptr) { + 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, 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, cap, &res)) == -1) { + long lval; + /* string capability not present, so try for tag# as numeric */ + if ((r = cgetnum(lc->lc_cap, 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, 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, cap, ':') != NULL); +} + + +/* + * login_getstyle() + * Given a login_cap entry , and optionally a type of auth , + * and optionally a style