/src/selinux/libselinux/src/seusers.c
Line | Count | Source |
1 | | #include <unistd.h> |
2 | | #include <fcntl.h> |
3 | | #include <stdlib.h> |
4 | | #include <string.h> |
5 | | #include <stdio.h> |
6 | | #include <stdio_ext.h> |
7 | | #include <ctype.h> |
8 | | #include <errno.h> |
9 | | #include <limits.h> |
10 | | |
11 | | #include <selinux/selinux.h> |
12 | | #include <selinux/context.h> |
13 | | |
14 | | #include "selinux_internal.h" |
15 | | #include "callbacks.h" |
16 | | |
17 | | /* Process line from seusers.conf and split into its fields. |
18 | | Returns 0 on success, -1 on comments, and -2 on error. */ |
19 | | static int process_seusers(const char *buffer, char **luserp, char **seuserp, |
20 | | char **levelp, int mls_enabled) |
21 | 0 | { |
22 | 0 | char *newbuf = strdup(buffer); |
23 | 0 | char *luser = NULL, *seuser = NULL, *level = NULL; |
24 | 0 | char *start, *end; |
25 | 0 | int mls_found = 1; |
26 | |
|
27 | 0 | if (!newbuf) |
28 | 0 | goto err; |
29 | | |
30 | 0 | start = newbuf; |
31 | 0 | while (isspace((unsigned char)*start)) |
32 | 0 | start++; |
33 | 0 | if (*start == '#' || *start == 0) { |
34 | 0 | free(newbuf); |
35 | 0 | return -1; /* Comment or empty line, skip over */ |
36 | 0 | } |
37 | 0 | end = strchr(start, ':'); |
38 | 0 | if (!end) |
39 | 0 | goto err; |
40 | 0 | *end = 0; |
41 | |
|
42 | 0 | luser = strdup(start); |
43 | 0 | if (!luser) |
44 | 0 | goto err; |
45 | | |
46 | 0 | start = end + 1; |
47 | 0 | end = strchr(start, ':'); |
48 | 0 | if (!end) { |
49 | 0 | mls_found = 0; |
50 | |
|
51 | 0 | end = start; |
52 | 0 | while (*end && !isspace((unsigned char)*end)) |
53 | 0 | end++; |
54 | 0 | } |
55 | 0 | *end = 0; |
56 | |
|
57 | 0 | seuser = strdup(start); |
58 | 0 | if (!seuser) |
59 | 0 | goto err; |
60 | | |
61 | 0 | if (!strcmp(seuser, "")) |
62 | 0 | goto err; |
63 | | |
64 | | /* Skip MLS if disabled, or missing. */ |
65 | 0 | if (!mls_enabled || !mls_found) |
66 | 0 | goto out; |
67 | | |
68 | 0 | start = ++end; |
69 | 0 | while (*end && !isspace((unsigned char)*end)) |
70 | 0 | end++; |
71 | 0 | *end = 0; |
72 | |
|
73 | 0 | level = strdup(start); |
74 | 0 | if (!level) |
75 | 0 | goto err; |
76 | | |
77 | 0 | if (!strcmp(level, "")) |
78 | 0 | goto err; |
79 | | |
80 | 0 | out: |
81 | 0 | free(newbuf); |
82 | 0 | *luserp = luser; |
83 | 0 | *seuserp = seuser; |
84 | 0 | *levelp = level; |
85 | 0 | return 0; |
86 | 0 | err: |
87 | 0 | free(newbuf); |
88 | 0 | free(luser); |
89 | 0 | free(seuser); |
90 | 0 | free(level); |
91 | 0 | return -2; /* error */ |
92 | 0 | } |
93 | | |
94 | | int require_seusers = 0; |
95 | | |
96 | | #include <pwd.h> |
97 | | #include <grp.h> |
98 | | |
99 | | static gid_t get_default_gid(const char *name) |
100 | 0 | { |
101 | 0 | struct passwd pwstorage, *pwent = NULL; |
102 | 0 | gid_t gid = (gid_t)-1; |
103 | | /* Allocate space for the getpwnam_r buffer */ |
104 | 0 | char *rbuf = NULL; |
105 | 0 | long rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); |
106 | 0 | if (rbuflen <= 0) |
107 | 0 | rbuflen = 1024; |
108 | |
|
109 | 0 | for (;;) { |
110 | 0 | int rc; |
111 | |
|
112 | 0 | rbuf = malloc(rbuflen); |
113 | 0 | if (rbuf == NULL) |
114 | 0 | break; |
115 | | |
116 | 0 | rc = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent); |
117 | 0 | if (rc == ERANGE && |
118 | 0 | !__builtin_smull_overflow(rbuflen, 2, &rbuflen)) { |
119 | 0 | free(rbuf); |
120 | 0 | continue; |
121 | 0 | } |
122 | 0 | if (rc == 0 && pwent) |
123 | 0 | gid = pwent->pw_gid; |
124 | |
|
125 | 0 | break; |
126 | 0 | } |
127 | |
|
128 | 0 | free(rbuf); |
129 | 0 | return gid; |
130 | 0 | } |
131 | | |
132 | | static int check_group(const char *group, const char *name, const gid_t gid) |
133 | 0 | { |
134 | 0 | int match = 0; |
135 | 0 | int i, ng = 0; |
136 | 0 | gid_t *groups = NULL; |
137 | 0 | struct group gbuf, *grent = NULL; |
138 | |
|
139 | 0 | long rbuflen = sysconf(_SC_GETGR_R_SIZE_MAX); |
140 | 0 | if (rbuflen <= 0) |
141 | 0 | rbuflen = 1024; |
142 | 0 | char *rbuf; |
143 | |
|
144 | 0 | while (1) { |
145 | 0 | rbuf = malloc(rbuflen); |
146 | 0 | if (rbuf == NULL) |
147 | 0 | return 0; |
148 | 0 | int retval = getgrnam_r(group, &gbuf, rbuf, rbuflen, &grent); |
149 | 0 | if (retval == ERANGE && rbuflen < LONG_MAX / 2) { |
150 | 0 | free(rbuf); |
151 | 0 | rbuflen = rbuflen * 2; |
152 | 0 | } else if (retval != 0 || grent == NULL) { |
153 | 0 | goto done; |
154 | 0 | } else { |
155 | 0 | break; |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | 0 | if (getgrouplist(name, gid, NULL, &ng) < 0) { |
160 | 0 | if (ng == 0) |
161 | 0 | goto done; |
162 | 0 | groups = calloc(ng, sizeof(*groups)); |
163 | 0 | if (!groups) |
164 | 0 | goto done; |
165 | 0 | if (getgrouplist(name, gid, groups, &ng) < 0) |
166 | 0 | goto done; |
167 | 0 | } else { |
168 | | /* WTF? ng was 0 and we didn't fail? Are we in 0 groups? */ |
169 | 0 | goto done; |
170 | 0 | } |
171 | | |
172 | 0 | for (i = 0; i < ng; i++) { |
173 | 0 | if (grent->gr_gid == groups[i]) { |
174 | 0 | match = 1; |
175 | 0 | goto done; |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | 0 | done: |
180 | 0 | free(groups); |
181 | 0 | free(rbuf); |
182 | 0 | return match; |
183 | 0 | } |
184 | | |
185 | | int getseuserbyname(const char *name, char **r_seuser, char **r_level) |
186 | 0 | { |
187 | 0 | FILE *cfg = NULL; |
188 | 0 | size_t size = 0; |
189 | 0 | char *buffer = NULL; |
190 | 0 | int rc; |
191 | 0 | unsigned long lineno = 0; |
192 | 0 | int mls_enabled = is_selinux_mls_enabled(); |
193 | |
|
194 | 0 | char *username = NULL; |
195 | 0 | char *seuser = NULL; |
196 | 0 | char *level = NULL; |
197 | 0 | char *groupseuser = NULL; |
198 | 0 | char *grouplevel = NULL; |
199 | 0 | char *defaultseuser = NULL; |
200 | 0 | char *defaultlevel = NULL; |
201 | |
|
202 | 0 | gid_t gid = get_default_gid(name); |
203 | |
|
204 | 0 | cfg = fopen(selinux_usersconf_path(), "re"); |
205 | 0 | if (!cfg) |
206 | 0 | goto nomatch; |
207 | | |
208 | 0 | __fsetlocking(cfg, FSETLOCKING_BYCALLER); |
209 | 0 | while (getline(&buffer, &size, cfg) > 0) { |
210 | 0 | ++lineno; |
211 | 0 | rc = process_seusers(buffer, &username, &seuser, &level, |
212 | 0 | mls_enabled); |
213 | 0 | if (rc == -1) |
214 | 0 | continue; /* comment, skip */ |
215 | 0 | if (rc == -2) { |
216 | 0 | selinux_log(SELINUX_ERROR, |
217 | 0 | "%s: error on line %lu, skipping...\n", |
218 | 0 | selinux_usersconf_path(), lineno); |
219 | 0 | continue; |
220 | 0 | } |
221 | | |
222 | 0 | if (!strcmp(username, name)) |
223 | 0 | break; |
224 | | |
225 | 0 | if (username[0] == '%' && !groupseuser && |
226 | 0 | check_group(&username[1], name, gid)) { |
227 | 0 | groupseuser = seuser; |
228 | 0 | grouplevel = level; |
229 | 0 | } else { |
230 | 0 | if (!defaultseuser && |
231 | 0 | !strcmp(username, "__default__")) { |
232 | 0 | defaultseuser = seuser; |
233 | 0 | defaultlevel = level; |
234 | 0 | } else { |
235 | 0 | free(seuser); |
236 | 0 | free(level); |
237 | 0 | } |
238 | 0 | } |
239 | 0 | free(username); |
240 | 0 | username = NULL; |
241 | 0 | seuser = NULL; |
242 | 0 | } |
243 | |
|
244 | 0 | free(buffer); |
245 | 0 | fclose(cfg); |
246 | |
|
247 | 0 | if (seuser) { |
248 | 0 | free(username); |
249 | 0 | free(defaultseuser); |
250 | 0 | free(defaultlevel); |
251 | 0 | free(groupseuser); |
252 | 0 | free(grouplevel); |
253 | 0 | *r_seuser = seuser; |
254 | 0 | *r_level = level; |
255 | 0 | return 0; |
256 | 0 | } |
257 | | |
258 | 0 | if (groupseuser) { |
259 | 0 | free(defaultseuser); |
260 | 0 | free(defaultlevel); |
261 | 0 | *r_seuser = groupseuser; |
262 | 0 | *r_level = grouplevel; |
263 | 0 | return 0; |
264 | 0 | } |
265 | | |
266 | 0 | if (defaultseuser) { |
267 | 0 | *r_seuser = defaultseuser; |
268 | 0 | *r_level = defaultlevel; |
269 | 0 | return 0; |
270 | 0 | } |
271 | | |
272 | 0 | nomatch: |
273 | 0 | if (require_seusers) |
274 | 0 | return -1; |
275 | | |
276 | | /* Fall back to the Linux username and no level. */ |
277 | 0 | *r_seuser = strdup(name); |
278 | 0 | if (!(*r_seuser)) |
279 | 0 | return -1; |
280 | 0 | *r_level = NULL; |
281 | 0 | return 0; |
282 | 0 | } |
283 | | |
284 | | int getseuser(const char *username, const char *service, char **r_seuser, |
285 | | char **r_level) |
286 | 0 | { |
287 | 0 | int ret = -1; |
288 | 0 | int len = 0; |
289 | 0 | char *seuser = NULL; |
290 | 0 | char *level = NULL; |
291 | 0 | char *buffer = NULL; |
292 | 0 | size_t size = 0; |
293 | 0 | char *rec = NULL; |
294 | 0 | char *path = NULL; |
295 | 0 | FILE *fp = NULL; |
296 | 0 | if (asprintf(&path, "%s/logins/%s", selinux_policy_root(), username) < |
297 | 0 | 0) |
298 | 0 | goto err; |
299 | 0 | fp = fopen(path, "re"); |
300 | 0 | free(path); |
301 | 0 | if (fp == NULL) |
302 | 0 | goto err; |
303 | 0 | __fsetlocking(fp, FSETLOCKING_BYCALLER); |
304 | 0 | while (getline(&buffer, &size, fp) > 0) { |
305 | 0 | if (strncmp(buffer, "*:", 2) == 0) { |
306 | 0 | free(rec); |
307 | 0 | rec = strdup(buffer); |
308 | 0 | if (!rec) |
309 | 0 | goto err; |
310 | 0 | continue; |
311 | 0 | } |
312 | 0 | if (!service) |
313 | 0 | continue; |
314 | 0 | len = strlen(service); |
315 | 0 | if ((strncmp(buffer, service, len) == 0) && |
316 | 0 | (buffer[len] == ':')) { |
317 | 0 | free(rec); |
318 | 0 | rec = strdup(buffer); |
319 | 0 | if (!rec) |
320 | 0 | goto err; |
321 | 0 | break; |
322 | 0 | } |
323 | 0 | } |
324 | | |
325 | 0 | if (!rec) |
326 | 0 | goto err; |
327 | 0 | seuser = strchr(rec, ':'); |
328 | 0 | if (!seuser) |
329 | 0 | goto err; |
330 | | |
331 | 0 | seuser++; |
332 | 0 | level = strchr(seuser, ':'); |
333 | 0 | if (!level) |
334 | 0 | goto err; |
335 | 0 | *level = 0; |
336 | 0 | level++; |
337 | 0 | *r_seuser = strdup(seuser); |
338 | 0 | if (!*r_seuser) |
339 | 0 | goto err; |
340 | | |
341 | 0 | len = strlen(level); |
342 | 0 | if (len && level[len - 1] == '\n') |
343 | 0 | level[len - 1] = 0; |
344 | |
|
345 | 0 | *r_level = strdup(level); |
346 | 0 | if (!*r_level) { |
347 | 0 | free(*r_seuser); |
348 | 0 | goto err; |
349 | 0 | } |
350 | 0 | ret = 0; |
351 | |
|
352 | 0 | err: |
353 | 0 | free(buffer); |
354 | 0 | if (fp) |
355 | 0 | fclose(fp); |
356 | 0 | free(rec); |
357 | |
|
358 | 0 | return (ret ? getseuserbyname(username, r_seuser, r_level) : ret); |
359 | 0 | } |