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. */
15static 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
91int require_seusers hidden = 0;
92
93#include <pwd.h>
94#include <grp.h>
95
96static 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
113static 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
169int 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(), "r");
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
268int 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, "r");
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