1 /*
2 * Copyright (c) 1999,2007,2019-21 Andrew G. Morgan <[email protected]>
3 *
4 * The purpose of this module is to enforce inheritable, bounding and
5 * ambient capability sets for a specified user.
6 */
7
8 /* #define PAM_DEBUG */
9
10 #ifndef _DEFAULT_SOURCE
11 #define _DEFAULT_SOURCE
12 #endif
13
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <grp.h>
17 #include <limits.h>
18 #include <pwd.h>
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <syslog.h>
24 #include <sys/capability.h>
25 #include <sys/prctl.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <linux/limits.h>
29
30 #include <security/pam_modules.h>
31 #include <security/_pam_macros.h>
32
33 #define USER_CAP_FILE "/etc/security/capability.conf"
34 #define CAP_FILE_BUFFER_SIZE 4096
35 #define CAP_FILE_DELIMITERS " \t\n"
36
37 /*
38 * pam_cap_s is used to summarize argument values in a parsed form.
39 */
40 struct pam_cap_s {
41 int debug;
42 int keepcaps;
43 int autoauth;
44 int defer;
45 const char *user;
46 const char *conf_filename;
47 const char *fallback;
48 pam_handle_t *pamh;
49 };
50
51 /*
52 * load_groups obtains the list all of the groups associated with the
53 * requested user: gid & supplemental groups.
54 */
load_groups(const char * user,char *** groups,int * groups_n)55 static int load_groups(const char *user, char ***groups, int *groups_n) {
56 struct passwd *pwd;
57 gid_t grps[NGROUPS_MAX];
58 int ngrps = NGROUPS_MAX;
59
60 *groups = NULL;
61 *groups_n = 0;
62
63 pwd = getpwnam(user);
64 if (pwd == NULL) {
65 return -1;
66 }
67
68 /* must include at least pwd->pw_gid, hence < 1 test. */
69 if (getgrouplist(user, pwd->pw_gid, grps, &ngrps) < 1) {
70 return -1;
71 }
72
73 *groups = calloc(ngrps, sizeof(char *));
74 if (*groups == NULL) {
75 return -1;
76 }
77 int g_n = 0, i;
78 for (i = 0; i < ngrps; i++) {
79 const struct group *g = getgrgid(grps[i]);
80 if (g == NULL) {
81 continue;
82 }
83 D(("noting [%s] is a member of [%s]", user, g->gr_name));
84 (*groups)[g_n++] = strdup(g->gr_name);
85 }
86
87 *groups_n = g_n;
88 return 0;
89 }
90
91 /* obtain the desired IAB capabilities for the current user */
92
read_capabilities_for_user(const char * user,const char * source)93 static char *read_capabilities_for_user(const char *user, const char *source)
94 {
95 char *cap_string = NULL;
96 char buffer[CAP_FILE_BUFFER_SIZE], *line;
97 char **groups;
98 int groups_n;
99 FILE *cap_file;
100
101 if (load_groups(user, &groups, &groups_n)) {
102 D(("unknown user [%s]", user));
103 return NULL;
104 }
105
106 cap_file = fopen(source, "r");
107 if (cap_file == NULL) {
108 D(("failed to open capability file"));
109 goto defer;
110 }
111 /*
112 * In all cases other than "/dev/null", the config file should not
113 * be world writable. We do not check for ownership limitations or
114 * group write restrictions as these represent legitimate local
115 * administration choices. Especially in a system operating in
116 * CAP_MODE_PURE1E.
117 */
118 if (strcmp(source, "/dev/null") != 0) {
119 struct stat sb;
120 D(("validate filehandle [for opened %s] does not point to a world"
121 " writable file", source));
122 if (fstat(fileno(cap_file), &sb) != 0) {
123 D(("unable to fstat config file: %d", errno));
124 goto close_out_file;
125 }
126 if ((sb.st_mode & S_IWOTH) != 0) {
127 D(("open failed [%s] is world writable test: security hole",
128 source));
129 goto close_out_file;
130 }
131 }
132
133 int found_one = 0;
134 while (!found_one &&
135 (line = fgets(buffer, CAP_FILE_BUFFER_SIZE, cap_file))) {
136 const char *cap_text;
137
138 char *next = NULL;
139 cap_text = strtok_r(line, CAP_FILE_DELIMITERS, &next);
140
141 if (cap_text == NULL) {
142 D(("empty line"));
143 continue;
144 }
145 if (*cap_text == '#') {
146 D(("comment line"));
147 continue;
148 }
149
150 /*
151 * Explore whether any of the ids are a match for the current
152 * user.
153 */
154 while ((line = strtok_r(next, CAP_FILE_DELIMITERS, &next))) {
155 if (strcmp("*", line) == 0) {
156 D(("wildcard matched"));
157 found_one = 1;
158 break;
159 }
160
161 if (strcmp(user, line) == 0) {
162 D(("exact match for user"));
163 found_one = 1;
164 break;
165 }
166
167 if (line[0] != '@') {
168 D(("user [%s] is not [%s] - skipping", user, line));
169 }
170
171 int i;
172 for (i=0; i < groups_n; i++) {
173 if (!strcmp(groups[i], line+1)) {
174 D(("user group matched [%s]", line));
175 found_one = 1;
176 break;
177 }
178 }
179 if (found_one) {
180 break;
181 }
182 }
183
184 if (found_one) {
185 cap_string = strdup(cap_text);
186 D(("user [%s] matched - caps are [%s]", user, cap_string));
187 }
188
189 cap_text = NULL;
190 line = NULL;
191 }
192
193 close_out_file:
194 fclose(cap_file);
195
196 defer:
197 memset(buffer, 0, CAP_FILE_BUFFER_SIZE);
198
199 int i;
200 for (i = 0; i < groups_n; i++) {
201 char *g = groups[i];
202 _pam_overwrite(g);
203 _pam_drop(g);
204 }
205 if (groups != NULL) {
206 memset(groups, 0, groups_n * sizeof(char *));
207 _pam_drop(groups);
208 }
209
210 return cap_string;
211 }
212
213 /*
214 * This is the "defer" cleanup function that actually applies the IAB
215 * tuple. This happens really late in the PAM session, hopefully after
216 * the application has performed its setuid() function.
217 */
iab_apply(pam_handle_t * pamh,void * data,int error_status)218 static void iab_apply(pam_handle_t *pamh, void *data, int error_status)
219 {
220 cap_iab_t iab = data;
221 int retval = error_status & ~(PAM_DATA_REPLACE|PAM_DATA_SILENT);
222
223 #ifdef PAM_DEBUG
224 {
225 cap_t c = cap_get_proc();
226 cap_iab_t tu = cap_iab_get_proc();
227 char *tc, *ttu;
228 tc = cap_to_text(c, NULL);
229 ttu = cap_iab_to_text(tu);
230
231 D(("iab_apply with uid=%d,euid=%d and error_status=0x%08x \"%s\", [%s]",
232 getuid(), geteuid(), error_status, tc, ttu));
233
234 cap_free(ttu);
235 cap_free(tc);
236 cap_free(tu);
237 cap_free(c);
238 }
239 #endif
240
241 data = NULL;
242 if (error_status & PAM_DATA_REPLACE) {
243 goto done;
244 }
245
246 if (retval != PAM_SUCCESS || !(error_status & PAM_DATA_SILENT)) {
247 goto done;
248 }
249
250 if (cap_iab_set_proc(iab) != 0) {
251 D(("IAB setting failed"));
252 }
253
254 done:
255 cap_free(iab);
256 }
257
258 /*
259 * Set capabilities for current process to match the current
260 * permitted+executable sets combined with the configured inheritable
261 * set.
262 */
set_capabilities(struct pam_cap_s * cs)263 static int set_capabilities(struct pam_cap_s *cs)
264 {
265 cap_t cap_s;
266 char *conf_caps;
267 int ok = 0;
268 cap_iab_t iab;
269
270 cap_s = cap_get_proc();
271 if (cap_s == NULL) {
272 D(("your kernel is capability challenged - upgrade: %s",
273 strerror(errno)));
274 return 0;
275 }
276
277 conf_caps = read_capabilities_for_user(cs->user,
278 cs->conf_filename
279 ? cs->conf_filename:USER_CAP_FILE );
280 if (conf_caps == NULL) {
281 D(("no capabilities found for user [%s]", cs->user));
282 if (cs->fallback == NULL) {
283 goto cleanup_cap_s;
284 }
285 conf_caps = strdup(cs->fallback);
286 D(("user [%s] received fallback caps [%s]", cs->user, conf_caps));
287 }
288
289 ssize_t conf_caps_length = strlen(conf_caps);
290 if (!strcmp(conf_caps, "all")) {
291 /*
292 * all here is interpreted as no change/pass through, which is
293 * likely to be the same as none for sensible system defaults.
294 */
295 ok = 1;
296 goto cleanup_conf;
297 }
298
299 if (!strcmp(conf_caps, "none")) {
300 /* clearing CAP_INHERITABLE will also clear the ambient caps,
301 * but for legacy reasons we do not alter the bounding set. */
302 cap_clear_flag(cap_s, CAP_INHERITABLE);
303 if (!cap_set_proc(cap_s)) {
304 ok = 1;
305 }
306 goto cleanup_conf;
307 }
308
309 iab = cap_iab_from_text(conf_caps);
310 if (iab == NULL) {
311 D(("unable to parse the IAB [%s] value", conf_caps));
312 goto cleanup_conf;
313 }
314
315 if (cs->defer) {
316 D(("configured to delay applying IAB"));
317 int ret = pam_set_data(cs->pamh, "pam_cap_iab", iab, iab_apply);
318 if (ret != PAM_SUCCESS) {
319 D(("unable to cache capabilities for delayed setting: %d", ret));
320 /* since ok=0, the module will return PAM_IGNORE */
321 cap_free(iab);
322 }
323 iab = NULL;
324 } else if (!cap_iab_set_proc(iab)) {
325 D(("able to set the IAB [%s] value", conf_caps));
326 ok = 1;
327 }
328 cap_free(iab);
329
330 if (cs->keepcaps) {
331 /*
332 * Best effort to set keep caps - this may help work around
333 * situations where applications are using a capabilities
334 * unaware setuid() call.
335 *
336 * It isn't needed unless you want to support Ambient vector
337 * values in the IAB. In this case, it will likely also
338 * require you use the "defer" module argument.
339 */
340 D(("setting keepcaps"));
341 (void) cap_prctlw(PR_SET_KEEPCAPS, 1, 0, 0, 0, 0);
342 }
343
344 cleanup_conf:
345 memset(conf_caps, 0, conf_caps_length);
346 _pam_drop(conf_caps);
347
348 cleanup_cap_s:
349 cap_free(cap_s);
350 cap_s = NULL;
351
352 return ok;
353 }
354
355 /* log errors */
356
_pam_log(int err,const char * format,...)357 static void _pam_log(int err, const char *format, ...)
358 {
359 va_list args;
360
361 va_start(args, format);
362 openlog("pam_cap", LOG_CONS|LOG_PID, LOG_AUTH);
363 vsyslog(err, format, args);
364 va_end(args);
365 closelog();
366 }
367
parse_args(int argc,const char ** argv,struct pam_cap_s * pcs)368 static void parse_args(int argc, const char **argv, struct pam_cap_s *pcs)
369 {
370 D(("parsing %d module arg(s)", argc));
371
372 memset(pcs, 0, sizeof(*pcs));
373
374 /* step through arguments */
375 for (; argc-- > 0; ++argv) {
376 if (!strcmp(*argv, "debug")) {
377 pcs->debug = 1;
378 } else if (!strncmp(*argv, "config=", 7)) {
379 pcs->conf_filename = 7 + *argv;
380 } else if (!strcmp(*argv, "keepcaps")) {
381 pcs->keepcaps = 1;
382 } else if (!strcmp(*argv, "autoauth")) {
383 pcs->autoauth = 1;
384 } else if (!strncmp(*argv, "default=", 8)) {
385 pcs->fallback = 8 + *argv;
386 } else if (!strcmp(*argv, "defer")) {
387 pcs->defer = 1;
388 } else {
389 _pam_log(LOG_ERR, "unknown option; %s", *argv);
390 }
391 }
392 }
393
394 /*
395 * pam_sm_authenticate parses the config file with respect to the user
396 * being authenticated and determines if they are covered by any
397 * capability inheritance rules.
398 */
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)399 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
400 int argc, const char **argv)
401 {
402 int retval;
403 struct pam_cap_s pcs;
404 char *conf_caps;
405
406 parse_args(argc, argv, &pcs);
407
408 retval = pam_get_user(pamh, &pcs.user, NULL);
409 if (retval == PAM_CONV_AGAIN) {
410 D(("user conversation is not available yet"));
411 memset(&pcs, 0, sizeof(pcs));
412 return PAM_INCOMPLETE;
413 }
414
415 if (pcs.autoauth) {
416 D(("pam_sm_authenticate autoauth = success"));
417 memset(&pcs, 0, sizeof(pcs));
418 return PAM_SUCCESS;
419 }
420
421 if (retval != PAM_SUCCESS) {
422 D(("pam_get_user failed: pam error=%d", retval));
423 memset(&pcs, 0, sizeof(pcs));
424 return PAM_AUTH_ERR;
425 }
426
427 conf_caps = read_capabilities_for_user(pcs.user,
428 pcs.conf_filename
429 ? pcs.conf_filename:USER_CAP_FILE );
430 memset(&pcs, 0, sizeof(pcs));
431
432 if (conf_caps) {
433 D(("it appears that there are capabilities for this user [%s]",
434 conf_caps));
435
436 /* We could also store this as a pam_[gs]et_data item for use
437 by the setcred call to follow. However, this precludes
438 using pam_cap as just a cred module, and requires that the
439 'auth' component be called first. As it is, there is a
440 small race associated with a redundant read of the
441 config. */
442
443 _pam_overwrite(conf_caps);
444 _pam_drop(conf_caps);
445
446 return PAM_SUCCESS;
447 }
448
449 D(("there are no capabilities restrictions on this user"));
450 return PAM_IGNORE;
451 }
452
453 /*
454 * pam_sm_setcred optionally applies inheritable capabilities loaded
455 * by the pam_sm_authenticate pass for the user. If it doesn't apply
456 * them directly (because of the "defer" module argument), it caches
457 * the cap_iab_t value for later use during the pam_end() call.
458 */
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)459 int pam_sm_setcred(pam_handle_t *pamh, int flags,
460 int argc, const char **argv)
461 {
462 int retval = 0;
463 struct pam_cap_s pcs;
464
465 if (!(flags & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED))) {
466 D(("we don't handle much in the way of credentials"));
467 return PAM_IGNORE;
468 }
469
470 parse_args(argc, argv, &pcs);
471
472 retval = pam_get_item(pamh, PAM_USER, (const void **)&pcs.user);
473 if ((retval != PAM_SUCCESS) || (pcs.user == NULL) || !(pcs.user[0])) {
474 D(("user's name is not set"));
475 return PAM_AUTH_ERR;
476 }
477
478 pcs.pamh = pamh;
479 retval = set_capabilities(&pcs);
480 memset(&pcs, 0, sizeof(pcs));
481
482 return (retval ? PAM_SUCCESS:PAM_IGNORE);
483 }
484