seusers.c revision 13cd4c8960688af11ad23b4c946149015c80d549
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 <selinux/selinux.h>
9#include <selinux/context.h>
10#include "selinux_internal.h"
11
12/* Process line from seusers.conf and split into its fields.
13   Returns 0 on success, -1 on comments, and -2 on error. */
14static int process_seusers(const char *buffer,
15			   char **luserp,
16			   char **seuserp, char **levelp, int mls_enabled)
17{
18	char *newbuf = strdup(buffer);
19	char *luser = NULL, *seuser = NULL, *level = NULL;
20	char *start, *end;
21	int mls_found = 1;
22
23	if (!newbuf)
24		goto err;
25
26	start = newbuf;
27	while (isspace(*start))
28		start++;
29	if (*start == '#' || *start == 0) {
30		free(newbuf);
31		return -1;	/* Comment or empty line, skip over */
32	}
33	end = strchr(start, ':');
34	if (!end)
35		goto err;
36	*end = 0;
37
38	luser = strdup(start);
39	if (!luser)
40		goto err;
41
42	start = end + 1;
43	end = strchr(start, ':');
44	if (!end) {
45		mls_found = 0;
46
47		end = start;
48		while (*end && !isspace(*end))
49			end++;
50	}
51	*end = 0;
52
53	seuser = strdup(start);
54	if (!seuser)
55		goto err;
56
57	if (!strcmp(seuser, ""))
58		goto err;
59
60	/* Skip MLS if disabled, or missing. */
61	if (!mls_enabled || !mls_found)
62		goto out;
63
64	start = ++end;
65	while (*end && !isspace(*end))
66		end++;
67	*end = 0;
68
69	level = strdup(start);
70	if (!level)
71		goto err;
72
73	if (!strcmp(level, ""))
74		goto err;
75
76      out:
77	free(newbuf);
78	*luserp = luser;
79	*seuserp = seuser;
80	*levelp = level;
81	return 0;
82      err:
83	free(newbuf);
84	free(luser);
85	free(seuser);
86	free(level);
87	return -2;		/* error */
88}
89
90int require_seusers hidden = 0;
91
92#include <pwd.h>
93#include <grp.h>
94
95static gid_t get_default_gid(const char *name) {
96	struct passwd pwstorage, *pwent = NULL;
97	gid_t gid = -1;
98	/* Allocate space for the getpwnam_r buffer */
99	long rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
100	if (rbuflen <= 0) return -1;
101	char *rbuf = malloc(rbuflen);
102	if (rbuf == NULL) return -1;
103
104	int retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
105	if (retval == 0 && pwent) {
106		gid = pwent->pw_gid;
107	}
108	free(rbuf);
109	return gid;
110}
111
112static int check_group(const char *group, const char *name, const gid_t gid) {
113	int match = 0;
114	int i, ng = 0;
115	gid_t *groups = NULL;
116	struct group gbuf, *grent = NULL;
117
118	long rbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
119	if (rbuflen <= 0)
120		return 0;
121	char *rbuf = malloc(rbuflen);
122	if (rbuf == NULL)
123		return 0;
124
125	if (getgrnam_r(group, &gbuf, rbuf, rbuflen,
126		       &grent) != 0)
127		goto done;
128
129	if (getgrouplist(name, gid, NULL, &ng) < 0) {
130		groups = (gid_t *) malloc(sizeof (gid_t) * ng);
131		if (!groups) goto done;
132		if (getgrouplist(name, gid, groups, &ng) < 0) goto done;
133	}
134
135	for (i = 0; i < ng; i++) {
136		if (grent->gr_gid == groups[i]) {
137			match = 1;
138			goto done;
139		}
140	}
141
142 done:
143	free(groups);
144	free(rbuf);
145	return match;
146}
147
148int getseuserbyname(const char *name, char **r_seuser, char **r_level)
149{
150	FILE *cfg = NULL;
151	size_t size = 0;
152	char *buffer = NULL;
153	int rc;
154	unsigned long lineno = 0;
155	int mls_enabled = is_selinux_mls_enabled();
156
157	char *username = NULL;
158	char *seuser = NULL;
159	char *level = NULL;
160	char *groupseuser = NULL;
161	char *grouplevel = NULL;
162	char *defaultseuser = NULL;
163	char *defaultlevel = NULL;
164
165	gid_t gid = get_default_gid(name);
166
167	cfg = fopen(selinux_usersconf_path(), "r");
168	if (!cfg)
169		goto nomatch;
170
171	__fsetlocking(cfg, FSETLOCKING_BYCALLER);
172	while (getline(&buffer, &size, cfg) > 0) {
173		++lineno;
174		rc = process_seusers(buffer, &username, &seuser, &level,
175				     mls_enabled);
176		if (rc == -1)
177			continue;	/* comment, skip */
178		if (rc == -2) {
179			fprintf(stderr, "%s:  error on line %lu, skipping...\n",
180				selinux_usersconf_path(), lineno);
181			continue;
182		}
183
184		if (!strcmp(username, name))
185			break;
186
187		if (username[0] == '%' &&
188		    !groupseuser &&
189		    check_group(&username[1], name, gid)) {
190				groupseuser = seuser;
191				grouplevel = level;
192		} else {
193			if (!defaultseuser &&
194			    !strcmp(username, "__default__")) {
195				defaultseuser = seuser;
196				defaultlevel = level;
197			} else {
198				free(seuser);
199				free(level);
200			}
201		}
202		free(username);
203		username = NULL;
204		seuser = NULL;
205	}
206
207	free(buffer);
208	fclose(cfg);
209
210	if (seuser) {
211		free(username);
212		free(defaultseuser);
213		free(defaultlevel);
214		free(groupseuser);
215		free(grouplevel);
216		*r_seuser = seuser;
217		*r_level = level;
218		return 0;
219	}
220
221	if (groupseuser) {
222		free(defaultseuser);
223		free(defaultlevel);
224		*r_seuser = groupseuser;
225		*r_level = grouplevel;
226		return 0;
227	}
228
229	if (defaultseuser) {
230		*r_seuser = defaultseuser;
231		*r_level = defaultlevel;
232		return 0;
233	}
234
235      nomatch:
236	if (require_seusers)
237		return -1;
238
239	/* Fall back to the Linux username and no level. */
240	*r_seuser = strdup(name);
241	if (!(*r_seuser))
242		return -1;
243	*r_level = NULL;
244	return 0;
245}
246