xref: /aosp_15_r20/external/libcap/pam_cap/pam_cap.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
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