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