From d705b50256fa7515c470d86c6ad190e665cc17b8 Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Thu, 14 Jul 2005 09:59:35 +0000 Subject: Relevant BUGIDs: none Purpose of commit: new feature Commit summary: --------------- Add new PAM module for setting umask --- CHANGELOG | 2 + modules/pam_umask/Makefile | 16 +++ modules/pam_umask/README | 14 ++ modules/pam_umask/pam_umask.c | 307 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 339 insertions(+) create mode 100644 modules/pam_umask/Makefile create mode 100644 modules/pam_umask/README create mode 100644 modules/pam_umask/pam_umask.c diff --git a/CHANGELOG b/CHANGELOG index a6fc5ae2..a62e370a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -62,6 +62,8 @@ BerliOS Bugs are marked with (BerliOS #XXXX). 0.81: please submit patches for this section with actual code/doc patches! +* pam_umask: New module for setting umask from GECOS field, /etc/login.defs + or /etc/default/login (kukuk) 0.80: Wed Jul 13 13:23:20 CEST 2005 * pam_tally: test for NULL data before dereferencing them (t8m) diff --git a/modules/pam_umask/Makefile b/modules/pam_umask/Makefile new file mode 100644 index 00000000..c99ca8e0 --- /dev/null +++ b/modules/pam_umask/Makefile @@ -0,0 +1,16 @@ +# +# $Id$ +# +# This Makefile controls a build process of pam_umask module for +# Linux-PAM. You should not modify this Makefile (unless you know +# what you are doing!). +# + +include ../../Make.Rules + +TITLE=pam_umask + +DEFS=-DDEFAULT_CONF_FILE=\"/etc/login.defs\" +CFLAGS += $(DEFS) + +include ../Simple.Rules diff --git a/modules/pam_umask/README b/modules/pam_umask/README new file mode 100644 index 00000000..3fd4f7f0 --- /dev/null +++ b/modules/pam_umask/README @@ -0,0 +1,14 @@ +This is the README for pam_umask +-------------------------------- + +pam_umask sets the set the file mode creation mask of the current +environment. It tries to get the umask value from the following +files in the following priority: + +- umask= argument +- umask= entry of the users GECOS field +- pri= entry of the users GECOS field +- ulimit= entry of the users GECOS field +- UMASK= entry from /etc/default/login +- UMASK entry from /etc/login.defs + diff --git a/modules/pam_umask/pam_umask.c b/modules/pam_umask/pam_umask.c new file mode 100644 index 00000000..c51ef800 --- /dev/null +++ b/modules/pam_umask/pam_umask.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2005 Thorsten Kukuk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAM_SM_SESSION + +#include + +#include +#include + +struct options_t { + int debug; + int usergroups; + char *umask; +}; +typedef struct options_t options_t; + +/* syslogging function for errors and other information */ +static void +__pam_log (int err, const char *format,...) +{ + va_list args; + char *str; + + va_start (args, format); + if (vasprintf (&str, format, args) < 0) + return; + syslog (err, "pam_umask: %s", str); + va_end (args); +} + +static void +parse_option (const char *argv, options_t *options) +{ + if (argv == NULL || argv[0] == '\0') + return; + + if (strcasecmp (argv, "debug") == 0) + options->debug = 1; + else if (strncasecmp (argv, "umask=", 6) == 0) + options->umask = strdup (&argv[6]); + else if (strcasecmp (argv, "usergroups") == 0) + options->usergroups = 1; + else + __pam_log (LOG_ERR, "Unknown option: `%s'", argv); +} + +static char * +search_key (const char *filename) +{ + FILE *fp; + char *buf = NULL; + size_t buflen = 0; + char *retval = NULL; + + fp = fopen (filename, "r"); + if (NULL == fp) + return NULL; + + while (!feof (fp)) + { + char *tmp, *cp; +#if defined(HAVE_GETLINE) + ssize_t n = getline (&buf, &buflen, fp); +#elif defined (HAVE_GETDELIM) + ssize_t n = getdelim (&buf, &buflen, '\n', fp); +#else + ssize_t n; + + if (buf == NULL) + { + buflen = 8096; + buf = malloc (buflen); + } + buf[0] = '\0'; + fgets (buf, buflen - 1, fp); + if (buf != NULL) + n = strlen (buf); + else + n = 0; +#endif /* HAVE_GETLINE / HAVE_GETDELIM */ + cp = buf; + + if (n < 1) + break; + + tmp = strchr (cp, '#'); /* remove comments */ + if (tmp) + *tmp = '\0'; + while (isspace ((int)*cp)) /* remove spaces and tabs */ + ++cp; + if (*cp == '\0') /* ignore empty lines */ + continue; + + if (cp[strlen (cp) - 1] == '\n') + cp[strlen (cp) - 1] = '\0'; + + tmp = strsep (&cp, " \t="); + if (cp != NULL) + while (isspace ((int)*cp) || *cp == '=') + ++cp; + + if (strcasecmp (tmp, "UMASK") == 0) + { + retval = strdup (cp); + break; + } + } + fclose (fp); + + if (buf) + free (buf); + + return retval; +} + +static int +get_options (options_t *options, int argc, const char **argv) +{ + memset (options, 0, sizeof (options_t)); + /* Parse parameters for module */ + for ( ; argc-- > 0; argv++) + parse_option (*argv, options); + + if (options->umask == NULL) + options->umask = search_key ("/etc/login.defs"); + if (options->umask == NULL) + options->umask = search_key ("/etc/default/login"); + + return 0; +} + +static void +set_umask (const char *value) +{ + const char *value_orig = value; + mode_t mask; + char *endptr; + + mask = strtol (value, &endptr, 8) & 0777; + if ((mask == 0) && (value_orig == endptr)) + return; + umask (mask); + return; +} + +/* Set the process nice, ulimit, and umask from the + password file entry. */ +static void +setup_limits_from_gecos (pam_handle_t *pamh, options_t *options, + struct passwd *pw) +{ + char *cp; + + if (options->usergroups) + { + /* if not root, and UID == GID, and username is the same as + primary group name, set umask group bits to be the same as + owner bits (examples: 022 -> 002, 077 -> 007). */ + if (pw->pw_uid != 0 && pw->pw_uid == pw->pw_gid) + { + struct group *grp = _pammodutil_getgrgid (pamh, pw->pw_gid); + if (grp && (strcmp (pw->pw_name, grp->gr_name) == 0)) + { + mode_t oldmask = umask (0777); + umask ((oldmask & ~070) | ((oldmask >> 3) & 070)); + } + } + } + + /* See if the GECOS field contains values for NICE, UMASK or ULIMIT. */ + for (cp = pw->pw_gecos; cp != NULL; cp = strchr (cp, ',')) + { + if (*cp == ',') + cp++; + + if (strncasecmp (cp, "umask=", 6) == 0) + umask (strtol (cp + 6, NULL, 8) & 0777); + else if (strncasecmp (cp, "pri=", 4) == 0) + nice (strtol (cp + 4, NULL, 10)); + else if (strncasecmp (cp, "ulimit=", 7) == 0) + { + struct rlimit rlimit_fsize; + rlimit_fsize.rlim_cur = 512L * strtol (cp + 7, NULL, 10); + rlimit_fsize.rlim_max = rlimit_fsize.rlim_cur; + setrlimit (RLIMIT_FSIZE, &rlimit_fsize); + } + } +} + + +PAM_EXTERN int +pam_sm_open_session (pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + struct passwd *pw; + options_t options; + const char *name; + int retval = PAM_SUCCESS; + + get_options (&options, argc, argv); + + /* get the user name. */ + if ((retval = pam_get_user (pamh, &name, NULL)) != PAM_SUCCESS) + { + __pam_log (LOG_ERR, "pam_get_user failed: return %d", retval); + return (retval == PAM_CONV_AGAIN ? PAM_INCOMPLETE:retval); + } + + if (name == NULL || name[0] == '\0') + { + if (name) + { + __pam_log (LOG_ERR, "bad username [%s]", name); + return PAM_USER_UNKNOWN; + } + return PAM_SERVICE_ERR; + } + + pw = _pammodutil_getpwnam (pamh, name); + if (pw == NULL) + { + __pam_log (LOG_ERR, "account for %s not found", name); + return PAM_USER_UNKNOWN; + } + + if (options.umask != NULL) + { + set_umask (options.umask); + free (options.umask); + } + + setup_limits_from_gecos (pamh, &options, pw); + + return retval; +} + +PAM_EXTERN int +pam_sm_close_session (pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + return PAM_SUCCESS; +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_umask_modstruct = { + "pam_umask", + NULL, + NULL, + NULL, + pam_sm_open_session, + pam_sm_close_session, + NULL +}; + +#endif + +/* end of module definition */ -- cgit v1.2.3