1 /*
2 * pam_umask module
3 *
4 * Copyright (c) 2005, 2006, 2007, 2010, 2013 Thorsten Kukuk <kukuk@thkukuk.de>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, and the entire permission notice in its entirety,
11 * including the disclaimer of warranties.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior
17 * written permission.
18 *
19 * ALTERNATIVELY, this product may be distributed under the terms of
20 * the GNU Public License V2, in which case the provisions of the GPL
21 * are required INSTEAD OF the above restrictions. (This clause is
22 * necessary due to a potential bad interaction between the GPL and
23 * the restrictions contained in a BSD-style copyright.)
24 *
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include "config.h"
39
40 #include <pwd.h>
41 #include <grp.h>
42 #include <stdio.h>
43 #include <ctype.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <string.h>
47 #include <stdarg.h>
48 #include <unistd.h>
49 #include <stdlib.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #include <sys/resource.h>
53 #include <syslog.h>
54
55 #include <security/pam_modules.h>
56 #include <security/pam_modutil.h>
57 #include <security/pam_ext.h>
58 #include "pam_inline.h"
59
60 #define LOGIN_DEFS "/etc/login.defs"
61 #define LOGIN_CONF "/etc/default/login"
62
63 struct options_t {
64 int debug;
65 int usergroups;
66 int silent;
67 const char *umask;
68 char *login_umask;
69 };
70 typedef struct options_t options_t;
71
72 static void
73 parse_option (const pam_handle_t *pamh, const char *argv, options_t *options)
74 {
75 const char *str;
76
77 if (argv == NULL || argv[0] == '\0')
78 return;
79
80 if (strcasecmp (argv, "debug") == 0)
81 options->debug = 1;
82 else if ((str = pam_str_skip_icase_prefix (argv, "umask=")) != NULL)
83 options->umask = str;
84 else if (strcasecmp (argv, "usergroups") == 0)
85 options->usergroups = 1;
86 else if (strcasecmp (argv, "nousergroups") == 0)
87 options->usergroups = 0;
88 else if (strcasecmp (argv, "silent") == 0)
89 options->silent = 1;
90 else
91 pam_syslog (pamh, LOG_ERR, "Unknown option: `%s'", argv);
92 }
93
94 static int
95 get_options (pam_handle_t *pamh, options_t *options,
96 int argc, const char **argv)
97 {
98 memset (options, 0, sizeof (options_t));
99
100 options->usergroups = DEFAULT_USERGROUPS_SETTING;
101
102 /* Parse parameters for module */
103 for ( ; argc-- > 0; argv++)
104 parse_option (pamh, *argv, options);
105
106 if (options->umask == NULL) {
107 options->login_umask = pam_modutil_search_key (pamh, LOGIN_DEFS, "UMASK");
108 if (options->login_umask == NULL)
109 options->login_umask = pam_modutil_search_key (pamh, LOGIN_CONF, "UMASK");
110 options->umask = options->login_umask;
111 }
112
113 return 0;
114 }
115
116 static void
117 set_umask (const char *value)
118 {
119 const char *value_orig = value;
120 mode_t mask;
121 char *endptr;
122
123 mask = strtoul (value, &endptr, 8) & 0777;
124 if (((mask == 0) && (value_orig == endptr)) ||
125 ((mask == UINT_MAX) && (errno == ERANGE)))
126 return;
127 umask (mask);
128 return;
129 }
130
131 /* Set the process nice, ulimit, and umask from the
132 password file entry. */
133 static void
134 setup_limits_from_gecos (pam_handle_t *pamh, options_t *options,
135 struct passwd *pw)
136 {
137 char *cp;
138
139 if (options->usergroups)
140 {
141 /* if not root and username is the same as primary group name,
142 set umask group bits to be the same as owner bits
143 (examples: 022 -> 002, 077 -> 007). */
144 if (pw->pw_uid != 0)
145 {
146 struct group *grp = pam_modutil_getgrgid (pamh, pw->pw_gid);
147 if (grp && (strcmp (pw->pw_name, grp->gr_name) == 0))
148 {
149 mode_t oldmask = umask (0777);
150 umask ((oldmask & ~070) | ((oldmask >> 3) & 070));
151 }
152 }
153 }
154
155 /* See if the GECOS field contains values for NICE, UMASK or ULIMIT. */
156 for (cp = pw->pw_gecos; cp != NULL; cp = strchr (cp, ','))
157 {
158 const char *str;
159
160 if (*cp == ',')
161 cp++;
162
163 if ((str = pam_str_skip_icase_prefix (cp, "umask=")) != NULL)
164 umask (strtol (str, NULL, 8) & 0777);
165 else if ((str = pam_str_skip_icase_prefix (cp, "pri=")) != NULL)
166 {
167 errno = 0;
168 if (nice (strtol (str, NULL, 10)) == -1 && errno != 0)
169 {
170 if (!options->silent || options->debug)
171 pam_error (pamh, "nice failed: %m\n");
172 pam_syslog (pamh, LOG_ERR, "nice failed: %m");
173 }
174 }
175 else if ((str = pam_str_skip_icase_prefix (cp, "ulimit=")) != NULL)
176 {
177 struct rlimit rlimit_fsize;
178 rlimit_fsize.rlim_cur = 512L * strtol (str, NULL, 10);
179 rlimit_fsize.rlim_max = rlimit_fsize.rlim_cur;
180 if (setrlimit (RLIMIT_FSIZE, &rlimit_fsize) == -1)
181 {
182 if (!options->silent || options->debug)
183 pam_error (pamh, "setrlimit failed: %m\n");
184 pam_syslog (pamh, LOG_ERR, "setrlimit failed: %m");
185 }
186 }
187 }
188 }
189
190
191 int
192 pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
193 int argc, const char **argv)
194 {
195 struct passwd *pw;
196 options_t options;
197 const char *name;
198 int retval = PAM_SUCCESS;
199
200 get_options (pamh, &options, argc, argv);
201 if (flags & PAM_SILENT)
202 options.silent = 1;
203
204 /* get the user name. */
205 if ((retval = pam_get_user (pamh, &name, NULL)) != PAM_SUCCESS)
206 {
207 pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
208 pam_strerror(pamh, retval));
209 return (retval == PAM_CONV_AGAIN ? PAM_INCOMPLETE:retval);
210 }
211
212 pw = pam_modutil_getpwnam (pamh, name);
213 if (pw == NULL)
214 {
215 pam_syslog (pamh, LOG_NOTICE, "account for %s not found", name);
216 return PAM_USER_UNKNOWN;
217 }
218
219 if (options.umask != NULL)
220 {
221 set_umask (options.umask);
222 free (options.login_umask);
223 options.umask = options.login_umask = NULL;
224 }
225
226 setup_limits_from_gecos (pamh, &options, pw);
227
228 return retval;
229 }
230
231 int
232 pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED,
233 int argc UNUSED, const char **argv UNUSED)
234 {
235 return PAM_SUCCESS;
236 }
237
238 /* end of module definition */