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