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