From 56c0434453eef215dd1bff2f1b5796f5f7c95dfa Mon Sep 17 00:00:00 2001 From: David Nugent Date: Sat, 10 May 1997 18:55:38 +0000 Subject: [PATCH] Summary of login.conf support changes: o Incorporated BSDI code and enhancements, better logging for error checking (which has been shown to be a problem, and is therefore justified, imho); also some minor things we were missing, including better quad_t math, which checks for under/overflows. o setusercontext() now allows user resource limit overrides, but does this AFTER dropping root privs, to restrict the user to droping hard limits and set soft limits within the kernel's allowed user limits. o umask() only set once, and only if requested. o add _secure_path(), and use in login.conf to guard against symlinks etc. and non-root owned or non-user owned files being used. Derived from BSDI contributed code. o revamped authentication code to BSDI's latest api, which includes deleting authenticate() and adding auth_check() and a few other functions. This is still marked as depecated in BSDI, but is included for completeness. No other source in the tree uses this anyway, so it is now bracketed with #ifdef LOGIN_CAP_AUTH which is by default not defined. Only auth_checknologin() and auth_cat() are actually used in module login_auth.c. o AUTH_NONE definition removed (collided with other includes in the tree). [bde] o BSDI's login_getclass() now accepts a char *classname parameter rather than struct passwd *pwd. We now do likewise, but added login_getpwclass() for (sort of) backwards compatiblity, namely because we handle root as a special case for the default class. This will require quite a few changes elsewhere in the source tree. o We no longer pretend to support rlim_t as a long type. o Revised code formatting to be more bsd-ish style. --- lib/libutil/Makefile | 6 +- lib/libutil/_secure_path.3 | 70 ++++ lib/libutil/_secure_path.c | 72 ++++ lib/libutil/libutil.h | 3 +- lib/libutil/login_auth.c | 703 +++++++++++++++++++++++---------- lib/libutil/login_cap.c | 790 +++++++++++++++++++++++-------------- lib/libutil/login_cap.h | 111 +++--- lib/libutil/login_class.c | 491 ++++++++++++----------- lib/libutil/login_ok.c | 225 ++++++----- lib/libutil/login_times.c | 169 ++++---- 10 files changed, 1653 insertions(+), 987 deletions(-) create mode 100644 lib/libutil/_secure_path.3 create mode 100644 lib/libutil/_secure_path.c diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index de882b032a7e..8ac63d2ed746 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 000000000000..6387b73a696f --- /dev/null +++ b/lib/libutil/_secure_path.3 @@ -0,0 +1,70 @@ +.\" Copyright (c) 1997 David Nugent +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, is permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice immediately at the beginning of the file, without modification, +.\" this list of conditions, and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. This work was done expressly for inclusion into FreeBSD. Other use +.\" is permitted provided this notation is included. +.\" 4. Absolutely no warranty of function or purpose is made by the 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 +.Fd #include +.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 000000000000..38a974eb18a8 --- /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 +#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 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 35c1163ed766..3db526307051 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 565ddc3290bf..695b006b6863 100644 --- a/lib/libutil/login_auth.c +++ b/lib/libutil/login_auth.c @@ -4,6 +4,10 @@ * David Nugent * 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 #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -40,297 +46,575 @@ #include #include #include +#include #include +#include +#include -#ifdef RLIM_LONG -# define STRTOV strtol -#else -# define STRTOV strtoq -#endif +#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 + */ -#define AUTHMAXLINES 1024 -#define AUTHMAXARGS 16 -struct auth_info { - int reject; - int auths; - int env_count; - char **env; - int file_count; - char **files; +#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]; }; -static struct auth_info auth_info; +struct authopts { + struct authopts *next; + char opt[1]; +}; + +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) -{ - int i; +static char *auth_environ[] = { + "PATH=" _PATH_DEFPATH, + "SHELL=" _PATH_BSHELL, + NULL, +}; - 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]); + + +/* + * 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) +{ + char *ptr = NULL; + + if (spoolbuf != NULL && *idx < spoolidx) { + ptr = spoolbuf + *idx; + *idx += strlen(ptr) + 1; } - 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]); - } - free(auth_info.files); - auth_info.files = NULL; - } + return ptr; } /* - * collect_info() - * Read from , a list of authorization commands. - * These commands are: - * reject - * authorize [root|secure] - * setenv [ ] - * remove - * 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"); + if (spoolbuf) + free(spoolbuf); + spoolidx = 0; - 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++; + 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; } - } - } 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++; - } - } + /* + * 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 , with a class , * style