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 hidden = 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(), "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 
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, "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