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