1 /*
2 * Copyright (c) 1999,2007,19,20 Andrew G. Morgan <morgan@kernel.org>
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 DEBUG */
9
10 #ifndef _DEFAULT_SOURCE
11 #define _DEFAULT_SOURCE
12 #endif
13
14 #include <errno.h>
15 #include <grp.h>
16 #include <limits.h>
17 #include <pwd.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <syslog.h>
23 #include <sys/capability.h>
24 #include <sys/types.h>
25 #include <linux/limits.h>
26
27 #include <security/pam_modules.h>
28 #include <security/_pam_macros.h>
29
30 #define USER_CAP_FILE "/etc/security/capability.conf"
31 #define CAP_FILE_BUFFER_SIZE 4096
32 #define CAP_FILE_DELIMITERS " \t\n"
33
34 struct pam_cap_s {
35 int debug;
36 const char *user;
37 const char *conf_filename;
38 };
39
40 /*
41 * load_groups obtains the list all of the groups associated with the
42 * requested user: gid & supplemental groups.
43 */
load_groups(const char * user,char *** groups,int * groups_n)44 static int load_groups(const char *user, char ***groups, int *groups_n) {
45 struct passwd *pwd;
46 gid_t grps[NGROUPS_MAX];
47 int ngrps = NGROUPS_MAX;
48
49 *groups = NULL;
50 *groups_n = 0;
51
52 pwd = getpwnam(user);
53 if (pwd == NULL) {
54 return -1;
55 }
56
57 /* must include at least pwd->pw_gid, hence < 1 test. */
58 if (getgrouplist(user, pwd->pw_gid, grps, &ngrps) < 1) {
59 return -1;
60 }
61
62 *groups = calloc(ngrps, sizeof(char *));
63 int g_n = 0, i;
64 for (i = 0; i < ngrps; i++) {
65 const struct group *g = getgrgid(grps[i]);
66 if (g == NULL) {
67 continue;
68 }
69 D(("noting [%s] is a member of [%s]", user, g->gr_name));
70 (*groups)[g_n++] = strdup(g->gr_name);
71 }
72
73 *groups_n = g_n;
74 return 0;
75 }
76
77 /* obtain the inheritable capabilities for the current user */
78
read_capabilities_for_user(const char * user,const char * source)79 static char *read_capabilities_for_user(const char *user, const char *source)
80 {
81 char *cap_string = NULL;
82 char buffer[CAP_FILE_BUFFER_SIZE], *line;
83 char **groups;
84 int groups_n;
85 FILE *cap_file;
86
87 if (load_groups(user, &groups, &groups_n)) {
88 D(("unknown user [%s]", user));
89 return NULL;
90 }
91
92 cap_file = fopen(source, "r");
93 if (cap_file == NULL) {
94 D(("failed to open capability file"));
95 goto defer;
96 }
97
98 int found_one = 0;
99 while (!found_one &&
100 (line = fgets(buffer, CAP_FILE_BUFFER_SIZE, cap_file))) {
101 const char *cap_text;
102
103 char *next = NULL;
104 cap_text = strtok_r(line, CAP_FILE_DELIMITERS, &next);
105
106 if (cap_text == NULL) {
107 D(("empty line"));
108 continue;
109 }
110 if (*cap_text == '#') {
111 D(("comment line"));
112 continue;
113 }
114
115 /*
116 * Explore whether any of the ids are a match for the current
117 * user.
118 */
119 while ((line = strtok_r(next, CAP_FILE_DELIMITERS, &next))) {
120 if (strcmp("*", line) == 0) {
121 D(("wildcard matched"));
122 found_one = 1;
123 break;
124 }
125
126 if (strcmp(user, line) == 0) {
127 D(("exact match for user"));
128 found_one = 1;
129 break;
130 }
131
132 if (line[0] != '@') {
133 D(("user [%s] is not [%s] - skipping", user, line));
134 }
135
136 int i;
137 for (i=0; i < groups_n; i++) {
138 if (!strcmp(groups[i], line+1)) {
139 D(("user group matched [%s]", line));
140 found_one = 1;
141 break;
142 }
143 }
144 if (found_one) {
145 break;
146 }
147 }
148
149 if (found_one) {
150 cap_string = strdup(cap_text);
151 D(("user [%s] matched - caps are [%s]", user, cap_string));
152 }
153
154 cap_text = NULL;
155 line = NULL;
156 }
157
158 fclose(cap_file);
159
160 defer:
161 memset(buffer, 0, CAP_FILE_BUFFER_SIZE);
162
163 int i;
164 for (i = 0; i < groups_n; i++) {
165 char *g = groups[i];
166 _pam_overwrite(g);
167 _pam_drop(g);
168 }
169 if (groups != NULL) {
170 memset(groups, 0, groups_n * sizeof(char *));
171 _pam_drop(groups);
172 }
173
174 return cap_string;
175 }
176
177 /*
178 * Set capabilities for current process to match the current
179 * permitted+executable sets combined with the configured inheritable
180 * set.
181 */
set_capabilities(struct pam_cap_s * cs)182 static int set_capabilities(struct pam_cap_s *cs)
183 {
184 cap_t cap_s;
185 char *conf_caps;
186 int ok = 0;
187 cap_iab_t iab;
188
189 cap_s = cap_get_proc();
190 if (cap_s == NULL) {
191 D(("your kernel is capability challenged - upgrade: %s",
192 strerror(errno)));
193 return 0;
194 }
195
196 conf_caps = read_capabilities_for_user(cs->user,
197 cs->conf_filename
198 ? cs->conf_filename:USER_CAP_FILE );
199 if (conf_caps == NULL) {
200 D(("no capabilities found for user [%s]", cs->user));
201 goto cleanup_cap_s;
202 }
203
204 ssize_t conf_caps_length = strlen(conf_caps);
205 if (!strcmp(conf_caps, "all")) {
206 /*
207 * all here is interpreted as no change/pass through, which is
208 * likely to be the same as none for sensible system defaults.
209 */
210 ok = 1;
211 goto cleanup_conf;
212 }
213
214 if (!strcmp(conf_caps, "none")) {
215 /* clearing CAP_INHERITABLE will also clear the ambient caps,
216 * but for legacy reasons we do not alter the bounding set. */
217 cap_clear_flag(cap_s, CAP_INHERITABLE);
218 if (!cap_set_proc(cap_s)) {
219 ok = 1;
220 }
221 goto cleanup_cap_s;
222 }
223
224 iab = cap_iab_from_text(conf_caps);
225 if (iab == NULL) {
226 D(("unable to parse the IAB [%s] value", conf_caps));
227 goto cleanup_conf;
228 }
229
230 if (!cap_iab_set_proc(iab)) {
231 D(("able to set the IAB [%s] value", conf_caps));
232 ok = 1;
233 }
234 cap_free(iab);
235
236 cleanup_conf:
237 memset(conf_caps, 0, conf_caps_length);
238 _pam_drop(conf_caps);
239
240 cleanup_cap_s:
241 if (cap_s) {
242 cap_free(cap_s);
243 cap_s = NULL;
244 }
245 return ok;
246 }
247
248 /* log errors */
249
_pam_log(int err,const char * format,...)250 static void _pam_log(int err, const char *format, ...)
251 {
252 va_list args;
253
254 va_start(args, format);
255 openlog("pam_cap", LOG_CONS|LOG_PID, LOG_AUTH);
256 vsyslog(err, format, args);
257 va_end(args);
258 closelog();
259 }
260
parse_args(int argc,const char ** argv,struct pam_cap_s * pcs)261 static void parse_args(int argc, const char **argv, struct pam_cap_s *pcs)
262 {
263 /* step through arguments */
264 for (; argc-- > 0; ++argv) {
265 if (!strcmp(*argv, "debug")) {
266 pcs->debug = 1;
267 } else if (!strncmp(*argv, "config=", 7)) {
268 pcs->conf_filename = 7 + *argv;
269 } else {
270 _pam_log(LOG_ERR, "unknown option; %s", *argv);
271 }
272 }
273 }
274
275 /*
276 * pam_sm_authenticate parses the config file with respect to the user
277 * being authenticated and determines if they are covered by any
278 * capability inheritance rules.
279 */
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)280 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
281 int argc, const char **argv)
282 {
283 int retval;
284 struct pam_cap_s pcs;
285 char *conf_caps;
286
287 memset(&pcs, 0, sizeof(pcs));
288 parse_args(argc, argv, &pcs);
289
290 retval = pam_get_user(pamh, &pcs.user, NULL);
291 if (retval == PAM_CONV_AGAIN) {
292 D(("user conversation is not available yet"));
293 memset(&pcs, 0, sizeof(pcs));
294 return PAM_INCOMPLETE;
295 }
296
297 if (retval != PAM_SUCCESS) {
298 D(("pam_get_user failed: %s", pam_strerror(pamh, retval)));
299 memset(&pcs, 0, sizeof(pcs));
300 return PAM_AUTH_ERR;
301 }
302
303 conf_caps = read_capabilities_for_user(pcs.user,
304 pcs.conf_filename
305 ? pcs.conf_filename:USER_CAP_FILE );
306 memset(&pcs, 0, sizeof(pcs));
307
308 if (conf_caps) {
309 D(("it appears that there are capabilities for this user [%s]",
310 conf_caps));
311
312 /* We could also store this as a pam_[gs]et_data item for use
313 by the setcred call to follow. As it is, there is a small
314 race associated with a redundant read. Oh well, if you
315 care, send me a patch.. */
316
317 _pam_overwrite(conf_caps);
318 _pam_drop(conf_caps);
319
320 return PAM_SUCCESS;
321
322 } else {
323
324 D(("there are no capabilities restrictions on this user"));
325 return PAM_IGNORE;
326
327 }
328 }
329
330 /*
331 * pam_sm_setcred applies inheritable capabilities loaded by the
332 * pam_sm_authenticate pass for the user.
333 */
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)334 int pam_sm_setcred(pam_handle_t *pamh, int flags,
335 int argc, const char **argv)
336 {
337 int retval;
338 struct pam_cap_s pcs;
339
340 if (!(flags & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED))) {
341 D(("we don't handle much in the way of credentials"));
342 return PAM_IGNORE;
343 }
344
345 memset(&pcs, 0, sizeof(pcs));
346 parse_args(argc, argv, &pcs);
347
348 retval = pam_get_item(pamh, PAM_USER, (const void **)&pcs.user);
349 if ((retval != PAM_SUCCESS) || (pcs.user == NULL) || !(pcs.user[0])) {
350 D(("user's name is not set"));
351 return PAM_AUTH_ERR;
352 }
353
354 retval = set_capabilities(&pcs);
355 memset(&pcs, 0, sizeof(pcs));
356
357 return (retval ? PAM_SUCCESS:PAM_IGNORE );
358 }
359