diff options
-rw-r--r-- | lib/libutil/Makefile | 6 | ||||
-rw-r--r-- | lib/libutil/_secure_path.3 | 70 | ||||
-rw-r--r-- | lib/libutil/_secure_path.c | 72 | ||||
-rw-r--r-- | lib/libutil/libutil.h | 3 | ||||
-rw-r--r-- | lib/libutil/login_auth.c | 715 | ||||
-rw-r--r-- | lib/libutil/login_cap.c | 828 | ||||
-rw-r--r-- | lib/libutil/login_cap.h | 115 | ||||
-rw-r--r-- | lib/libutil/login_class.c | 499 | ||||
-rw-r--r-- | lib/libutil/login_ok.c | 247 | ||||
-rw-r--r-- | lib/libutil/login_times.c | 179 |
10 files changed, 1700 insertions, 1034 deletions
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index de882b0..8ac63d2 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -4,11 +4,13 @@ LIB= util SHLIB_MAJOR= 2 SHLIB_MINOR= 2 CFLAGS+=-Wall -DLIBC_SCCS -I${.CURDIR} -I/sys +#CFLAGS+=LOGIN_CAP_AUTH SRCS= login.c login_tty.c logout.c logwtmp.c pty.c setproctitle.c \ login_cap.c login_class.c login_auth.c login_times.c login_ok.c \ - uucplock.c + _secure_path.c uucplock.c MAN3+= login.3 login_tty.3 logout.3 logwtmp.3 pty.3 setproctitle.3 \ - login_cap.3 login_class.3 login_times.3 login_ok.3 uucplock.3 + login_cap.3 login_class.3 login_times.3 login_ok.3 \ + _secure_path.3 uucplock.3 MAN5+= login.conf.5 MLINKS+= pty.3 openpty.3 pty.3 forkpty.3 MLINKS+=login_cap.3 login_getclassbyname.3 login_cap.3 login_close.3 \ diff --git a/lib/libutil/_secure_path.3 b/lib/libutil/_secure_path.3 new file mode 100644 index 0000000..6387b73 --- /dev/null +++ b/lib/libutil/_secure_path.3 @@ -0,0 +1,70 @@ +.\" Copyright (c) 1997 David Nugent <davidn@blaze.net.au> +.\" 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 author +.\" David Nugent. +.\" 5. Modifications may be freely made to this file providing the above +.\" conditions are met. +.\" +.\" $Id$ +.\" +.Dd May 2, 1997 +.Os FreeBSD +.Dt _SECURE_PATH 3 +.Sh NAME +.Nm _secure_path +.Nd determine if a file appears to be secure +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <libutil.h> +.Ft int +.Fn _secure_path "const char *path" "uid_t uid" "gid_t gid" +.Pp +.Sh DESCRIPTION +This function does some basic security checking on a given path. +It is intended to be used by processes running with root privileges +in order to decide whether or not to trust the contents of a given +file. +It uses a method often used to detect system compromise. +.Pp +A file is considered 'secure' if it meets the following conditions: +.Bl -enum +.It +The file exists, and is a regular file (not a symlink, device +special or named pipe, etc.), +.It +Is not world writable. +.It +Is owned by the given uid, if uid is not -1, +.It +Is not group wriable or it has group ownership by the given +gid, if gid is not -1. +.El +.Sh RETURN VALUES +This function returns zero if the file exists and may be +considered secure, -2 if the file does not exist, and +-1 otherwise to indicate a security failure. +.Xr syslog 3 , +is used to log any failure of this function, including the +reason, at LOG_ERR priority. +.Sh BUGS +The checks carried out are rudamentary and no attempt is made +to eliminate race conditions between use of this function and +access to the file referenced. +.Sh SEE ALSO +.Xr lstat 3 , +.Xr syslog 3 . +.Sh HISTORY +Code from which this function was derived was contributed to the +FreeBSD project by Berkeley Software Design, Inc. diff --git a/lib/libutil/_secure_path.c b/lib/libutil/_secure_path.c new file mode 100644 index 0000000..38a974e --- /dev/null +++ b/lib/libutil/_secure_path.c @@ -0,0 +1,72 @@ +/*- + * 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. + * + * $Id$ + */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <syslog.h> +#include <errno.h> +#include <libutil.h> + +/* + * Check for common security problems on a given path + * It must be: + * 1. A regular file, and exists + * 2. Owned and writaable 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 (uid != -1 && sb.st_uid != uid) { + if (uid == 0) + msg = "%s: %s is not owned by root"; + else + msg = "%s: %s is not owned by uid %d"; + } else if (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/lib/libutil/libutil.h b/lib/libutil/libutil.h index 35c1163..3db5263 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -18,7 +18,7 @@ * 5. Modifications may be freely made to this file providing the above * conditions are met. * - * $Id: libutil.h,v 1.5 1997/03/30 12:11:27 brian Exp $ + * $Id: libutil.h,v 1.6 1997/03/31 22:47:53 brian Exp $ */ #ifndef _LIBUTIL_H_ @@ -44,6 +44,7 @@ int forkpty __P((int *amaster, char *name, char *uu_lockerr __P((int uu_lockresult)); int uu_lock __P((char *ttyname)); int uu_unlock __P((char *ttyname)); +int _secure_path __P((const char *path, uid_t uid, gid_t gid)); __END_DECLS #define UU_LOCK_INUSE (1) diff --git a/lib/libutil/login_auth.c b/lib/libutil/login_auth.c index 565ddc3..695b006 100644 --- a/lib/libutil/login_auth.c +++ b/lib/libutil/login_auth.c @@ -4,6 +4,10 @@ * David Nugent <davidn@blaze.net.au> * All rights reserved. * + * Portions 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: @@ -21,17 +25,19 @@ * * Low-level routines relating to the user capabilities database * - * $Id$ + * $Id: login_auth.c,v 1.6 1997/02/22 15:08:18 peter Exp $ */ #include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/stat.h> +#include <sys/param.h> #include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> +#include <ctype.h> #include <pwd.h> #include <stdlib.h> #include <string.h> @@ -40,297 +46,575 @@ #include <login_cap.h> #include <stdarg.h> #include <paths.h> +#include <sys/socket.h> #include <sys/wait.h> +#include <err.h> +#include <libutil.h> + +#ifdef LOGIN_CAP_AUTH +/* + * Comment from BSDI's authenticate.c module: + * NOTE: THIS MODULE IS TO BE DEPRECATED. FUTURE VERSIONS OF BSD/OS WILL + * HAVE AN UPDATED API, THOUGH THESE FUNCTIONS WILL CONTINUE TO BE AVAILABLE + * FOR BACKWARDS COMPATABILITY + */ -#ifdef RLIM_LONG -# define STRTOV strtol -#else -# define STRTOV strtoq -#endif -#define AUTHMAXLINES 1024 -#define AUTHMAXARGS 16 +#define AUTHMAXSPOOL (8 * 1024) /* Max size of authentication data */ +#define AUTHCOMM_FD 3 /* Handle used to read/write auth data */ + +struct rmfiles { + struct rmfiles *next; + char file[1]; +}; -struct auth_info { - int reject; - int auths; - int env_count; - char **env; - int file_count; - char **files; +struct authopts { + struct authopts *next; + char opt[1]; }; -static struct auth_info auth_info; +static char *spoolbuf = NULL; +static int spoolidx = 0; +static struct rmfiles *rmfirst = NULL; +static struct authopts *optfirst = NULL; + /* - * free_auth_info() - * Go through the auth_info structure, and free() anything of interest. - * This includes the string arrays, and any individual element. - * All part of being environmentally conscious ;). + * Setup a known environment for all authentication scripts. */ -static void -free_auth_info(void) +static char *auth_environ[] = { + "PATH=" _PATH_DEFPATH, + "SHELL=" _PATH_BSHELL, + NULL, +}; + + + +/* + * nextline() + * Get the next line from the data buffer collected from + * the authentication program. This function relies on the + * fact that lines are nul terminated. + */ + +static char * +nextline(int *idx) { - int i; - - auth_info.reject = 0; - auth_info.auths = 0; - if (auth_info.env) { - for (i = 0; i < auth_info.env_count; i++) { - if (auth_info.env[i]) - free(auth_info.env[i]); - } - free(auth_info.env); - auth_info.env = NULL; - } - if (auth_info.files) { - for (i = 0; i < auth_info.file_count; i++) { - if (auth_info.files[i]) - free(auth_info.files[i]); + char *ptr = NULL; + + if (spoolbuf != NULL && *idx < spoolidx) { + ptr = spoolbuf + *idx; + *idx += strlen(ptr) + 1; } - free(auth_info.files); - auth_info.files = NULL; - } + return ptr; } /* - * collect_info() - * Read from <fd>, a list of authorization commands. - * These commands are: - * reject - * authorize [root|secure] - * setenv <name>[ <value>] - * remove <file> - * A single reject means the entire thing is bad; - * multiple authorize statements can be present (it would be - * silly, but that's what the spec says). - * The commands are collected, and are accted upon by: - * auth_scan() -- check for authorization or rejection - * auth_rmfiles() -- remove the specified files - * auth_env() -- set the specified environment variables - * We only get up to AUTHMAXLINES lines of input from the program. + * spooldata() + * Read data returned on authentication backchannel and + * stuff it into our spool buffer. We also replace \n with nul + * to make parsing easier later. */ -#define STRSIZEOF(x) (sizeof(x)-1) -static void -collect_info(int fd) + +static int +spooldata(int fd) { - char *line; - FILE *fp; - char *ptr; - size_t len; - int line_count = 0; - - fp = fdopen(fd, "r"); - - while ((line = fgetln(fp, &len)) != NULL) { - if (++line_count > AUTHMAXLINES) - break; - if (len && line[len-1] == '\n') - --len; - line[len] = '\0'; /* Terminate */ - if (strncasecmp(line, BI_REJECT, STRSIZEOF(BI_REJECT)) == 0) { - auth_info.reject = 1; - } else if (strncasecmp(line, BI_AUTH, STRSIZEOF(BI_AUTH)) == 0) { - ptr = line + STRSIZEOF(BI_AUTH); - ptr += strspn(ptr, " \t"); - if (!*ptr) - auth_info.auths |= AUTH_OKAY; - else if (strncasecmp(ptr, BI_ROOTOKAY, STRSIZEOF(BI_ROOTOKAY)) == 0) - auth_info.auths |= AUTH_ROOTOKAY; - else if (strncasecmp(ptr, BI_SECURE, STRSIZEOF(BI_SECURE)) == 0) - auth_info.auths |= AUTH_SECURE; - } else if (strncasecmp(line, BI_SETENV, STRSIZEOF(BI_SETENV)) == 0) { - ptr = line + STRSIZEOF(BI_SETENV); - ptr += strspn(ptr, " \t"); - if (*ptr) { - char **tmp = realloc(auth_info.env, sizeof(char*) * (auth_info.env_count + 1)); - if (tmp != NULL) { - auth_info.env = tmp; - if ((auth_info.env[auth_info.env_count] = strdup(ptr)) != NULL) - auth_info.env_count++; - } - } - } else if (strncasecmp(line, BI_REMOVE, STRSIZEOF(BI_REMOVE)) == 0) { - ptr = line + STRSIZEOF(BI_REMOVE); - ptr += strspn(ptr, " \t"); - if (*ptr) { - char **tmp = realloc(auth_info.files, sizeof(char*) * (auth_info.file_count + 1)); - if (tmp != NULL) { - auth_info.files = tmp; - if ((auth_info.files[auth_info.file_count] = strdup(ptr)) != NULL) - auth_info.file_count++; + + if (spoolbuf) + free(spoolbuf); + spoolidx = 0; + + if (spoolbuf == NULL && (spoolbuf = malloc(AUTHMAXSPOOL)) == NULL) + syslog(LOG_ERR, "authbuffer malloc: %m"); + + else while (spoolidx < sizeof(spoolbuf) - 1) { + int r = read(fd, spoolbuf + spoolidx, sizeof(spoolbuf)-spoolidx); + char *b; + + if (r <= 0) { + spoolbuf[spoolidx] = '\0'; + return 0; } - } + /* + * Convert newlines into NULs to allow + * easier scanning of the file. + */ + while ((b = memchr(spoolbuf + spoolidx, '\n', r)) != NULL) + *b = '\0'; + spoolidx += r; } - } - fclose(fp); + return -1; } - + /* - * authenticate() + * auth_check() * Starts an auth_script() for the given <user>, with a class <class>, * style <style>, and service <service>. <style> is necessary, * as are <user> and <class>, but <service> is optional -- it defaults * to "login". * Since auth_script() expects an execl'able program name, authenticate() * also concatenates <style> to _PATH_AUTHPROG. - * Lastly, calls auth_scan(AUTH_NONE) to see if there are any "reject" statements, + * Lastly, calls auth_scan(0) to see if there are any "reject" statements, * or lack of "auth" statements. * Returns -1 on error, 0 on rejection, and >0 on success. * (See AUTH_* for the return values.) * */ + int -authenticate(const char * name, const char * class, const char * style, const char *service) +auth_check(const char *name, const char *clss, const char *style, + const char *service, int *status) { - int retval; + int _status; - if (style == NULL || *style == '\0') - retval = -1; - else { - char buf[sizeof(_PATH_AUTHPROG) + 64]; + if (status == NULL) + status = &_status; + *status = 0; - if (service == NULL || *service == '\0') - service = LOGIN_DEFSERVICE; + if (style != NULL) { + char path[MAXPATHLEN]; - free_auth_info(); + if (service == NULL) + service = LOGIN_DEFSERVICE; - if (snprintf(buf, sizeof buf, _PATH_AUTHPROG "%s", style) >= sizeof buf) - retval = -1; - else { - retval = auth_script(buf, style, "-s", service, name, class, NULL); - if (retval >= 0) - retval = auth_scan(AUTH_NONE); + snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); + if (auth_script(path, style, "-s", service, name, clss, 0)) + status = 0; + else + *status = auth_scan(0); + + return *status & AUTH_ALLOW; + } + return -1; +} + + +int +auth_response(const char *name, const char *class, const char *style, + const char *service, int *status, + const char *challenge, const char *response) +{ + int _status; + + if (status == NULL) + status = &_status; + *status = 0; + + if (style != NULL) { + int datalen; + char *data; + + if (service == NULL) + service = LOGIN_DEFSERVICE; + + datalen = strlen(challenge) + strlen(response) + 2; + + if ((data = malloc(datalen)) == NULL) { + syslog(LOG_ERR, "auth_response: %m"); + warnx("internal resource failure"); + } else { + char path[MAXPATHLEN]; + + snprintf(data, datalen, "%s%c%s", challenge, 0, response); + snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); + if (auth_script_data(data, datalen, path, style, "-s", service, + name, class, 0)) + *status = 0; + else + *status = auth_scan(0); + free(data); + return (*status & AUTH_ALLOW); + } + } + return -1; +} + + +int +auth_approve(login_cap_t *lc, const char *name, const char *service) +{ + int r = -1; + char path[MAXPATHLEN]; + + if (lc == NULL) { + if (strlen(name) > MAXPATHLEN) { + syslog(LOG_ERR, "%s: username too long", name); + warnx("username too long"); + } else { + struct passwd *pwd; + char *p; + + pwd = getpwnam(name); + if (pwd == NULL && (p = strchr(name, '.')) != NULL) { + int i = p - name; + + if (i >= MAXPATHLEN) + i = MAXPATHLEN - 1; + strncpy(path, name, i); + path[i] = '\0'; + pwd = getpwnam(path); /* Fixed bug in BSDI code... */ + } + if ((lc = login_getpwclass(pwd ? pwd->pw_class : NULL)) == NULL) + warnx("unable to classify user '%s'", name); + } + } + + if (lc != NULL) { + char *approve; + char *s; + + if (service != NULL) + service = LOGIN_DEFSERVICE; + + snprintf(path, sizeof(path), "approve-%s", service); + + if ((approve = login_getcapstr(lc, s = path, NULL, NULL)) == NULL && + (approve = login_getcapstr(lc, s = "approve", NULL, NULL)) == NULL) + r = AUTH_OKAY; + else { + + if (approve[0] != '/') { + syslog(LOG_ERR, "Invalid %s script: %s", s, approve); + warnx("invalid path to approval script"); + } else { + char *s; + + s = strrchr(approve, '/') + 1; + if (auth_script(approve, s, name, + lc->lc_class, service, 0) == 0 && + (r = auth_scan(AUTH_OKAY) & AUTH_ALLOW) != 0) + auth_env(); + } + } + } + return r; +} + + +void +auth_env(void) +{ + int idx = 0; + char *line; + + while ((line = nextline(&idx)) != NULL) { + if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { + line += sizeof(BI_SETENV) - 1; + if (*line && isspace(*line)) { + char *name; + char ch, *p; + + while (*line && isspace(*line)) + ++line; + name = line; + while (*line && !isspace(*line)) + ++line; + ch = *(p = line); + if (*line) + ++line; + if (setenv(name, line, 1)) + warn("setenv(%s, %s)", name, line); + *p = ch; + } + } + } +} + + +char * +auth_value(const char *what) +{ + int idx = 0; + char *line; + + while ((line = nextline(&idx)) != NULL) { + if (!strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1)) { + char *name; + + line += sizeof(BI_VALUE) - 1; + while (*line && isspace(*line)) + ++line; + name = line; + if (*line) { + int i; + char ch, *p; + + ch = *(p = line); + *line++ = '\0'; + i = strcmp(name, what); + *p = ch; + if (i == 0) + return auth_mkvalue(line); + } + } + } + return NULL; +} + +char * +auth_mkvalue(const char *value) +{ + char *big, *p; + + big = malloc(strlen(value) * 4 + 1); + if (big != NULL) { + for (p = big; *value; ++value) { + switch (*value) { + case '\r': + *p++ = '\\'; + *p++ = 'r'; + break; + case '\n': + *p++ = '\\'; + *p++ = 'n'; + break; + case '\\': + *p++ = '\\'; + *p++ = *value; + break; + case '\t': + case ' ': + if (p == big) + *p++ = '\\'; + *p++ = *value; + break; + default: + if (!isprint(*value)) { + *p++ = '\\'; + *p++ = ((*value >> 6) & 0x3) + '0'; + *p++ = ((*value >> 3) & 0x7) + '0'; + *p++ = ((*value ) & 0x7) + '0'; + } else + *p++ = *value; + break; + } + } + *p = '\0'; + big = realloc(big, strlen(big) + 1); + } + return big; +} + + +#define NARGC 63 +static int +_auth_script(const char *data, int nbytes, const char *path, va_list ap) +{ + int r, argc, status; + int pfd[2]; + pid_t pid; + struct authopts *e; + char *argv[NARGC+1]; + + r = -1; + argc = 0; + for (e = optfirst; argc < (NARGC - 1) && e != NULL; e = e->next) { + argv[argc++] = "-v"; + argv[argc++] = e->opt; + } + while (argc < NARGC && (argv[argc] = va_arg(ap, char *)) != NULL) + ++argc; + argv[argc] = NULL; + + if (argc >= NARGC && va_arg(ap, char *)) + syslog(LOG_ERR, "too many arguments"); + else if (_secure_path(path, 0, 0) < 0) { + syslog(LOG_ERR, "%s: path not secure", path); + warnx("invalid script: %s", path); + } else if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) < 0) { + syslog(LOG_ERR, "unable to create backchannel %m"); + warnx("internal resource failure"); + } else switch (pid = fork()) { + case -1: /* fork() failure */ + close(pfd[0]); + close(pfd[1]); + syslog(LOG_ERR, "fork %s: %m", path); + warnx("internal resource failure"); + break; + case 0: /* child process */ + close(pfd[0]); + if (pfd[1] != AUTHCOMM_FD) { + if (dup2(pfd[1], AUTHCOMM_FD) < 0) + err(1, "dup backchannel"); + close(pfd[1]); + } + for (r = getdtablesize(); --r > AUTHCOMM_FD; ) + close(r); + execve(path, argv, auth_environ); + syslog(LOG_ERR, "exec %s: %m", path); + err(1, path); + default: /* parent */ + close(pfd[1]); + if (data && nbytes) + write(pfd[0], data, nbytes); + r = spooldata(pfd[0]); + close(pfd[0]); + if (waitpid(pid, &status, 0) < 0) { + syslog(LOG_ERR, "%s: waitpid: %m", path); + warnx("internal failure"); + r = -1; + } else { + if (r != 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + r = -1; + } + /* kill the buffer if it is of no use */ + if (r != 0) { + free(spoolbuf); + spoolbuf = NULL; + spoolidx = 0; + } + break; } - } - return retval; + return r; } + /* * auth_script() * Runs an authentication program with specified arguments. * It sets up file descriptor 3 for the program to write to; * it stashes the output somewhere. The output of the program * consists of statements: - * reject + * reject [challenge|silent] * authorize [root|secure] * setenv <name> [<value>] * remove <file> * - * Terribly exciting, isn't it? There is no limit specified in - * BSDi's API for how much output can be present, but we should - * keep it fairly small, I think. - * No more than AUTHMAXLINES lines. + * Terribly exciting, isn't it? + * Output cannot exceed AUTHMAXSPOOL characters. */ int -auth_script(const char * path, ...) +auth_script(const char *path, ...) { - va_list ap; - int pid, status; - int argc = 0; - int p[2]; /* pipes */ - char *argv[AUTHMAXARGS+1]; - - va_start(ap, path); - while (argc < AUTHMAXARGS && (argv[argc++] = va_arg(ap, char*)) != NULL) - ; - argv[argc] = NULL; - va_end(ap); - - fflush(NULL); - - if (pipe(p) >= 0) { - if ((pid = fork()) == -1) { - close(p[0]); - close(p[1]); - } else if (pid == 0) { /* Child */ - close(p[0]); - dup2(p[1], 3); - if (setenv("PATH", _PATH_DEFPATH, 1)==0 && setenv("SHELL", _PATH_BSHELL, 1)==0) - execv(path, argv); - _exit(1); - } else { - close(p[1]); - collect_info(p[0]); - if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status) && !WEXITSTATUS(status)) - return 0; - } - } - return -1; -} + int r; + va_list ap; + va_start(ap, path); + r = _auth_script(NULL, 0, path, ap); + va_end(ap); + return r; +} -/* - * auth_env() - * Processes the stored "setenv" lines from the stored authentication - * output. - */ int -auth_env(void) +auth_script_data(const char *data, int nbytes, const char *path, ...) +{ + int r; + va_list ap; + + va_start(ap, path); + r = _auth_script(data, nbytes, path, ap); + va_end(ap); + return r; +} + + +static void +add_rmlist(const char *file) { - int i; - - for (i = 0; i < auth_info.env_count; i++) { - char *nam = auth_info.env[i]; - char *ptr = nam + strcspn(nam, " \t="); - if (*ptr) { - *ptr++ = '\0'; - ptr += strspn(ptr, " \t"); + struct rmfiles *rm; + + if ((rm = malloc(sizeof(struct rmfiles) + strlen(file) + 1)) == NULL) + syslog(LOG_ERR, "add_rmfile malloc: %m"); + else { + strcpy(rm->file, file); + rm->next = rmfirst; + rmfirst = rm; } - setenv(nam, ptr, 1); - } - return 0; } -/* - * auth_scan() - * Goes through the output of the auth_script/authenticate, and - * checks for a failure or authentication. - * <ok> is a default authentication value -- if there are no - * rejection or authentication statements, then it is returned - * unmodified. - * AUTH_NONE is returned if there were any reject statements - * from the authentication program (invoked by auth_script()), and - * AUTH, AUTH_ROOTOKAY, and/or AUTH_SECURE are returned if the - * appropriate directives were found. Note that AUTH* are - * *bitmasks*! - */ +int +auth_scan(int okay) +{ + int idx = 0; + char *line; + + while ((line = nextline(&idx)) != NULL) { + if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) { + line += sizeof(BI_REJECT) - 1; + while (*line && isspace(*line)) + ++line; + if (*line) { + if (!strcasecmp(line, "silent")) + return AUTH_SILENT; + if (!strcasecmp(line, "challenge")) + return AUTH_CHALLENGE; + } + return 0; + } else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) { + line += sizeof(BI_AUTH) - 1; + while (*line && isspace(*line)) + ++line; + if (*line == '\0') + okay |= AUTH_OKAY; + else if (!strcasecmp(line, "root")) + okay |= AUTH_ROOTOKAY; + else if (!strcasecmp(line, "secure")) + okay |= AUTH_SECURE; + } + else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) { + line += sizeof(BI_REMOVE) - 1; + while (*line && isspace(*line)) + ++line; + if (*line) + add_rmlist(line); + } + } + + return okay; +} + int -auth_scan(int ok) +auth_setopt(const char *n, const char *v) { - if (auth_info.reject) - return 0; - return ok | auth_info.auths; + int r; + struct authopts *e; + + if ((e = malloc(sizeof(*e) + strlen(n) + strlen(v) + 1)) == NULL) + r = -1; + else { + sprintf(e->opt, "%s=%s", n, v); + e->next = optfirst; + optfirst = e; + r = 0; + } + return r; } -/* - * auth_rmfiles() - * Removes any files that the authentication program said needed to be - * removed, said files having come from a previous execution of - * auth_script(). - */ +void +auth_clropts(void) +{ + struct authopts *e; -int + while ((e = optfirst) != NULL) { + optfirst = e->next; + free(e); + } +} + + +void auth_rmfiles(void) { - int i = auth_info.file_count; - while (i-- > 0) { - unlink(auth_info.files[i]); - free(auth_info.files[i]); - auth_info.files[i] = NULL; - } - return 0; + struct rmfiles *rm; + + while ((rm = rmfirst) != NULL) { + unlink(rm->file); + rmfirst = rm->next; + free(rm); + } } +#endif + /* * auth_checknologin() @@ -370,6 +654,7 @@ auth_checknologin(login_cap_t *lc) * reading, it prints it out to stdout, and then exits. Otherwise, * it returns 0 (meaning no nologin file). */ + int auth_cat(const char *file) { @@ -379,7 +664,7 @@ auth_cat(const char *file) if ((fd = open(file, O_RDONLY)) < 0) return 0; while ((count = read(fd, buf, sizeof(buf))) > 0) - write(fileno(stdout), buf, count); + (void)write(fileno(stdout), buf, count); close(fd); return 1; } diff --git a/lib/libutil/login_cap.c b/lib/libutil/login_cap.c index f507715..633da20 100644 --- a/lib/libutil/login_cap.c +++ b/lib/libutil/login_cap.c @@ -4,6 +4,10 @@ * David Nugent <davidn@blaze.net.au> * 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: @@ -21,13 +25,14 @@ * * Low-level routines relating to the user capabilities database * - * $Id: login_cap.c,v 1.10 1997/02/22 15:08:20 peter Exp $ + * $Id: login_cap.c,v 1.11 1997/02/27 00:24:05 ache Exp $ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> +#include <fcntl.h> #include <unistd.h> #include <sys/types.h> @@ -35,13 +40,19 @@ #include <sys/resource.h> #include <sys/param.h> #include <pwd.h> +#include <libutil.h> +#include <syslog.h> #include <login_cap.h> -#ifdef RLIM_LONG -# define STRTOV strtol -#else -# define STRTOV strtoq -#endif +/* + * 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; @@ -51,30 +62,33 @@ static size_t internal_arraysz = 0; static char ** internal_array = NULL; static char * -allocstr(char * str) +allocstr(char *str) { - char * p; - size_t sz = strlen(str) + 1; /* realloc() only if necessary */ - if (sz <= internal_stringsz) - p = strcpy(internal_string, str); - else if ((p = realloc(internal_string, sz)) != NULL) { - internal_stringsz = sz; - internal_string = strcpy(p, str); - } - return p; + char *p; + + size_t sz = strlen(str) + 1; /* realloc() only if necessary */ + if (sz <= internal_stringsz) + p = strcpy(internal_string, str); + else if ((p = realloc(internal_string, sz)) != NULL) { + internal_stringsz = sz; + internal_string = strcpy(p, str); + } + return p; } + static char ** allocarray(size_t sz) { - char ** p; - if (sz <= internal_arraysz) - p = internal_array; - else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) { - internal_arraysz = sz; - internal_array = p; - } - return p; + 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; } @@ -89,38 +103,43 @@ allocarray(size_t sz) static char ** arrayize(char *str, const char *chars, int *size) { - int i; - char *ptr; - char **res = NULL; - - for (i = 0, ptr = str; *ptr; i++) { - int count = strcspn(ptr, chars); - ptr += count; - if (*ptr) - ++ptr; - } - - if ((ptr = allocstr(str)) == NULL) { - res = NULL; - i = 0; - } else if ((res = allocarray(++i)) == NULL) { - free(str); + int i; + char *ptr; + char **res = NULL; + + /* count the sub-strings */ + for (i = 0, ptr = str; *ptr; i++) { + int count = strcspn(ptr, chars); + ptr += count; + if (*ptr) + ++ptr; + } + i = 0; - } else { - for (i = 0; *ptr; i++) { - int count = strcspn(ptr, chars); - res[i] = ptr; - ptr += count; - if (*ptr) - *ptr++ = '\0'; + /* alloc the array */ + if ((ptr = allocstr(str)) != NULL) { + if ((res = allocarray(++i)) == NULL) + free(str); + else { + /* now split the string */ + while (*ptr) { + int count = strcspn(ptr, chars); + res[i++] = ptr; + ptr += count; + if (*ptr) + *ptr++ = '\0'; + } + res[i] = NULL; + } } - res[i] = 0; - } - if (size) - *size = i; - return res; + + if (size) + *size = i; + + return res; } + /* * login_close() * Frees up all resources relating to a login class @@ -130,20 +149,20 @@ arrayize(char *str, const char *chars, int *size) void login_close(login_cap_t * lc) { - if (lc) { - free(lc->lc_style); - free(lc->lc_class); - free(lc); - if (--lc_object_count == 0) { - free(internal_string); - free(internal_array); - internal_array = NULL; - internal_arraysz = 0; - internal_string = NULL; - internal_stringsz = 0; - cgetclose(); + if (lc) { + free(lc->lc_style); + free(lc->lc_class); + free(lc); + if (--lc_object_count == 0) { + free(internal_string); + free(internal_array); + internal_array = NULL; + internal_arraysz = 0; + internal_string = NULL; + internal_stringsz = 0; + cgetclose(); + } } - } } @@ -159,40 +178,101 @@ login_close(login_cap_t * lc) */ login_cap_t * -login_getclassbyname(char const * name, char const * dir) +login_getclassbyname(char const *name, const struct passwd *pwd) { - login_cap_t *lc = malloc(sizeof(login_cap_t)); + login_cap_t *lc; - if (lc != NULL) { - int i = 0; - char userpath[MAXPATHLEN]; - static char *login_dbarray[] = { NULL, NULL, NULL }; - - if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, _FILE_LOGIN_CONF) < MAXPATHLEN) - login_dbarray[i++] = userpath; - else - login_dbarray[i++] = _PATH_LOGIN_CONF; - login_dbarray[i ] = NULL; - - lc->lc_cap = lc->lc_class = lc->lc_style = NULL; - - if ((name == NULL || cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0) && - cgetent(&lc->lc_cap, login_dbarray, (char*)(name = LOGIN_DEFCLASS)) != 0) { + if ((lc = malloc(sizeof(login_cap_t))) != NULL) { + int r, i = 0; + const char *msg = NULL; + const char *dir = (pwd == NULL) ? NULL : pwd->pw_dir; + char userpath[MAXPATHLEN]; + + static char *login_dbarray[] = { NULL, NULL, NULL }; + + if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, + _FILE_LOGIN_CONF) < MAXPATHLEN) { + login_dbarray[i] = userpath; + if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1) + i++; /* only use 'secure' data */ + } + if (_secure_path(_PATH_LOGIN_CONF, 0, 0) != -1) + login_dbarray[i++] = _PATH_LOGIN_CONF; + login_dbarray[i] = NULL; + + memset(lc, 0, sizeof(login_cap_t)); + lc->lc_cap = lc->lc_class = lc->lc_style = NULL; + + if (name == NULL || *name == '\0') + name = LOGIN_DEFCLASS; + + switch (cgetent(&lc->lc_cap, login_dbarray, (char*)name)) { + case -1: /* Failed, entry does not exist */ + if (strcmp(name, LOGIN_MECLASS) == 0) + break; /* Don't retry default on 'me' */ + if (i == 0) + r = -1; + else if ((r = open(login_dbarray[0], O_RDONLY)) >= 0) + close(r); + /* + * If there's at least one login class database, + * and we aren't searching for a default class + * then complain about a non-existent class. + */ + if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0) + syslog(LOG_ERR, "login_getclass: unknown class '%s'", name); + /* fall-back to default class */ + name = LOGIN_DEFCLASS; + msg = "%s: no default/fallback class '%s'"; + if (cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0 && r >= 0) + break; + /* Fallthru - just return system defaults */ + case 0: /* success! */ + if ((lc->lc_class = strdup(name)) != NULL) { + ++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 (msg != NULL) + syslog(LOG_ERR, msg, "login_getclass", name); free(lc); - lc = NULL; - } else { - ++lc_object_count; - lc->lc_class = strdup(name); } - } - return lc; + return NULL; } /* * login_getclass() + * Get the login class for the system (only) login class database. + * Return a filled-out login_cap_t structure, including + * class name, and the capability record buffer. + */ + +login_cap_t * +login_getclass(const char *cls) +{ + return login_getclassbyname(cls, NULL); +} + + +/* + * login_getclass() * Get the login class for a given password entry from * the system (only) login class database. * If the password entry's class field is not set, or @@ -203,14 +283,16 @@ login_getclassbyname(char const * name, char const * dir) */ login_cap_t * -login_getclass(const struct passwd *pwd) +login_getpwclass(const struct passwd *pwd) { - const char * class = NULL; - if (pwd != NULL) { - if ((class = pwd->pw_class) == NULL || *class == '\0') - class = (pwd->pw_uid == 0) ? "root" : NULL; - } - return login_getclassbyname(class, 0); + const char *cls = NULL; + + if (pwd != NULL) { + cls = pwd->pw_class; + if (cls == NULL || *cls == '\0') + cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS; + } + return login_getclassbyname(cls, pwd); } @@ -218,20 +300,12 @@ login_getclass(const struct passwd *pwd) * login_getuserclass() * Get the login class for a given password entry, allowing user * overrides via ~/.login_conf. - * ### WAS: If the password entry's class field is not set, - * ####### or the class specified does not exist, then use - * If an entry with the recordid "me" does not exist, then use - * the default of LOGIN_DEFCLASS (ie. "default"). - * Return a filled-out login_cap_t structure, including - * class name, and the capability record buffer. */ login_cap_t * login_getuserclass(const struct passwd *pwd) { - const char * class = "me"; /* (pwd == NULL) ? NULL : pwd->pw_class; */ - const char * home = (pwd == NULL) ? NULL : pwd->pw_dir; - return login_getclassbyname(class, home); + return login_getclassbyname(LOGIN_MECLASS, pwd); } @@ -246,18 +320,15 @@ login_getuserclass(const struct passwd *pwd) char * login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *error) { - char *res; - int ret; + char *res; + int ret; - if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0') - return def; + if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0') + return def; - if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) { - return def; - } else if (ret >= 0) - return res; - else - return error; + if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) + return def; + return (ret >= 0) ? res : error; } @@ -269,15 +340,15 @@ login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *error) */ char ** -login_getcaplist(login_cap_t *lc, const char * cap, const char * chars) +login_getcaplist(login_cap_t *lc, const char *cap, const char *chars) { - char * lstring; + char *lstring; - if (chars == NULL) - chars = ", \t"; - if ((lstring = login_getcapstr(lc, (char*)cap, NULL, NULL)) != NULL) - return arrayize(lstring, chars, NULL); - return NULL; + if (chars == NULL) + chars = ", \t"; + if ((lstring = login_getcapstr(lc, (char*)cap, NULL, NULL)) != NULL) + return arrayize(lstring, chars, NULL); + return NULL; } @@ -292,21 +363,124 @@ login_getcaplist(login_cap_t *lc, const char * cap, const char * chars) char * login_getpath(login_cap_t *lc, const char *cap, char * error) { - char *str = login_getcapstr(lc, (char*)cap, NULL, NULL); - - if (str == NULL) - str = error; - else { - char *ptr = str; - - while (*ptr) { - int count = strcspn(ptr, ", \t"); - ptr += count; - if (*ptr) - *ptr++ = ':'; + char *str; + + if ((str = login_getcapstr(lc, (char*)cap, NULL, NULL)) == NULL) + str = error; + else { + char *ptr = str; + + while (*ptr) { + int count = strcspn(ptr, ", \t"); + ptr += count; + if (*ptr) + *ptr++ = ':'; + } + } + return str; +} + + +static int +isinfinite(const char *s) +{ + static const char *infs[] = { + "infinity", + "inf", + "unlimited", + "unlimit", + "-1", + NULL + }; + const char **i = &infs[0]; + + while (*i != NULL) { + if (strcasecmp(s, *i) == 0) + return 1; + ++i; + } + return 0; +} + + +static u_quad_t +rmultiply(u_quad_t n1, u_quad_t n2) +{ + u_quad_t m, r; + int b1, b2; + + static int bpw = 0; + + /* Handle simple cases */ + if (n1 == 0 || n2 == 0) + return 0; + if (n1 == 1) + return n2; + if (n2 == 1) + return n1; + + /* + * sizeof() returns number of bytes needed for storage. + * This may be different from the actual number of useful bits. + */ + if (!bpw) { + bpw = sizeof(u_quad_t) * 8; + while (((u_quad_t)1 << (bpw-1)) == 0) + --bpw; } - } - return str; + + /* + * 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); } @@ -321,70 +495,80 @@ login_getpath(login_cap_t *lc, const char *cap, char * error) rlim_t login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) { - char *res, *ep; - int ret; - rlim_t tot; - - errno = 0; - if (lc == NULL || lc->lc_cap == NULL) - return def; - - /* - * Look for <cap> in lc_cap. - * If it's not there (-1), return <def>. - * If there's an error, return <error>. - */ - - if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) - return def; - else if (ret < 0) - return error; - - /* - * "inf" and "infinity" are two special cases for this. - */ - if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf")) - return RLIM_INFINITY; - - /* - * Now go through the string, turning something like 1h2m3s into - * an integral value. Whee. - */ - - errno = 0; - tot = 0; - while (*res) { - rlim_t tim = STRTOV(res, &ep, 0); - if ((ep == NULL) || (ep == res) || errno) { - return error; + char *res, *ep, *oval; + int r; + rlim_t tot; + + errno = 0; + if (lc == NULL || lc->lc_cap == NULL) + return def; + + /* + * Look for <cap> in lc_cap. + * If it's not there (-1), return <def>. + * If there's an error, return <error>. + */ + + if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) + return def; + else if (r < 0) { + 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 */ - tim *= 60L; - break; - case 'h': case 'H': /* hours */ - tim *= (60L * 60L); - break; - case 'd': case 'D': /* days */ - tim *= (60L * 60L * 24L); - break; - case 'w': case 'W': /* weeks */ - tim *= (60L * 60L * 24L * 7L); - case 'y': case 'Y': /* Years */ - /* I refuse to take leap years into account here. Sue me. */ - tim *= (60L * 60L * 24L * 365L); - default: - return error; + + /* "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; + case 'y': case 'Y': /* 365-day years */ + mult = 60L * 60L * 24L * 365L; + default: + goto invalid; + } + res = ep; + tot += rmultiply(tim, mult); + if (errno) + goto invalid; } - res = ep; - tot += tim; - } - return tot; + + return tot; } @@ -400,41 +584,47 @@ login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) rlim_t login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) { - char *ep, *res; - int ret; - rlim_t val; - - if (lc == NULL || lc->lc_cap == NULL) - return def; - - /* - * For BSDI compatibility, try for the tag=<val> first - */ - if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) { - long lval; + char *ep, *res; + int r; + rlim_t val; + + if (lc == NULL || lc->lc_cap == NULL) + return def; + /* - * String capability not present, so try for tag#<val> as numeric + * For BSDI compatibility, try for the tag=<val> first */ - if ((ret = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1) - return def; /* Not there, so return default */ - else if (ret < 0) - return error; - return (rlim_t)lval; - } - else if (ret < 0) - return error; - - if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf")) - return RLIM_INFINITY; - - errno = 0; - val = STRTOV(res, &ep, 0); - if ((ep == NULL) || (ep == res) || errno) - return error; - return val; + if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) { + long lval; + /* string capability not present, so try for tag#<val> as numeric */ + if ((r = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1) + return def; /* Not there, so return default */ + else if (r >= 0) + return (rlim_t)lval; + } + + if (r < 0) { + errno = ERANGE; + return error; + } + + if (isinfinite(res)) + return RLIM_INFINITY; + + errno = 0; + val = strtoq(res, &ep, 0); + if (ep == NULL || ep == res || errno != 0) { + syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s", + lc->lc_class, cap, res); + errno = ERANGE; + return error; + } + + return val; } + /* * login_getcapsize() * From the login_cap_t <lc>, extract the capability <cap>, which is @@ -444,55 +634,68 @@ login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) */ rlim_t -login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) { - char *ep, *res; - int ret; - rlim_t tot, mult; - - if (lc == NULL || lc->lc_cap == NULL) - return def; - - if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) - return def; - else if (ret < 0) - return error; - - /* - * "inf" and "infinity" are two special cases for this. - */ - if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf")) - return RLIM_INFINITY; - - errno = 0; - tot = 0; - while (*res) { - rlim_t val = STRTOV(res, &ep, 0); - if ((res == NULL) || (res == ep) || errno) - return error; - switch (*ep++) { - case 0: /* end of string */ - ep--; - mult = 1; - break; - case 'b': case 'B': /* 512-byte blocks */ - mult = 512; break; - case 'k': case 'K': /* 1024-byte Kilobytes */ - mult = 1024; break; - case 'm': case 'M': /* 1024-k kbytes */ - mult = 1024 * 1024; break; - case 'g': case 'G': /* 1Gbyte */ - mult = 1024 * 1024 * 1024; break; -#ifndef RLIM_LONG - case 't': case 'T': /* 1TBte */ - mult = 1024LL * 1024LL * 1024LL * 1024LL; break; -#endif - default: - return error; +login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) +{ + char *ep, *res, *oval; + int r; + rlim_t tot; + + if (lc == NULL || lc->lc_cap == NULL) + return def; + + if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) + return def; + else if (r < 0) { + errno = ERANGE; + return error; + } + + if (isinfinite(res)) + return RLIM_INFINITY; + + errno = 0; + tot = 0; + oval = res; + while (*res) { + rlim_t siz = strtoq(res, &ep, 0); + rlim_t mult = 1; + + if (ep == NULL || ep == res || errno != 0) { + invalid: + syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s", + lc->lc_class, cap, oval); + errno = ERANGE; + return error; + } + switch (*ep++) { + case 0: /* end of string */ + ep--; + break; + case 'b': case 'B': /* 512-byte blocks */ + mult = 512; + break; + case 'k': case 'K': /* 1024-byte Kilobytes */ + mult = 1024; + break; + case 'm': case 'M': /* 1024-k kbytes */ + mult = 1024 * 1024; + break; + case 'g': case 'G': /* 1Gbyte */ + mult = 1024 * 1024 * 1024; + break; + case 't': case 'T': /* 1TBte */ + mult = 1024LL * 1024LL * 1024LL * 1024LL; + break; + default: + goto invalid; + } + res = ep; + tot += rmultiply(siz, mult); + if (errno) + goto invalid; } - res = ep; - tot += (val * mult); - } - return tot; + + return tot; } @@ -506,9 +709,9 @@ login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) { int login_getcapbool(login_cap_t *lc, const char *cap, int def) { - if (lc == NULL || lc->lc_cap == NULL) - return def; - return (cgetcap(lc->lc_cap, (char *)cap, ':') != NULL); + if (lc == NULL || lc->lc_cap == NULL) + return def; + return (cgetcap(lc->lc_cap, (char *)cap, ':') != NULL); } @@ -535,38 +738,41 @@ login_getcapbool(login_cap_t *lc, const char *cap, int def) char * login_getstyle(login_cap_t *lc, char *style, const char *auth) { - int i; - char **authtypes = NULL; - char *auths= NULL; - char realauth[64]; - - static char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL }; - - if (auth != NULL && *auth != '\0' && - snprintf(realauth, sizeof realauth, "auth-%s", auth) < sizeof realauth) - authtypes = login_getcaplist(lc, realauth, NULL); - - if (authtypes == NULL) - authtypes = login_getcaplist(lc, "auth", NULL); - - if (authtypes == NULL) - authtypes = defauthtypes; - - /* - * We have at least one authtype now; auths is a comma-seperated - * (or space-separated) list of authentication types. We have to - * convert from this to an array of char*'s; authtypes then gets this. - */ - i = 0; - if (style != NULL && *style != '\0') { - while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0) - i++; - } - lc->lc_style = NULL; - if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL) - lc->lc_style = auths; - - return lc->lc_style; -} + int i; + char **authtypes = NULL; + char *auths= NULL; + char realauth[64]; + static char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL }; + if (auth != NULL && *auth != '\0') { + if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < sizeof realauth) + authtypes = login_getcaplist(lc, realauth, NULL); + } + + if (authtypes == NULL) + authtypes = login_getcaplist(lc, "auth", NULL); + + if (authtypes == NULL) + authtypes = defauthtypes; + + /* + * We have at least one authtype now; auths is a comma-seperated + * (or space-separated) list of authentication types. We have to + * convert from this to an array of char*'s; authtypes then gets this. + */ + i = 0; + if (style != NULL && *style != '\0') { + while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0) + i++; + } + + lc->lc_style = NULL; + if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL) + lc->lc_style = auths; + + if (lc->lc_style != NULL) + lc->lc_style = strdup(lc->lc_style); + + return lc->lc_style; +} diff --git a/lib/libutil/login_cap.h b/lib/libutil/login_cap.h index 4c92f0a..f380936 100644 --- a/lib/libutil/login_cap.h +++ b/lib/libutil/login_cap.h @@ -22,13 +22,15 @@ * Low-level routines relating to the user capabilities database * * Was login_cap.h,v 1.9 1997/05/07 20:00:01 eivind Exp - * $Id$ + * $Id: login_cap.h,v 1.1 1997/05/10 12:49:30 davidn Exp $ */ #ifndef _LOGIN_CAP_H_ #define _LOGIN_CAP_H_ #define LOGIN_DEFCLASS "default" +#define LOGIN_DEFROOTCLASS "root" +#define LOGIN_MECLASS "me" #define LOGIN_DEFSTYLE "passwd" #define LOGIN_DEFSERVICE "login" #define LOGIN_DEFUMASK 022 @@ -37,53 +39,57 @@ #define _FILE_LOGIN_CONF ".login_conf" #define _PATH_AUTHPROG "/usr/libexec/login_" -#define LOGIN_SETGROUP 0x0001 /* set group */ -#define LOGIN_SETLOGIN 0x0002 /* set login (via setlogin) */ -#define LOGIN_SETPATH 0x0004 /* set path */ -#define LOGIN_SETPRIORITY 0x0008 /* set priority */ -#define LOGIN_SETRESOURCES 0x0010 /* set resources (cputime, etc.) */ -#define LOGIN_SETUMASK 0x0020 /* set umask, obviously */ -#define LOGIN_SETUSER 0x0040 /* set user (via setuid) */ -#define LOGIN_SETENV 0x0080 /* set user environment */ -#define LOGIN_SETALL 0x00ff /* set everything */ - -#define BI_AUTH "authorize" -#define BI_AUTH2 "authorise" -#define BI_REJECT "reject" -#define BI_REMOVE "remove" -#define BI_ROOTOKAY "root" -#define BI_SECURE "secure" -#define BI_SETENV "setenv" - -#ifndef AUTH_NONE /* Protect against <rpc/auth.h> */ -#define AUTH_NONE 0x00 -#endif -#define AUTH_OKAY 0x01 -#define AUTH_ROOTOKAY 0x02 /* root login okay */ -#define AUTH_SECURE 0x04 /* secure login */ +#define LOGIN_SETGROUP 0x0001 /* set group */ +#define LOGIN_SETLOGIN 0x0002 /* set login (via setlogin) */ +#define LOGIN_SETPATH 0x0004 /* set path */ +#define LOGIN_SETPRIORITY 0x0008 /* set priority */ +#define LOGIN_SETRESOURCES 0x0010 /* set resources (cputime, etc.) */ +#define LOGIN_SETUMASK 0x0020 /* set umask, obviously */ +#define LOGIN_SETUSER 0x0040 /* set user (via setuid) */ +#define LOGIN_SETENV 0x0080 /* set user environment */ +#define LOGIN_SETALL 0x00ff /* set everything */ + +#define BI_AUTH "authorize" /* accepted authentication */ +#define BI_REJECT "reject" /* rejected authentication */ +#define BI_CHALLENG "reject challenge" /* reject with a challenge */ +#define BI_SILENT "reject silent" /* reject silently */ +#define BI_REMOVE "remove" /* remove file on error */ +#define BI_ROOTOKAY "authorize root" /* root authenticated */ +#define BI_SECURE "authorize secure" /* okay on non-secure line */ +#define BI_SETENV "setenv" /* set environment variable */ +#define BI_VALUE "value" /* set local variable */ + +#define AUTH_OKAY 0x01 /* user authenticated */ +#define AUTH_ROOTOKAY 0x02 /* root login okay */ +#define AUTH_SECURE 0x04 /* secure login */ +#define AUTH_SILENT 0x08 /* silent rejection */ +#define AUTH_CHALLENGE 0x10 /* a chellenge was given */ + +#define AUTH_ALLOW (AUTH_OKAY | AUTH_ROOTOKAY | AUTH_SECURE) typedef struct login_cap { - char *lc_class; - char *lc_cap; - char *lc_style; + char *lc_class; + char *lc_cap; + char *lc_style; } login_cap_t; typedef struct login_time { - u_short lt_start; /* Start time */ - u_short lt_end; /* End time */ - #define LTM_NONE 0x00 - #define LTM_SUN 0x01 - #define LTM_MON 0x02 - #define LTM_TUE 0x04 - #define LTM_WED 0x08 - #define LTM_THU 0x10 - #define LTM_FRI 0x20 - #define LTM_SAT 0x40 - #define LTM_ANY 0x7F - #define LTM_WK 0x3E - #define LTM_WD 0x41 - u_char lt_dow; /* Days of week */ + u_short lt_start; /* Start time */ + u_short lt_end; /* End time */ +#define LTM_NONE 0x00 +#define LTM_SUN 0x01 +#define LTM_MON 0x02 +#define LTM_TUE 0x04 +#define LTM_WED 0x08 +#define LTM_THU 0x10 +#define LTM_FRI 0x20 +#define LTM_SAT 0x40 +#define LTM_ANY 0x7F +#define LTM_WK 0x3E +#define LTM_WD 0x41 + u_char lt_dow; /* Days of week */ } login_time_t; + #define LC_MAXTIMES 64 #include <sys/cdefs.h> @@ -91,8 +97,9 @@ __BEGIN_DECLS struct passwd; void login_close __P((login_cap_t *)); -login_cap_t *login_getclassbyname __P((const char *, const char *homedir)); -login_cap_t *login_getclass __P((const struct passwd *)); +login_cap_t *login_getclassbyname __P((const char *, const struct passwd *)); +login_cap_t *login_getclass __P((const char *)); +login_cap_t *login_getpwclass __P((const struct passwd *)); login_cap_t *login_getuserclass __P((const struct passwd *)); char *login_getcapstr __P((login_cap_t*, const char *, char *, char *)); @@ -109,11 +116,20 @@ int setusercontext __P((login_cap_t*, const struct passwd*, uid_t, unsigned int) void setclassresources __P((login_cap_t *)); void setclassenvironment __P((login_cap_t *, const struct passwd *, int)); -int authenticate __P((const char*, const char*, const char*, const char*)); -int auth_script __P((const char*, ...)); -int auth_env __P((void)); +/* Most of these functions are deprecated */ +int auth_approve __P((login_cap_t*, const char*, const char*)); +int auth_check __P((const char *, const char *, const char *, const char *, int *)); +void auth_env __P((void)); +char *auth_mkvalue __P((const char *n)); +int auth_response __P((const char *, const char *, const char *, const char *, int *, const char *, const char *)); +void auth_rmfiles __P((void)); int auth_scan __P((int)); -int auth_rmfiles __P((void)); +int auth_script __P((const char*, ...)); +int auth_script_data __P((const char *, int, const char *, ...)); +char *auth_valud __P((const char *)); +int auth_setopt __P((const char *, const char *)); +void auth_clropts __P((void)); + void auth_checknologin __P((login_cap_t*)); int auth_cat __P((const char*)); @@ -127,7 +143,7 @@ login_time_t parse_lt __P((const char *)); int in_ltm __P((const login_time_t *, struct tm *, time_t *)); int in_ltms __P((const login_time_t *, struct tm *, time_t *)); -/* auxiliary functions */ +/* helper functions */ int login_strinlist __P((char **, char const *, int)); int login_str2inlist __P((char **, const char *, const char *, int)); @@ -138,4 +154,3 @@ int login_hostok __P((login_cap_t *, const char *, const char *, const char *, c __END_DECLS #endif /* _LOGIN_CAP_H_ */ - diff --git a/lib/libutil/login_class.c b/lib/libutil/login_class.c index cf6d6d6..73190e9 100644 --- a/lib/libutil/login_class.c +++ b/lib/libutil/login_class.c @@ -21,7 +21,7 @@ * * High-level routines relating to use of the user capabilities database * - * $Id$ + * $Id: login_class.c,v 1.5 1997/02/22 15:08:22 peter Exp $ */ #include <stdio.h> @@ -45,202 +45,197 @@ static struct login_res { - const char * what; - rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t); - int why; + const char *what; + rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t); + int why; } resources[] = { - { "cputime", login_getcaptime, RLIMIT_CPU }, - { "filesize", login_getcapsize, RLIMIT_FSIZE }, - { "datasize", login_getcapsize, RLIMIT_DATA }, - { "stacksize", login_getcapsize, RLIMIT_STACK }, - { "coredumpsize", login_getcapsize, RLIMIT_CORE }, - { "memoryuse", login_getcapsize, RLIMIT_RSS }, - { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, - { "maxproc", login_getcapnum, RLIMIT_NPROC }, - { "openfiles", login_getcapnum, RLIMIT_NOFILE }, - { NULL, 0, 0 } + { "cputime", login_getcaptime, RLIMIT_CPU }, + { "filesize", login_getcapsize, RLIMIT_FSIZE }, + { "datasize", login_getcapsize, RLIMIT_DATA }, + { "stacksize", login_getcapsize, RLIMIT_STACK }, + { "memoryuse", login_getcapsize, RLIMIT_RSS }, + { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, + { "maxproc", login_getcapnum, RLIMIT_NPROC }, + { "openfiles", login_getcapnum, RLIMIT_NOFILE }, + { "coredumpsize", login_getcapsize, RLIMIT_CORE }, + { NULL, 0, 0 } }; - void setclassresources(login_cap_t *lc) { - struct login_res *lr = resources; - - if (lc == NULL) - return; + struct login_res *lr; - while (lr->what != NULL) { - struct rlimit rlim, - newlim; - char cur[40], - max[40]; - rlim_t rcur, - rmax; + if (lc == NULL) + return; - sprintf(cur, "%s-cur", lr->what); - sprintf(max, "%s-max", lr->what); + for (lr = resources; lr->what != NULL; ++lr) { + struct rlimit rlim; - /* - * The login.conf file can have <limit>, <limit>-max, and - * <limit>-cur entries. - * What we do is get the current current- and maximum- limits. - * Then, we try to get an entry for <limit> from the capability, - * using the current and max limits we just got as the - * default/error values. - * *Then*, we try looking for <limit>-cur and <limit>-max, - * again using the appropriate values as the default/error - * conditions. - */ - - getrlimit(lr->why, &rlim); - rcur = rlim.rlim_cur; - rmax = rlim.rlim_max; + /* + * The login.conf file can have <limit>, <limit>-max, and + * <limit>-cur entries. + * What we do is get the current current- and maximum- limits. + * Then, we try to get an entry for <limit> from the capability, + * using the current and max limits we just got as the + * default/error values. + * *Then*, we try looking for <limit>-cur and <limit>-max, + * again using the appropriate values as the default/error + * conditions. + */ - rcur = (*lr->who)(lc, lr->what, rcur, rcur); - rmax = (*lr->who)(lc, lr->what, rmax, rmax); - newlim.rlim_cur = (*lr->who)(lc, cur, rcur, rcur); - newlim.rlim_max = (*lr->who)(lc, max, rmax, rmax); + if (getrlimit(lr->why, &rlim) != 0) + syslog(LOG_ERR, "getting %s resource limit: %m", lr->what); + else { + char name_cur[40]; + char name_max[40]; + rlim_t rcur = rlim.rlim_cur; + rlim_t rmax = rlim.rlim_max; + + sprintf(name_cur, "%s-cur", lr->what); + sprintf(name_max, "%s-max", lr->what); + + rcur = (*lr->who)(lc, lr->what, rcur, rcur); + rmax = (*lr->who)(lc, lr->what, rmax, rmax); + rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur); + rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax); - if (setrlimit(lr->why, &newlim) == -1) - syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what); - - ++lr; - } + if (setrlimit(lr->why, &rlim) == -1) + syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what); + } + } } + + static struct login_vars { - const char * tag; - const char * var; - const char * def; + const char *tag; + const char *var; + const char *def; } pathvars[] = { - { "path", "PATH", NULL }, - { "manpath", "MANPATH", NULL }, - { NULL, NULL, NULL } + { "path", "PATH", NULL }, + { "cdpath", "CDPATH", NULL }, + { "manpath", "MANPATH", NULL }, + { NULL, NULL, NULL } }, envars[] = { - { "lang", "LANG", NULL }, - { "charset", "MM_CHARSET", NULL }, - { "timezone", "TZ", NULL }, - { "term", "TERM", UNKNOWN }, - { NULL, NULL, NULL } + { "lang", "LANG", NULL }, + { "charset", "MM_CHARSET", NULL }, + { "timezone", "TZ", NULL }, + { "term", "TERM", UNKNOWN }, + { NULL, NULL, NULL } }; static char * substvar(char * var, const struct passwd * pwd, int hlen, int pch, int nlen) { - char * np = NULL; - - if (var != NULL) { - int tildes = 0; - int dollas = 0; - char * p; - - if (pwd != NULL) { - /* - * Count the number of ~'s in var to substitute - */ - p = var; - while ((p = strchr(p, '~')) != NULL) { - ++tildes; - ++p; - } - - /* - * Count the number of $'s in var to substitute - */ - p = var; - while ((p = strchr(p, '$')) != NULL) { - ++dollas; - ++p; - } - } - - np = malloc(strlen(var) + (dollas * nlen) - dollas + (tildes * (pch+hlen)) - tildes + 1); - - if (np != NULL) { - p = strcpy(np, var); + char *np = NULL; + + if (var != NULL) { + int tildes = 0; + int dollas = 0; + char *p; + + if (pwd != NULL) { + /* Count the number of ~'s in var to substitute */ + p = var; + for (p = var; (p = strchr(p, '~')) != NULL; p++) + ++tildes; + /* Count the number of $'s in var to substitute */ + p = var; + for (p = var; (p = strchr(p, '$')) != NULL; p++) + ++dollas; + } - if (pwd != NULL) { - /* - * This loop does user username and homedir substitutions - * for unescaped $ (username) and ~ (homedir) - */ - while (*(p += strcspn(p, "~$")) != '\0') { - int l = strlen(p); - - if (p > var && *(p-1) == '\\') /* Escaped: */ - memmove(p - 1, p, l + 1); /* Slide-out the backslash */ - else if (*p == '~') { - int v = pch && *(p+1) != '/'; /* Avoid double // */ - memmove(p + hlen + v, p + 1, l); /* Subst homedir */ - memmove(p, pwd->pw_dir, hlen); - if (v) - p[hlen] = '/'; - p += hlen + v; - } - else /* if (*p == '$') */ { - memmove(p + nlen, p + 1, l); /* Subst username */ - memmove(p, pwd->pw_name, nlen); - p += nlen; - } + np = malloc(strlen(var) + (dollas * nlen) + - dollas + (tildes * (pch+hlen)) + - tildes + 1); + + if (np != NULL) { + p = strcpy(np, var); + + if (pwd != NULL) { + /* + * This loop does user username and homedir substitutions + * for unescaped $ (username) and ~ (homedir) + */ + while (*(p += strcspn(p, "~$")) != '\0') { + int l = strlen(p); + + if (p > var && *(p-1) == '\\') /* Escaped: */ + memmove(p - 1, p, l + 1); /* Slide-out the backslash */ + else if (*p == '~') { + int v = pch && *(p+1) != '/'; /* Avoid double // */ + memmove(p + hlen + v, p + 1, l); /* Subst homedir */ + memmove(p, pwd->pw_dir, hlen); + if (v) + p[hlen] = '/'; + p += hlen + v; + } + else /* if (*p == '$') */ { + memmove(p + nlen, p + 1, l); /* Subst username */ + memmove(p, pwd->pw_name, nlen); + p += nlen; + } + } + } } - } } - } - return np; + + return np; } void setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths) { - struct login_vars * vars = paths ? pathvars : envars; - int hlen = pwd ? strlen(pwd->pw_dir) : 0; - int nlen = pwd ? strlen(pwd->pw_name) : 0; - char pch = 0; + struct login_vars *vars = paths ? pathvars : envars; + int hlen = pwd ? strlen(pwd->pw_dir) : 0; + int nlen = pwd ? strlen(pwd->pw_name) : 0; + char pch = 0; - if (hlen && pwd->pw_dir[hlen-1] != '/') - ++pch; + if (hlen && pwd->pw_dir[hlen-1] != '/') + ++pch; - while (vars->tag != NULL) { - char * var = paths ? login_getpath(lc, vars->tag, NULL) - : login_getcapstr(lc, vars->tag, NULL, NULL); + while (vars->tag != NULL) { + char * var = paths ? login_getpath(lc, vars->tag, NULL) + : login_getcapstr(lc, vars->tag, NULL, NULL); - char * np = substvar(var, pwd, hlen, pch, nlen); + char * np = substvar(var, pwd, hlen, pch, nlen); - if (np != NULL) { - setenv(vars->var, np, 1); - free(np); - } else if (vars->def != NULL) { - setenv(vars->var, vars->def, 0); - } - ++vars; - } - - /* - * If we're not processing paths, then see if there is a setenv list by - * which the admin and/or user may set an arbitrary set of env vars. - */ - if (!paths) { - char ** set_env = login_getcaplist(lc, "setenv", ","); - - if (set_env != NULL) { - while (*set_env != NULL) { - char *p = strchr(*set_env, '='); - if (p != NULL) { /* Discard invalid entries */ - char * np; - - *p++ = '\0'; - if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) { - setenv(*set_env, np, 1); + if (np != NULL) { + setenv(vars->var, np, 1); free(np); - } + } else if (vars->def != NULL) { + setenv(vars->var, vars->def, 0); + } + ++vars; + } + + /* + * If we're not processing paths, then see if there is a setenv list by + * which the admin and/or user may set an arbitrary set of env vars. + */ + if (!paths) { + char **set_env = login_getcaplist(lc, "setenv", ","); + + if (set_env != NULL) { + while (*set_env != NULL) { + char *p = strchr(*set_env, '='); + + if (p != NULL) { /* Discard invalid entries */ + char *np; + + *p++ = '\0'; + if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) { + setenv(*set_env, np, 1); + free(np); + } + } + ++set_env; + } } - ++set_env; - } } - } } @@ -258,15 +253,48 @@ setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths) int setclasscontext(const char *classname, unsigned int flags) { - int rc; - login_cap_t * lc = login_getclassbyname(classname, NULL); - flags &= (LOGIN_SETRESOURCES| LOGIN_SETPRIORITY|LOGIN_SETUMASK); - rc = setusercontext(lc, NULL, 0, flags); - login_close(lc); - return rc; + int rc; + login_cap_t *lc; + + lc = login_getclassbyname(classname, NULL); + + flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | + LOGIN_SETUMASK | LOGIN_SETPATH; + + rc = lc ? setusercontext(lc, NULL, 0, flags) : -1; + login_close(lc); + return rc; +} + + + +/* + * Private functionw which takes care of processing + */ + +static mode_t +setlogincontext(login_cap_t *lc, const struct passwd *pwd, + mode_t mymask, unsigned long flags) +{ + if (lc) { + /* Set resources */ + if (flags & LOGIN_SETRESOURCES) + setclassresources(lc); + /* See if there's a umask override */ + if (flags & LOGIN_SETUMASK) + mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask); + /* Set paths */ + if (flags & LOGIN_SETPATH) + setclassenvironment(lc, pwd, 1); + /* Set environment */ + if (flags & LOGIN_SETENV) + setclassenvironment(lc, pwd, 0); + } + return mymask; } + /* * setusercontext() * @@ -284,93 +312,76 @@ setclasscontext(const char *classname, unsigned int flags) int setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags) { - int i; - login_cap_t * llc = NULL; - - if (lc == NULL) - { - if (pwd != NULL && (lc = login_getclass(pwd)) != NULL) - llc = lc; /* free this when we're done */ - } - - if (flags & LOGIN_SETPATH) - pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH; - - /* - * Set the process priority - */ - if (flags & LOGIN_SETPRIORITY) { - int pri = (int)login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI); - pri = (pri < PRIO_MIN) ? PRIO_MIN : (pri > PRIO_MAX) ? PRIO_MAX : pri; - if (setpriority(PRIO_PROCESS, 0, pri) != 0) - syslog(LOG_WARNING, "setpriority '%s': %m", pwd->pw_name); - } - - /* - * Set resources - */ - if (flags & LOGIN_SETRESOURCES) - setclassresources(lc); - - /* - * Set the sessions login - */ - if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) { - syslog(LOG_ERR, "setlogin '%s': %m", pwd->pw_name); - login_close(llc); - return -1; - } - - /* - * Setup the user's group permissions - */ - if (flags & LOGIN_SETGROUP) { - if (setgid(pwd->pw_gid) != 0) - syslog(LOG_WARNING, "setgid %ld: %m", (long)pwd->pw_gid); - if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) - syslog(LOG_WARNING, "initgroups '%s': %m", pwd->pw_name); - } - - /* - * FreeBSD extension: here we (might) loop and do this twice. - * First, for the class we have been given, and next for - * any user overrides in ~/.login.conf the user's home directory. - */ - if (flags & LOGIN_SETUMASK) - umask(LOGIN_DEFUMASK); /* Set default umask up front */ - - i = 0; - while (i < 2 && lc != NULL) { - - if (flags & LOGIN_SETUMASK) { - rlim_t tmp = login_getcapnum(lc, "umask", RLIM_INFINITY, RLIM_INFINITY); - if (tmp != RLIM_INFINITY) - umask((mode_t)tmp); + quad_t p; + mode_t mymask; + login_cap_t *llc = NULL; + + if (lc == NULL) { + if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL) + llc = lc; /* free this when we're done */ } if (flags & LOGIN_SETPATH) - setclassenvironment(lc, pwd, 1); + pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH; - if (flags & LOGIN_SETENV) - setclassenvironment(lc, pwd, 0); + /* we need a passwd entry to set these */ + if (pwd == NULL) + flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN); - if (i++ == 0) /* Play it again, Sam */ - lc = (pwd == NULL) ? NULL : login_getuserclass(pwd); - } + /* Set the process priority */ + if (flags & LOGIN_SETPRIORITY) { + p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI); - login_close(lc); /* User's private 'me' class */ - login_close(llc); /* Class we opened */ + p = (p < PRIO_MIN || p > PRIO_MAX) ? LOGIN_DEFPRI : p; + if (setpriority(PRIO_PROCESS, 0, (int)p) != 0) + syslog(LOG_WARNING, "setpriority '%s' (%s): %m", + pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS); + } - /* - * This needs to be done after all of the above. - * Do it last so we avoid getting killed and dropping core - */ - if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) { - syslog(LOG_ERR, "setuid %ld: %m", uid); + /* Setup the user's group permissions */ + if (flags & LOGIN_SETGROUP) { + if (setgid(pwd->pw_gid) != 0) { + syslog(LOG_ERR, "setgid(%ld): %m", (long)pwd->pw_gid); + login_close(llc); + return -1; + } + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { + syslog(LOG_ERR, "initgroups(%s,%ld): %m", pwd->pw_name, + pwd->pw_gid); + login_close(llc); + return -1; + } + } + + /* Set the sessions login */ + if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) { + syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name); + login_close(llc); + return -1; + } + + mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0; + mymask = setlogincontext(lc, pwd, mymask, flags); login_close(llc); - return -1; /* Paranoia again */ - } - return 0; + /* This needs to be done after anything that needs root privs */ + if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) { + syslog(LOG_ERR, "setuid(%ld): %m", uid); + return -1; /* Paranoia again */ + } + + /* + * Now, we repeat some of the above for the user's private entries + */ + if ((lc = login_getuserclass(pwd)) != NULL) { + mymask = setlogincontext(lc, pwd, mymask, flags); + login_close(lc); + } + + /* Finally, set any umask we've found */ + if (flags & LOGIN_SETUMASK) + umask(mymask); + + return 0; } diff --git a/lib/libutil/login_ok.c b/lib/libutil/login_ok.c index cf778da..88b6f4e 100644 --- a/lib/libutil/login_ok.c +++ b/lib/libutil/login_ok.c @@ -20,7 +20,7 @@ * * Support allow/deny lists in login class capabilities * - * $Id$ + * $Id: login_ok.c,v 1.3 1997/02/22 15:08:25 peter Exp $ */ #include <stdio.h> @@ -40,7 +40,8 @@ /* -- support functions -- */ -/* login_strinlist() +/* + * login_strinlist() * This function is intentionally public - reused by TAS. * Returns TRUE (non-zero) if a string matches a pattern * in a given array of patterns. 'flags' is passed directly @@ -50,106 +51,111 @@ int login_strinlist(char **list, char const *str, int flags) { - int rc = 0; - - if (str != NULL && *str != '\0') - { - int i = 0; - while (rc == 0 && list[i] != NULL) - rc = fnmatch(list[i], str, flags) == 0; - } - return rc; + int rc = 0; + + if (str != NULL && *str != '\0') { + int i = 0; + + while (rc == 0 && list[i] != NULL) + rc = fnmatch(list[i], str, flags) == 0; + } + return rc; } -/* login_str2inlist() +/* + * login_str2inlist() * Locate either or two strings in a given list */ int login_str2inlist(char **ttlst, const char *str1, const char *str2, int flags) { - int rc = 0; + int rc = 0; - if (login_strinlist(ttlst, str1, flags)) - rc = 1; - else if (login_strinlist(ttlst, str2, flags)) - rc = 1; - return rc; + if (login_strinlist(ttlst, str1, flags)) + rc = 1; + else if (login_strinlist(ttlst, str2, flags)) + rc = 1; + return rc; } -/* login_timelist() +/* + * login_timelist() * This function is intentinoally public - reused by TAS. * Returns an allocated list of time periods given an array * of time periods in ascii form. */ login_time_t * -login_timelist(login_cap_t *lc, char const *cap, int *ltno, login_time_t **ltptr) +login_timelist(login_cap_t *lc, char const *cap, int *ltno, + login_time_t **ltptr) { - int j = 0; - struct login_time * lt = NULL; - char **tl = login_getcaplist(lc, cap, NULL); - - if (tl) - { - while (tl[j++] != NULL) - ; - if (*ltno >= j) - lt = *ltptr; - else if ((lt = realloc(*ltptr, j)) != NULL) - { - *ltno = j; - *ltptr = lt; - } - if (lt != NULL) - { - int i = 0; - --j; - while (i < j) - { - lt[i] = parse_lt(tl[i]); - ++i; - } - lt[i].lt_dow = LTM_NONE; + int j = 0; + struct login_time *lt = NULL; + char **tl; + + if ((tl = login_getcaplist(lc, cap, NULL)) != NULL) { + + while (tl[j++] != NULL) + ; + if (*ltno >= j) + lt = *ltptr; + else if ((lt = realloc(*ltptr, j)) != NULL) { + *ltno = j; + *ltptr = lt; + } + if (lt != NULL) { + int i = 0; + + for (--j; i < j; i++) + lt[i] = parse_lt(tl[i]); + lt[i].lt_dow = LTM_NONE; + } } - } - return lt; + return lt; } -/* login_ttyok() +/* + * login_ttyok() * This function is a variation of auth_ttyok(), but it checks two * arbitrary capability lists not necessarily related to access. * This hook is provided for the accounted/exclude accounting lists. */ int -login_ttyok(login_cap_t *lc, const char *tty, const char *allowcap, const char *denycap) +login_ttyok(login_cap_t *lc, const char *tty, const char *allowcap, + const char *denycap) { - int rc = 1; - - if (lc != NULL && tty != NULL && *tty != '\0') - { - struct ttyent * te = getttynam(tty); /* Need group name */ - char * grp = te ? te->ty_group : NULL; - char **ttl = login_getcaplist(lc, allowcap, NULL); - - if (ttl != NULL && !login_str2inlist(ttl, tty, grp, 0)) - rc = 0; /* tty or ttygroup not in allow list */ - else - { - ttl = login_getcaplist(lc, denycap, NULL); - if (ttl != NULL && login_str2inlist(ttl, tty, grp, 0)) - rc = 0; /* tty or ttygroup in deny list */ + int rc = 1; + + if (lc != NULL && tty != NULL && *tty != '\0') { + struct ttyent *te; + char *grp; + char **ttl; + + te = getttynam(tty); /* Need group name */ + grp = te ? te->ty_group : NULL; + ttl = login_getcaplist(lc, allowcap, NULL); + + if (ttl != NULL && !login_str2inlist(ttl, tty, grp, 0)) + rc = 0; /* tty or ttygroup not in allow list */ + else { + + ttl = login_getcaplist(lc, denycap, NULL); + if (ttl != NULL && login_str2inlist(ttl, tty, grp, 0)) + rc = 0; /* tty or ttygroup in deny list */ + } } - } - return rc; + + return rc; } -/* auth_ttyok() +/* + * auth_ttyok() * Determine whether or not login on a tty is accessible for * a login class */ @@ -157,86 +163,89 @@ login_ttyok(login_cap_t *lc, const char *tty, const char *allowcap, const char * int auth_ttyok(login_cap_t *lc, const char * tty) { - return login_ttyok(lc, tty, "ttys.allow", "ttys.deny"); + return login_ttyok(lc, tty, "ttys.allow", "ttys.deny"); } -/* login_hostok() +/* + * login_hostok() * This function is a variation of auth_hostok(), but it checks two * arbitrary capability lists not necessarily related to access. * This hook is provided for the accounted/exclude accounting lists. */ int -login_hostok(login_cap_t *lc, const char *host, const char *ip, const char *allowcap, const char *denycap) +login_hostok(login_cap_t *lc, const char *host, const char *ip, + const char *allowcap, const char *denycap) { - int rc = 1; /* Default is ok */ - - if (lc != NULL && ((host != NULL && *host != '\0') || (ip != NULL && *ip != '\0'))) - { - char **hl = login_getcaplist(lc, allowcap, NULL); - - if (hl != NULL && !login_str2inlist(hl, host, ip, FNM_CASEFOLD)) - rc = 0; /* host or IP not in allow list */ - else - { - hl = login_getcaplist(lc, "host.deny", NULL); - if (hl != NULL && login_str2inlist(hl, host, ip, FNM_CASEFOLD)) - rc = 0; /* host or IP in deny list */ + int rc = 1; /* Default is ok */ + + if (lc != NULL && + ((host != NULL && *host != '\0') || (ip != NULL && *ip != '\0'))) { + char **hl; + + hl = login_getcaplist(lc, allowcap, NULL); + if (hl != NULL && !login_str2inlist(hl, host, ip, FNM_CASEFOLD)) + rc = 0; /* host or IP not in allow list */ + else { + + hl = login_getcaplist(lc, "host.deny", NULL); + if (hl != NULL && login_str2inlist(hl, host, ip, FNM_CASEFOLD)) + rc = 0; /* host or IP in deny list */ + } } - } - return rc; + + return rc; } -/* auth_hostok() +/* + * auth_hostok() * Determine whether or not login from a host is ok */ int auth_hostok(login_cap_t *lc, const char *host, const char *ip) { - return login_hostok(lc, host, ip, "host.allow", "host.deny"); + return login_hostok(lc, host, ip, "host.allow", "host.deny"); } -/* auth_timeok() +/* + * auth_timeok() * Determine whether or not login is ok at a given time */ int auth_timeok(login_cap_t *lc, time_t t) { - int rc = 1; /* Default is ok */ - - if (lc != NULL && t != (time_t)0 && t != (time_t)-1) - { - struct tm * tptr = localtime(&t); - - static int ltimesno = 0; - static struct login_time * ltimes = NULL; - - if (tptr != NULL) - { - struct login_time *lt = login_timelist(lc, "times.allow", <imesno, <imes); - - if (lt != NULL && in_ltms(lt, tptr, NULL) == -1) - rc = 0; /* not in allowed times list */ - else - { - lt = login_timelist(lc, "times.deny", <imesno, <imes); - - if (lt != NULL && in_ltms(lt, tptr, NULL) != -1) - rc = 0; /* in deny times list */ - } - if (ltimes) - { - free(ltimes); - ltimes = NULL; - ltimesno = 0; - } + int rc = 1; /* Default is ok */ + + if (lc != NULL && t != (time_t)0 && t != (time_t)-1) { + struct tm *tptr; + + static int ltimesno = 0; + static struct login_time *ltimes = NULL; + + if ((tptr = localtime(&t)) != NULL) { + struct login_time *lt; + + lt = login_timelist(lc, "times.allow", <imesno, <imes); + if (lt != NULL && in_ltms(lt, tptr, NULL) == -1) + rc = 0; /* not in allowed times list */ + else { + + lt = login_timelist(lc, "times.deny", <imesno, <imes); + if (lt != NULL && in_ltms(lt, tptr, NULL) != -1) + rc = 0; /* in deny times list */ + } + if (ltimes) { + free(ltimes); + ltimes = NULL; + ltimesno = 0; + } + } } - } - return rc; -} + return rc; +} diff --git a/lib/libutil/login_times.c b/lib/libutil/login_times.c index c21524c..598592b 100644 --- a/lib/libutil/login_times.c +++ b/lib/libutil/login_times.c @@ -20,7 +20,7 @@ * * Login period parsing and comparison functions. * - * $Id$ + * $Id: login_times.c,v 1.4 1997/02/22 15:08:27 peter Exp $ */ #include <stdio.h> @@ -34,134 +34,129 @@ static struct { - const char * dw; - u_char cn; - u_char fl; + const char *dw; + u_char cn; + u_char fl; } dws[] = { - { "su", 2, LTM_SUN }, { "mo", 2, LTM_MON }, { "tu", 2, LTM_TUE }, - { "we", 2, LTM_WED }, { "th", 2, LTM_THU }, { "fr", 2, LTM_FRI }, - { "sa", 2, LTM_SAT }, { "any",3, LTM_ANY }, { "all",3, LTM_ANY }, - { "wk", 2, LTM_WK }, { "wd", 2, LTM_WD }, { NULL, 0, 0 } + { "su", 2, LTM_SUN }, { "mo", 2, LTM_MON }, { "tu", 2, LTM_TUE }, + { "we", 2, LTM_WED }, { "th", 2, LTM_THU }, { "fr", 2, LTM_FRI }, + { "sa", 2, LTM_SAT }, { "any",3, LTM_ANY }, { "all",3, LTM_ANY }, + { "wk", 2, LTM_WK }, { "wd", 2, LTM_WD }, { NULL, 0, 0 } }; static char * parse_time(char * ptr, u_short * t) { - u_short val; + u_short val; - for (val = 0; *ptr && isdigit(*ptr); ptr++) - val = (u_short)(val * 10 + (*ptr - '0')); + for (val = 0; *ptr && isdigit(*ptr); ptr++) + val = (u_short)(val * 10 + (*ptr - '0')); - *t = (u_short)((val / 100) * 60 + (val % 100)); - return ptr; + *t = (u_short)((val / 100) * 60 + (val % 100)); + + return ptr; } + login_time_t parse_lt(const char * str) { - login_time_t t; - - memset(&t, 0, sizeof t); - t.lt_dow = LTM_NONE; - if (str && *str && strcmp(str, "Never") != 0 && strcmp(str, "None") != 0) - { - int i; - login_time_t m = t; - char * p; - char buf[64]; - - /* Make local copy and force lowercase to simplify parsing - */ - p = strncpy(buf, str, sizeof buf); - buf[sizeof buf - 1] = '\0'; - for (i = 0; buf[i]; i++) - buf[i] = (char)tolower(buf[i]); - - while (isalpha(*p)) - { - for (i = 0; dws[i].dw && strncmp(p, dws[i].dw, dws[i].cn) != 0 ; i++) - ; - if (dws[i].dw == NULL) - break; - m.lt_dow |= dws[i].fl; - p += dws[i].cn; - } + login_time_t t; + + memset(&t, 0, sizeof t); + t.lt_dow = LTM_NONE; + if (str && *str && strcmp(str, "Never") != 0 && strcmp(str, "None") != 0) { + int i; + login_time_t m = t; + char *p; + char buf[64]; + + /* Make local copy and force lowercase to simplify parsing */ + p = strncpy(buf, str, sizeof buf); + buf[sizeof buf - 1] = '\0'; + for (i = 0; buf[i]; i++) + buf[i] = (char)tolower(buf[i]); + + while (isalpha(*p)) { + + i = 0; + while (dws[i].dw && strncmp(p, dws[i].dw, dws[i].cn) != 0) + i++; + if (dws[i].dw == NULL) + break; + m.lt_dow |= dws[i].fl; + p += dws[i].cn; + } + + if (m.lt_dow == LTM_NONE) /* No (valid) prefix, assume any */ + m.lt_dow |= LTM_ANY; - if (m.lt_dow == LTM_NONE) /* No (valid) prefix, assume any */ - m.lt_dow |= LTM_ANY; - - if (isdigit(*p)) - p = parse_time(p, &m.lt_start); - else - m.lt_start = 0; - if (*p == '-') - p = parse_time(++p, &m.lt_end); - else - m.lt_end = 1440; - - t = m; - } - return t; + if (isdigit(*p)) + p = parse_time(p, &m.lt_start); + else + m.lt_start = 0; + if (*p == '-') + p = parse_time(++p, &m.lt_end); + else + m.lt_end = 1440; + + t = m; + } + return t; } int in_ltm(const login_time_t * ltm, struct tm * tt, time_t * ends) { - int rc = 0; - - if (tt != NULL) - { - /* First, examine the day of the week - */ - if ((u_char)(0x01 << tt->tm_wday) & ltm->lt_dow) - { - /* Convert `current' time to minute of the day - */ - u_short now = (u_short)((tt->tm_hour * 60) + tt->tm_min); - if (tt->tm_sec > 30) - ++now; - if (now >= ltm->lt_start && now < ltm->lt_end) - { - rc = 2; - if (ends != NULL) - { - /* If requested, return ending time for this period - */ - tt->tm_hour = (int)(ltm->lt_end / 60); - tt->tm_min = (int)(ltm->lt_end % 60); - *ends = mktime(tt); + int rc = 0; + + if (tt != NULL) { + /* First, examine the day of the week */ + if ((u_char)(0x01 << tt->tm_wday) & ltm->lt_dow) { + /* Convert `current' time to minute of the day */ + u_short now = (u_short)((tt->tm_hour * 60) + tt->tm_min); + + if (tt->tm_sec > 30) + ++now; + if (now >= ltm->lt_start && now < ltm->lt_end) { + rc = 2; + if (ends != NULL) { + /* If requested, return ending time for this period */ + tt->tm_hour = (int)(ltm->lt_end / 60); + tt->tm_min = (int)(ltm->lt_end % 60); + *ends = mktime(tt); + } + } } - } } - } - return rc; + return rc; } + int in_lt(const login_time_t * ltm, time_t * t) { - return in_ltm(ltm, localtime(t), t); + return in_ltm(ltm, localtime(t), t); } int in_ltms(const login_time_t * ltm, struct tm * tm, time_t * t) { - int i = 0; - - while (i < LC_MAXTIMES && ltm[i].lt_dow != LTM_NONE) - { - if (in_ltm(ltm + i, tm, t)) - return i; - i++; - } - return -1; + int i = 0; + + while (i < LC_MAXTIMES && ltm[i].lt_dow != LTM_NONE) { + if (in_ltm(ltm + i, tm, t)) + return i; + i++; + } + return -1; } int in_lts(const login_time_t * ltm, time_t * t) { - return in_ltms(ltm, localtime(t), t); + return in_ltms(ltm, localtime(t), t); } |