From ec831d9055a20b69cb9a7300990b8e4414cbd474 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Wed, 10 Feb 2021 11:29:12 -0500 Subject: pw: fix building for iOS, completely untested --- pw/cpdir.c | 2 +- pw/libutil/_secure_path.c | 74 +++ pw/libutil/flopen.c | 147 +++++ pw/libutil/gr_util.c | 667 +++++++++++++++++++++++ pw/libutil/libutil.h | 234 ++++++++ pw/libutil/login_cap.c | 821 ++++++++++++++++++++++++++++ pw/libutil/login_cap.h | 164 ++++++ pw/libutil/login_crypt.c | 50 ++ pw/libutil/pw_scan.c | 207 +++++++ pw/libutil/pw_scan.h | 39 ++ pw/libutil/pw_util.c | 685 ++++++++++++++++++++++++ pw/pw_log.c | 1 + pw/pw_user.c | 183 ++++++- pw/reallocarray.c | 11 + pw/reallocarray.h | 5 + pw/sbuf/subr_prf.c | 1310 +++++++++++++++++++++++++++++++++++++++++++++ pw/sbuf/subr_sbuf.c | 950 ++++++++++++++++++++++++++++++++ pw/sbuf/sys/sbuf.h | 123 +++++ 18 files changed, 5646 insertions(+), 27 deletions(-) create mode 100644 pw/libutil/_secure_path.c create mode 100644 pw/libutil/flopen.c create mode 100644 pw/libutil/gr_util.c create mode 100644 pw/libutil/libutil.h create mode 100644 pw/libutil/login_cap.c create mode 100644 pw/libutil/login_cap.h create mode 100644 pw/libutil/login_crypt.c create mode 100644 pw/libutil/pw_scan.c create mode 100644 pw/libutil/pw_scan.h create mode 100644 pw/libutil/pw_util.c create mode 100644 pw/reallocarray.c create mode 100644 pw/reallocarray.h create mode 100644 pw/sbuf/subr_prf.c create mode 100644 pw/sbuf/subr_sbuf.c create mode 100644 pw/sbuf/sys/sbuf.h diff --git a/pw/cpdir.c b/pw/cpdir.c index 4e6ca88..7686e0f 100644 --- a/pw/cpdir.c +++ b/pw/cpdir.c @@ -60,7 +60,7 @@ copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, } fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW); if (flags > 0) - chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW); + lchflags(dir, flags); if (skelfd == -1) return; diff --git a/pw/libutil/_secure_path.c b/pw/libutil/_secure_path.c new file mode 100644 index 0000000..363378b --- /dev/null +++ b/pw/libutil/_secure_path.c @@ -0,0 +1,74 @@ +/*- + * Based on code copyright (c) 1995,1997 by + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include + +/* + * Check for common security problems on a given path + * It must be: + * 1. A regular file, and exists + * 2. Owned and writable only by root (or given owner) + * 3. Group ownership is given group or is non-group writable + * + * Returns: -2 if file does not exist, + * -1 if security test failure + * 0 otherwise + */ + +int +_secure_path(const char *path, uid_t uid, gid_t gid) +{ + int r = -1; + struct stat sb; + const char *msg = NULL; + + if (lstat(path, &sb) < 0) { + if (errno == ENOENT) /* special case */ + r = -2; /* if it is just missing, skip the log entry */ + else + msg = "%s: cannot stat %s: %m"; + } + else if (!S_ISREG(sb.st_mode)) + msg = "%s: %s is not a regular file"; + else if (sb.st_mode & S_IWOTH) + msg = "%s: %s is world writable"; + else if ((int)uid != -1 && sb.st_uid != uid && sb.st_uid != 0) { + if (uid == 0) + msg = "%s: %s is not owned by root"; + else + msg = "%s: %s is not owned by uid %d"; + } else if ((int)gid != -1 && sb.st_gid != gid && (sb.st_mode & S_IWGRP)) + msg = "%s: %s is group writeable by non-authorised groups"; + else + r = 0; + if (msg != NULL) + syslog(LOG_ERR, msg, "_secure_path", path, uid); + return r; +} diff --git a/pw/libutil/flopen.c b/pw/libutil/flopen.c new file mode 100644 index 0000000..485eee4 --- /dev/null +++ b/pw/libutil/flopen.c @@ -0,0 +1,147 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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 THE AUTHOR 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 THE AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include + +#include + +/* + * Reliably open and lock a file. + * + * Please do not modify this code without first reading the revision history + * and discussing your changes with . Don't be fooled by the + * code's apparent simplicity; there would be no need for this function if it + * was easy to get right. + */ +static int +vflopenat(int dirfd, const char *path, int flags, va_list ap) +{ + int fd, operation, serrno, trunc; + struct stat sb, fsb; + mode_t mode; + +#ifdef O_EXLOCK + flags &= ~O_EXLOCK; +#endif + + mode = 0; + if (flags & O_CREAT) { + mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */ + } + + operation = LOCK_EX; + if (flags & O_NONBLOCK) + operation |= LOCK_NB; + + trunc = (flags & O_TRUNC); + flags &= ~O_TRUNC; + + for (;;) { + if ((fd = openat(dirfd, path, flags, mode)) == -1) + /* non-existent or no access */ + return (-1); + if (flock(fd, operation) == -1) { + /* unsupported or interrupted */ + serrno = errno; + (void)close(fd); + errno = serrno; + return (-1); + } + if (fstatat(dirfd, path, &sb, 0) == -1) { + /* disappeared from under our feet */ + (void)close(fd); + continue; + } + if (fstat(fd, &fsb) == -1) { + /* can't happen [tm] */ + serrno = errno; + (void)close(fd); + errno = serrno; + return (-1); + } + if (sb.st_dev != fsb.st_dev || + sb.st_ino != fsb.st_ino) { + /* changed under our feet */ + (void)close(fd); + continue; + } + if (trunc && ftruncate(fd, 0) != 0) { + /* can't happen [tm] */ + serrno = errno; + (void)close(fd); + errno = serrno; + return (-1); + } + /* + * The following change is provided as a specific example to + * avoid. + */ +#if 0 + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { + serrno = errno; + (void)close(fd); + errno = serrno; + return (-1); + } +#endif + return (fd); + } +} + +int +flopen(const char *path, int flags, ...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = vflopenat(AT_FDCWD, path, flags, ap); + va_end(ap); + return (ret); +} + +int +flopenat(int dirfd, const char *path, int flags, ...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = vflopenat(dirfd, path, flags, ap); + va_end(ap); + return (ret); +} diff --git a/pw/libutil/gr_util.c b/pw/libutil/gr_util.c new file mode 100644 index 0000000..cfb9b7e --- /dev/null +++ b/pw/libutil/gr_util.c @@ -0,0 +1,667 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2008 Sean C. Farley + * 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, + * without modification, immediately at the beginning of the file. + * 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 THE AUTHOR ``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 THE AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "reallocarray.h" + +static int lockfd = -1; +static char group_dir[PATH_MAX]; +static char group_file[PATH_MAX]; +static char tempname[PATH_MAX]; +static int initialized; +static size_t grmemlen(const struct group *, const char *, int *); +static struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx); + +/* + * Initialize statics + */ +int +gr_init(const char *dir, const char *group) +{ + + if (dir == NULL) { + strcpy(group_dir, _PATH_PWD); + } else { + if (strlen(dir) >= sizeof(group_dir)) { + errno = ENAMETOOLONG; + return (-1); + } + strcpy(group_dir, dir); + } + + if (group == NULL) { + if (dir == NULL) { + strcpy(group_file, _PATH_GROUP); + } else if (snprintf(group_file, sizeof(group_file), "%s/group", + group_dir) > (int)sizeof(group_file)) { + errno = ENAMETOOLONG; + return (-1); + } + } else { + if (strlen(group) >= sizeof(group_file)) { + errno = ENAMETOOLONG; + return (-1); + } + strcpy(group_file, group); + } + + initialized = 1; + return (0); +} + +/* + * Lock the group file + */ +int +gr_lock(void) +{ + if (*group_file == '\0') + return (-1); + + for (;;) { + struct stat st; + + lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); + if (lockfd == -1) { + if (errno == EWOULDBLOCK) { + errx(1, "the group file is busy"); + } else { + err(1, "could not lock the group file"); + } + } + if (fstat(lockfd, &st) == -1) + err(1, "fstat() failed"); + if (st.st_nlink != 0) + break; + close(lockfd); + lockfd = -1; + } + return (lockfd); +} + +/* + * Create and open a presmuably safe temp file for editing group data + */ +int +gr_tmp(int mfd) +{ + char buf[8192]; + ssize_t nr; + const char *p; + int tfd; + + if (*group_file == '\0') + return (-1); + if ((p = strrchr(group_file, '/'))) + ++p; + else + p = group_file; + if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX", + (int)(p - group_file), group_file) >= (int)sizeof(tempname)) { + errno = ENAMETOOLONG; + return (-1); + } + if ((tfd = mkostemp(tempname, 0)) == -1) + return (-1); + if (mfd != -1) { + while ((nr = read(mfd, buf, sizeof(buf))) > 0) + if (write(tfd, buf, (size_t)nr) != nr) + break; + if (nr != 0) { + unlink(tempname); + *tempname = '\0'; + close(tfd); + return (-1); + } + } + return (tfd); +} + +/* + * Copy the group file from one descriptor to another, replacing, deleting + * or adding a single record on the way. + */ +int +gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) +{ + char *buf, *end, *line, *p, *q, *r, *tmp; + struct group *fgr; + const struct group *sgr; + size_t len, size; + int eof, readlen; + char t; + + if (old_gr == NULL && gr == NULL) + return(-1); + + sgr = old_gr; + /* deleting a group */ + if (gr == NULL) { + line = NULL; + } else { + if ((line = gr_make(gr)) == NULL) + return (-1); + } + + /* adding a group */ + if (sgr == NULL) + sgr = gr; + + /* initialize the buffer */ + if ((buf = malloc(size = 1024)) == NULL) + goto err; + + eof = 0; + len = 0; + p = q = end = buf; + for (;;) { + /* find the end of the current line */ + for (p = q; q < end && *q != '\0'; ++q) + if (*q == '\n') + break; + + /* if we don't have a complete line, fill up the buffer */ + if (q >= end) { + if (eof) + break; + while ((size_t)(q - p) >= size) { + if ((tmp = reallocarray(buf, 2, size)) == NULL) { + warnx("group line too long"); + goto err; + } + p = tmp + (p - buf); + q = tmp + (q - buf); + end = tmp + (end - buf); + buf = tmp; + size = size * 2; + } + if (p < end) { + q = memmove(buf, p, end -p); + end -= p - buf; + } else { + p = q = end = buf; + } + readlen = read(ffd, end, size - (end - buf)); + if (readlen == -1) + goto err; + else + len = (size_t)readlen; + if (len == 0 && p == buf) + break; + end += len; + len = end - buf; + if (len < size) { + eof = 1; + if (len > 0 && buf[len -1] != '\n') + ++len, *end++ = '\n'; + } + continue; + } + + /* is it a blank line or a comment? */ + for (r = p; r < q && isspace(*r); ++r) + /* nothing */; + if (r == q || *r == '#') { + /* yep */ + if (write(tfd, p, q -p + 1) != q - p + 1) + goto err; + ++q; + continue; + } + + /* is it the one we're looking for? */ + + t = *q; + *q = '\0'; + + fgr = gr_scan(r); + + /* fgr is either a struct group for the current line, + * or NULL if the line is malformed. + */ + + *q = t; + if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) { + /* nope */ + if (fgr != NULL) + free(fgr); + if (write(tfd, p, q - p + 1) != q - p + 1) + goto err; + ++q; + continue; + } + if (old_gr && !gr_equal(fgr, old_gr)) { + warnx("entry inconsistent"); + free(fgr); + errno = EINVAL; /* hack */ + goto err; + } + free(fgr); + + /* it is, replace or remove it */ + if (line != NULL) { + len = strlen(line); + if (write(tfd, line, len) != (int) len) + goto err; + } else { + /* when removed, avoid the \n */ + q++; + } + /* we're done, just copy the rest over */ + for (;;) { + if (write(tfd, q, end - q) != end - q) + goto err; + q = buf; + readlen = read(ffd, buf, size); + if (readlen == 0) + break; + else + len = (size_t)readlen; + if (readlen == -1) + goto err; + end = buf + len; + } + goto done; + } + + /* if we got here, we didn't find the old entry */ + if (line == NULL) { + errno = ENOENT; + goto err; + } + len = strlen(line); + if ((size_t)write(tfd, line, len) != len || + write(tfd, "\n", 1) != 1) + goto err; + done: + free(line); + free(buf); + return (0); + err: + free(line); + free(buf); + return (-1); +} + +/* + * Regenerate the group file + */ +int +gr_mkdb(void) +{ + int fd; + + if (chmod(tempname, 0644) != 0) + return (-1); + + if (rename(tempname, group_file) != 0) + return (-1); + + /* + * Make sure new group file is safe on disk. To improve performance we + * will call fsync() to the directory where file lies + */ + if ((fd = open(group_dir, O_RDONLY|O_DIRECTORY)) == -1) + return (-1); + + if (fsync(fd) != 0) { + close(fd); + return (-1); + } + + close(fd); + return(0); +} + +/* + * Clean up. Preserves errno for the caller's convenience. + */ +void +gr_fini(void) +{ + int serrno; + + if (!initialized) + return; + initialized = 0; + serrno = errno; + if (*tempname != '\0') { + unlink(tempname); + *tempname = '\0'; + } + if (lockfd != -1) + close(lockfd); + errno = serrno; +} + +/* + * Compares two struct group's. + */ +int +gr_equal(const struct group *gr1, const struct group *gr2) +{ + + /* Check that the non-member information is the same. */ + if (gr1->gr_name == NULL || gr2->gr_name == NULL) { + if (gr1->gr_name != gr2->gr_name) + return (false); + } else if (strcmp(gr1->gr_name, gr2->gr_name) != 0) + return (false); + if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) { + if (gr1->gr_passwd != gr2->gr_passwd) + return (false); + } else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0) + return (false); + if (gr1->gr_gid != gr2->gr_gid) + return (false); + + /* + * Check all members in both groups. + * getgrnam can return gr_mem with a pointer to NULL. + * gr_dup and gr_add strip out this superfluous NULL, setting + * gr_mem to NULL for no members. + */ + if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) { + int i; + + for (i = 0; + gr1->gr_mem[i] != NULL && gr2->gr_mem[i] != NULL; i++) { + if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0) + return (false); + } + if (gr1->gr_mem[i] != NULL || gr2->gr_mem[i] != NULL) + return (false); + } else if (gr1->gr_mem != NULL && gr1->gr_mem[0] != NULL) { + return (false); + } else if (gr2->gr_mem != NULL && gr2->gr_mem[0] != NULL) { + return (false); + } + + return (true); +} + +/* + * Make a group line out of a struct group. + */ +char * +gr_make(const struct group *gr) +{ + const char *group_line_format = "%s:%s:%ju:"; + const char *sep; + char *line; + char *p; + size_t line_size; + int ndx; + + /* Calculate the length of the group line. */ + line_size = snprintf(NULL, 0, group_line_format, gr->gr_name, + gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1; + if (gr->gr_mem != NULL) { + for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) + line_size += strlen(gr->gr_mem[ndx]) + 1; + if (ndx > 0) + line_size--; + } + + /* Create the group line and fill it. */ + if ((line = p = malloc(line_size)) == NULL) + return (NULL); + p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd, + (uintmax_t)gr->gr_gid); + if (gr->gr_mem != NULL) { + sep = ""; + for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { + p = stpcpy(p, sep); + p = stpcpy(p, gr->gr_mem[ndx]); + sep = ","; + } + } + + return (line); +} + +/* + * Duplicate a struct group. + */ +struct group * +gr_dup(const struct group *gr) +{ + return (gr_add(gr, NULL)); +} +/* + * Add a new member name to a struct group. + */ +struct group * +gr_add(const struct group *gr, const char *newmember) +{ + char *mem; + size_t len; + int num_mem; + + num_mem = 0; + len = grmemlen(gr, newmember, &num_mem); + /* Create new group and copy old group into it. */ + if ((mem = malloc(len)) == NULL) + return (NULL); + return (grcopy(gr, mem, newmember, num_mem)); +} + +/* It is safer to walk the pointers given at gr_mem since there is no + * guarantee the gr_mem + strings are contiguous in the given struct group + * but compactify the new group into the following form. + * + * The new struct is laid out like this in memory. The example given is + * for a group with two members only. + * + * { + * (char *name) + * (char *passwd) + * (int gid) + * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area + * gr_mem area + * (member1 *) + * (member2 *) + * (NULL) + * (name string) + * (passwd string) + * (member1 string) + * (member2 string) + * } + */ +/* + * Copy the contents of a group plus given name to a preallocated group struct + */ +static struct group * +grcopy(const struct group *gr, char *dst, const char *name, int ndx) +{ + int i; + struct group *newgr; + + newgr = (struct group *)(void *)dst; /* avoid alignment warning */ + dst += sizeof(*newgr); + if (ndx != 0) { + newgr->gr_mem = (char **)(void *)(dst); /* avoid alignment warning */ + dst += (ndx + 1) * sizeof(*newgr->gr_mem); + } else + newgr->gr_mem = NULL; + if (gr->gr_name != NULL) { + newgr->gr_name = dst; + dst = stpcpy(dst, gr->gr_name) + 1; + } else + newgr->gr_name = NULL; + if (gr->gr_passwd != NULL) { + newgr->gr_passwd = dst; + dst = stpcpy(dst, gr->gr_passwd) + 1; + } else + newgr->gr_passwd = NULL; + newgr->gr_gid = gr->gr_gid; + i = 0; + /* Original group struct might have a NULL gr_mem */ + if (gr->gr_mem != NULL) { + for (; gr->gr_mem[i] != NULL; i++) { + newgr->gr_mem[i] = dst; + dst = stpcpy(dst, gr->gr_mem[i]) + 1; + } + } + /* If name is not NULL, newgr->gr_mem is known to be not NULL */ + if (name != NULL) { + newgr->gr_mem[i++] = dst; + dst = stpcpy(dst, name) + 1; + } + /* if newgr->gr_mem is not NULL add NULL marker */ + if (newgr->gr_mem != NULL) + newgr->gr_mem[i] = NULL; + + return (newgr); +} + +/* + * Calculate length of a struct group + given name + */ +static size_t +grmemlen(const struct group *gr, const char *name, int *num_mem) +{ + size_t len; + int i; + + if (gr == NULL) + return (0); + /* Calculate size of the group. */ + len = sizeof(*gr); + if (gr->gr_name != NULL) + len += strlen(gr->gr_name) + 1; + if (gr->gr_passwd != NULL) + len += strlen(gr->gr_passwd) + 1; + i = 0; + if (gr->gr_mem != NULL) { + for (; gr->gr_mem[i] != NULL; i++) { + len += strlen(gr->gr_mem[i]) + 1; + len += sizeof(*gr->gr_mem); + } + } + if (name != NULL) { + i++; + len += strlen(name) + 1; + len += sizeof(*gr->gr_mem); + } + /* Allow for NULL pointer */ + if (i != 0) + len += sizeof(*gr->gr_mem); + *num_mem = i; + return(len); +} + +/* + * Scan a line and place it into a group structure. + */ +static bool +__gr_scan(char *line, struct group *gr) +{ + char *loc; + int ndx; + + /* Assign non-member information to structure. */ + gr->gr_name = line; + if ((loc = strchr(line, ':')) == NULL) + return (false); + *loc = '\0'; + gr->gr_passwd = loc + 1; + if (*gr->gr_passwd == ':') + *gr->gr_passwd = '\0'; + else { + if ((loc = strchr(loc + 1, ':')) == NULL) + return (false); + *loc = '\0'; + } + if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1) + return (false); + + /* Assign member information to structure. */ + if ((loc = strchr(loc + 1, ':')) == NULL) + return (false); + line = loc + 1; + gr->gr_mem = NULL; + ndx = 0; + do { + gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) * + (ndx + 1)); + if (gr->gr_mem == NULL) + return (false); + + /* Skip locations without members (i.e., empty string). */ + do { + gr->gr_mem[ndx] = strsep(&line, ","); + } while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0'); + } while (gr->gr_mem[ndx++] != NULL); + + return (true); +} + +/* + * Create a struct group from a line. + */ +struct group * +gr_scan(const char *line) +{ + struct group gr; + char *line_copy; + struct group *new_gr; + + if ((line_copy = strdup(line)) == NULL) + return (NULL); + if (!__gr_scan(line_copy, &gr)) { + free(line_copy); + return (NULL); + } + new_gr = gr_dup(&gr); + free(line_copy); + if (gr.gr_mem != NULL) + free(gr.gr_mem); + + return (new_gr); +} diff --git a/pw/libutil/libutil.h b/pw/libutil/libutil.h new file mode 100644 index 0000000..6c1967e --- /dev/null +++ b/pw/libutil/libutil.h @@ -0,0 +1,234 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1996 Peter Wemm . + * All rights reserved. + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * ThinkSec AS and NAI Labs, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * 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, 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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$ + */ + +#ifndef _LIBUTIL_H_ +#define _LIBUTIL_H_ + +#include +#include +#include +#include + +#define PROPERTY_MAX_NAME 64 +#define PROPERTY_MAX_VALUE 512 + +/* For properties.c. */ +typedef struct _property { + struct _property *next; + char *name; + char *value; +} *properties; + +/* Avoid pulling in all the include files for no need. */ +struct in_addr; +struct pidfh; +struct sockaddr; +struct termios; +struct winsize; + +__BEGIN_DECLS +char *auth_getval(const char *_name); +void clean_environment(const char * const *_white, + const char * const *_more_white); +int expand_number(const char *_buf, uint64_t *_num); +int extattr_namespace_to_string(int _attrnamespace, char **_string); +int extattr_string_to_namespace(const char *_string, int *_attrnamespace); +int flopen(const char *_path, int _flags, ...); +int flopenat(int _dirfd, const char *_path, int _flags, ...); +int forkpty(int *_amaster, char *_name, + struct termios *_termp, struct winsize *_winp); +const char * + getlocalbase(void); +void hexdump(const void *_ptr, int _length, const char *_hdr, int _flags); +int humanize_number(char *_buf, size_t _len, int64_t _number, + const char *_suffix, int _scale, int _flags); +struct kinfo_file * + kinfo_getfile(pid_t _pid, int *_cntp); +struct kinfo_vmentry * + kinfo_getvmmap(pid_t _pid, int *_cntp); +struct kinfo_vmobject * + kinfo_getvmobject(int *_cntp); +struct kinfo_proc * + kinfo_getallproc(int *_cntp); +struct kinfo_proc * + kinfo_getproc(pid_t _pid); +int kld_isloaded(const char *_name); +int kld_load(const char *_name); +int login_tty(int _fd); +int openpty(int *_amaster, int *_aslave, char *_name, + struct termios *_termp, struct winsize *_winp); +int pidfile_close(struct pidfh *_pfh); +int pidfile_fileno(const struct pidfh *_pfh); +struct pidfh * + pidfile_open(const char *_path, mode_t _mode, pid_t *_pidptr); +int pidfile_remove(struct pidfh *_pfh); +int pidfile_write(struct pidfh *_pfh); +void properties_free(properties _list); +char *property_find(properties _list, const char *_name); +properties + properties_read(int _fd); +int realhostname(char *_host, size_t _hsize, const struct in_addr *_ip); +int realhostname_sa(char *_host, size_t _hsize, struct sockaddr *_addr, + int _addrlen); +int _secure_path(const char *_path, uid_t _uid, gid_t _gid); +void trimdomain(char *_fullhost, int _hostsize); +const char * + uu_lockerr(int _uu_lockresult); +int uu_lock(const char *_ttyname); +int uu_unlock(const char *_ttyname); +int uu_lock_txfr(const char *_ttyname, pid_t _pid); + +/* + * Conditionally prototype the following functions if the include + * files upon which they depend have been included. + */ +#ifdef _STDIO_H_ +char *fparseln(FILE *_fp, size_t *_len, size_t *_lineno, + const char _delim[3], int _flags); +#endif + +#ifdef _PWD_H_ +int pw_copy(int _ffd, int _tfd, const struct passwd *_pw, + struct passwd *_old_pw); +struct passwd + *pw_dup(const struct passwd *_pw); +int pw_edit(int _notsetuid); +int pw_equal(const struct passwd *_pw1, const struct passwd *_pw2); +void pw_fini(void); +int pw_init(const char *_dir, const char *_master); +void pw_initpwd(struct passwd *_pw); +char *pw_make(const struct passwd *_pw); +char *pw_make_v7(const struct passwd *_pw); +int pw_mkdb(const char *_user); +int pw_lock(void); +struct passwd * + pw_scan(const char *_line, int _flags); +const char * + pw_tempname(void); +int pw_tmp(int _mfd); +#endif + +#ifdef _GRP_H_ +int gr_copy(int __ffd, int _tfd, const struct group *_gr, + struct group *_old_gr); +struct group * + gr_dup(const struct group *_gr); +struct group * + gr_add(const struct group *_gr, const char *_newmember); +int gr_equal(const struct group *_gr1, const struct group *_gr2); +void gr_fini(void); +int gr_init(const char *_dir, const char *_master); +int gr_lock(void); +char *gr_make(const struct group *_gr); +int gr_mkdb(void); +struct group * + gr_scan(const char *_line); +int gr_tmp(int _mdf); +#endif + +#ifdef _UFS_UFS_QUOTA_H_ +struct fstab; +struct quotafile; +int quota_check_path(const struct quotafile *_qf, const char *_path); +void quota_close(struct quotafile *_qf); +int quota_convert(struct quotafile *_qf, int _wordsize); +const char * + quota_fsname(const struct quotafile *_qf); +int quota_maxid(struct quotafile *_qf); +int quota_off(struct quotafile *_qf); +int quota_on(struct quotafile *_qf); +struct quotafile * + quota_open(struct fstab *_fs, int _quotatype, int _openflags); +const char * + quota_qfname(const struct quotafile *_qf); +int quota_read(struct quotafile *_qf, struct dqblk *_dqb, int _id); +int quota_write_limits(struct quotafile *_qf, struct dqblk *_dqb, int _id); +int quota_write_usage(struct quotafile *_qf, struct dqblk *_dqb, int _id); +#endif + +__END_DECLS + +/* fparseln(3) */ +#define FPARSELN_UNESCESC 0x01 +#define FPARSELN_UNESCCONT 0x02 +#define FPARSELN_UNESCCOMM 0x04 +#define FPARSELN_UNESCREST 0x08 +#define FPARSELN_UNESCALL 0x0f + +/* Flags for hexdump(3). */ +#define HD_COLUMN_MASK 0xff +#define HD_DELIM_MASK 0xff00 +#define HD_OMIT_COUNT (1 << 16) +#define HD_OMIT_HEX (1 << 17) +#define HD_OMIT_CHARS (1 << 18) + +/* Values for humanize_number(3)'s flags parameter. */ +#define HN_DECIMAL 0x01 +#define HN_NOSPACE 0x02 +#define HN_B 0x04 +#define HN_DIVISOR_1000 0x08 +#define HN_IEC_PREFIXES 0x10 + +/* Values for humanize_number(3)'s scale parameter. */ +#define HN_GETSCALE 0x10 +#define HN_AUTOSCALE 0x20 + +/* Return values from realhostname(). */ +#define HOSTNAME_FOUND 0 +#define HOSTNAME_INCORRECTNAME 1 +#define HOSTNAME_INVALIDADDR 2 +#define HOSTNAME_INVALIDNAME 3 + +/* Flags for pw_scan(). */ +#define PWSCAN_MASTER 0x01 +#define PWSCAN_WARN 0x02 + +/* Return values from uu_lock(). */ +#define UU_LOCK_INUSE 1 +#define UU_LOCK_OK 0 +#define UU_LOCK_OPEN_ERR (-1) +#define UU_LOCK_READ_ERR (-2) +#define UU_LOCK_CREAT_ERR (-3) +#define UU_LOCK_WRITE_ERR (-4) +#define UU_LOCK_LINK_ERR (-5) +#define UU_LOCK_TRY_ERR (-6) +#define UU_LOCK_OWNER_ERR (-7) + +#endif /* !_LIBUTIL_H_ */ diff --git a/pw/libutil/login_cap.c b/pw/libutil/login_cap.c new file mode 100644 index 0000000..b544b69 --- /dev/null +++ b/pw/libutil/login_cap.c @@ -0,0 +1,821 @@ +/*- + * 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 + +#include "reallocarray.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 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 = reallocarray(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 existence 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