xref: /aosp_15_r20/external/selinux/libselinux/src/seusers.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
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