summaryrefslogtreecommitdiff
path: root/modules/pam_umask
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_umask')
-rw-r--r--modules/pam_umask/Makefile16
-rw-r--r--modules/pam_umask/README14
-rw-r--r--modules/pam_umask/pam_umask.c307
3 files changed, 337 insertions, 0 deletions
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 <kukuk@thkukuk.de>
+ *
+ * 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 <pwd.h>
+#include <grp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+
+#define PAM_SM_SESSION
+
+#include <security/_pam_aconf.h>
+
+#include <security/pam_modules.h>
+#include <security/_pam_modutil.h>
+
+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 */