16cd6a6acSopenharmony_ci#include <unistd.h>
26cd6a6acSopenharmony_ci#include <fcntl.h>
36cd6a6acSopenharmony_ci#include <stdlib.h>
46cd6a6acSopenharmony_ci#include <string.h>
56cd6a6acSopenharmony_ci#include <stdio.h>
66cd6a6acSopenharmony_ci#include <stdio_ext.h>
76cd6a6acSopenharmony_ci#include <ctype.h>
86cd6a6acSopenharmony_ci#include <errno.h>
96cd6a6acSopenharmony_ci#include <selinux/selinux.h>
106cd6a6acSopenharmony_ci#include <selinux/context.h>
116cd6a6acSopenharmony_ci#include "selinux_internal.h"
126cd6a6acSopenharmony_ci
136cd6a6acSopenharmony_ci/* Process line from seusers.conf and split into its fields.
146cd6a6acSopenharmony_ci   Returns 0 on success, -1 on comments, and -2 on error. */
156cd6a6acSopenharmony_cistatic int process_seusers(const char *buffer,
166cd6a6acSopenharmony_ci			   char **luserp,
176cd6a6acSopenharmony_ci			   char **seuserp, char **levelp, int mls_enabled)
186cd6a6acSopenharmony_ci{
196cd6a6acSopenharmony_ci	char *newbuf = strdup(buffer);
206cd6a6acSopenharmony_ci	char *luser = NULL, *seuser = NULL, *level = NULL;
216cd6a6acSopenharmony_ci	char *start, *end;
226cd6a6acSopenharmony_ci	int mls_found = 1;
236cd6a6acSopenharmony_ci
246cd6a6acSopenharmony_ci	if (!newbuf)
256cd6a6acSopenharmony_ci		goto err;
266cd6a6acSopenharmony_ci
276cd6a6acSopenharmony_ci	start = newbuf;
286cd6a6acSopenharmony_ci	while (isspace(*start))
296cd6a6acSopenharmony_ci		start++;
306cd6a6acSopenharmony_ci	if (*start == '#' || *start == 0) {
316cd6a6acSopenharmony_ci		free(newbuf);
326cd6a6acSopenharmony_ci		return -1;	/* Comment or empty line, skip over */
336cd6a6acSopenharmony_ci	}
346cd6a6acSopenharmony_ci	end = strchr(start, ':');
356cd6a6acSopenharmony_ci	if (!end)
366cd6a6acSopenharmony_ci		goto err;
376cd6a6acSopenharmony_ci	*end = 0;
386cd6a6acSopenharmony_ci
396cd6a6acSopenharmony_ci	luser = strdup(start);
406cd6a6acSopenharmony_ci	if (!luser)
416cd6a6acSopenharmony_ci		goto err;
426cd6a6acSopenharmony_ci
436cd6a6acSopenharmony_ci	start = end + 1;
446cd6a6acSopenharmony_ci	end = strchr(start, ':');
456cd6a6acSopenharmony_ci	if (!end) {
466cd6a6acSopenharmony_ci		mls_found = 0;
476cd6a6acSopenharmony_ci
486cd6a6acSopenharmony_ci		end = start;
496cd6a6acSopenharmony_ci		while (*end && !isspace(*end))
506cd6a6acSopenharmony_ci			end++;
516cd6a6acSopenharmony_ci	}
526cd6a6acSopenharmony_ci	*end = 0;
536cd6a6acSopenharmony_ci
546cd6a6acSopenharmony_ci	seuser = strdup(start);
556cd6a6acSopenharmony_ci	if (!seuser)
566cd6a6acSopenharmony_ci		goto err;
576cd6a6acSopenharmony_ci
586cd6a6acSopenharmony_ci	if (!strcmp(seuser, ""))
596cd6a6acSopenharmony_ci		goto err;
606cd6a6acSopenharmony_ci
616cd6a6acSopenharmony_ci	/* Skip MLS if disabled, or missing. */
626cd6a6acSopenharmony_ci	if (!mls_enabled || !mls_found)
636cd6a6acSopenharmony_ci		goto out;
646cd6a6acSopenharmony_ci
656cd6a6acSopenharmony_ci	start = ++end;
666cd6a6acSopenharmony_ci	while (*end && !isspace(*end))
676cd6a6acSopenharmony_ci		end++;
686cd6a6acSopenharmony_ci	*end = 0;
696cd6a6acSopenharmony_ci
706cd6a6acSopenharmony_ci	level = strdup(start);
716cd6a6acSopenharmony_ci	if (!level)
726cd6a6acSopenharmony_ci		goto err;
736cd6a6acSopenharmony_ci
746cd6a6acSopenharmony_ci	if (!strcmp(level, ""))
756cd6a6acSopenharmony_ci		goto err;
766cd6a6acSopenharmony_ci
776cd6a6acSopenharmony_ci      out:
786cd6a6acSopenharmony_ci	free(newbuf);
796cd6a6acSopenharmony_ci	*luserp = luser;
806cd6a6acSopenharmony_ci	*seuserp = seuser;
816cd6a6acSopenharmony_ci	*levelp = level;
826cd6a6acSopenharmony_ci	return 0;
836cd6a6acSopenharmony_ci      err:
846cd6a6acSopenharmony_ci	free(newbuf);
856cd6a6acSopenharmony_ci	free(luser);
866cd6a6acSopenharmony_ci	free(seuser);
876cd6a6acSopenharmony_ci	free(level);
886cd6a6acSopenharmony_ci	return -2;		/* error */
896cd6a6acSopenharmony_ci}
906cd6a6acSopenharmony_ci
916cd6a6acSopenharmony_ciint require_seusers  = 0;
926cd6a6acSopenharmony_ci
936cd6a6acSopenharmony_ci#include <pwd.h>
946cd6a6acSopenharmony_ci#include <grp.h>
956cd6a6acSopenharmony_ci
966cd6a6acSopenharmony_cistatic gid_t get_default_gid(const char *name) {
976cd6a6acSopenharmony_ci	struct passwd pwstorage, *pwent = NULL;
986cd6a6acSopenharmony_ci	gid_t gid = -1;
996cd6a6acSopenharmony_ci	/* Allocate space for the getpwnam_r buffer */
1006cd6a6acSopenharmony_ci	long rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
1016cd6a6acSopenharmony_ci	if (rbuflen <= 0) return -1;
1026cd6a6acSopenharmony_ci	char *rbuf = malloc(rbuflen);
1036cd6a6acSopenharmony_ci	if (rbuf == NULL) return -1;
1046cd6a6acSopenharmony_ci
1056cd6a6acSopenharmony_ci	int retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
1066cd6a6acSopenharmony_ci	if (retval == 0 && pwent) {
1076cd6a6acSopenharmony_ci		gid = pwent->pw_gid;
1086cd6a6acSopenharmony_ci	}
1096cd6a6acSopenharmony_ci	free(rbuf);
1106cd6a6acSopenharmony_ci	return gid;
1116cd6a6acSopenharmony_ci}
1126cd6a6acSopenharmony_ci
1136cd6a6acSopenharmony_cistatic int check_group(const char *group, const char *name, const gid_t gid) {
1146cd6a6acSopenharmony_ci	int match = 0;
1156cd6a6acSopenharmony_ci	int i, ng = 0;
1166cd6a6acSopenharmony_ci	gid_t *groups = NULL;
1176cd6a6acSopenharmony_ci	struct group gbuf, *grent = NULL;
1186cd6a6acSopenharmony_ci
1196cd6a6acSopenharmony_ci	long rbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
1206cd6a6acSopenharmony_ci	if (rbuflen <= 0)
1216cd6a6acSopenharmony_ci		return 0;
1226cd6a6acSopenharmony_ci	char *rbuf;
1236cd6a6acSopenharmony_ci
1246cd6a6acSopenharmony_ci	while(1) {
1256cd6a6acSopenharmony_ci		rbuf = malloc(rbuflen);
1266cd6a6acSopenharmony_ci		if (rbuf == NULL)
1276cd6a6acSopenharmony_ci			return 0;
1286cd6a6acSopenharmony_ci		int retval = getgrnam_r(group, &gbuf, rbuf,
1296cd6a6acSopenharmony_ci				rbuflen, &grent);
1306cd6a6acSopenharmony_ci		if ( retval == ERANGE )
1316cd6a6acSopenharmony_ci		{
1326cd6a6acSopenharmony_ci			free(rbuf);
1336cd6a6acSopenharmony_ci			rbuflen = rbuflen * 2;
1346cd6a6acSopenharmony_ci		} else if ( retval != 0 || grent == NULL )
1356cd6a6acSopenharmony_ci		{
1366cd6a6acSopenharmony_ci			goto done;
1376cd6a6acSopenharmony_ci		} else
1386cd6a6acSopenharmony_ci		{
1396cd6a6acSopenharmony_ci			break;
1406cd6a6acSopenharmony_ci		}
1416cd6a6acSopenharmony_ci	}
1426cd6a6acSopenharmony_ci
1436cd6a6acSopenharmony_ci	if (getgrouplist(name, gid, NULL, &ng) < 0) {
1446cd6a6acSopenharmony_ci		if (ng == 0)
1456cd6a6acSopenharmony_ci			goto done;
1466cd6a6acSopenharmony_ci		groups = calloc(ng, sizeof(*groups));
1476cd6a6acSopenharmony_ci		if (!groups)
1486cd6a6acSopenharmony_ci			goto done;
1496cd6a6acSopenharmony_ci		if (getgrouplist(name, gid, groups, &ng) < 0)
1506cd6a6acSopenharmony_ci			goto done;
1516cd6a6acSopenharmony_ci	} else {
1526cd6a6acSopenharmony_ci		/* WTF?  ng was 0 and we didn't fail? Are we in 0 groups? */
1536cd6a6acSopenharmony_ci		goto done;
1546cd6a6acSopenharmony_ci	}
1556cd6a6acSopenharmony_ci
1566cd6a6acSopenharmony_ci	for (i = 0; i < ng; i++) {
1576cd6a6acSopenharmony_ci		if (grent->gr_gid == groups[i]) {
1586cd6a6acSopenharmony_ci			match = 1;
1596cd6a6acSopenharmony_ci			goto done;
1606cd6a6acSopenharmony_ci		}
1616cd6a6acSopenharmony_ci	}
1626cd6a6acSopenharmony_ci
1636cd6a6acSopenharmony_ci done:
1646cd6a6acSopenharmony_ci	free(groups);
1656cd6a6acSopenharmony_ci	free(rbuf);
1666cd6a6acSopenharmony_ci	return match;
1676cd6a6acSopenharmony_ci}
1686cd6a6acSopenharmony_ci
1696cd6a6acSopenharmony_ciint getseuserbyname(const char *name, char **r_seuser, char **r_level)
1706cd6a6acSopenharmony_ci{
1716cd6a6acSopenharmony_ci	FILE *cfg = NULL;
1726cd6a6acSopenharmony_ci	size_t size = 0;
1736cd6a6acSopenharmony_ci	char *buffer = NULL;
1746cd6a6acSopenharmony_ci	int rc;
1756cd6a6acSopenharmony_ci	unsigned long lineno = 0;
1766cd6a6acSopenharmony_ci	int mls_enabled = is_selinux_mls_enabled();
1776cd6a6acSopenharmony_ci
1786cd6a6acSopenharmony_ci	char *username = NULL;
1796cd6a6acSopenharmony_ci	char *seuser = NULL;
1806cd6a6acSopenharmony_ci	char *level = NULL;
1816cd6a6acSopenharmony_ci	char *groupseuser = NULL;
1826cd6a6acSopenharmony_ci	char *grouplevel = NULL;
1836cd6a6acSopenharmony_ci	char *defaultseuser = NULL;
1846cd6a6acSopenharmony_ci	char *defaultlevel = NULL;
1856cd6a6acSopenharmony_ci
1866cd6a6acSopenharmony_ci	gid_t gid = get_default_gid(name);
1876cd6a6acSopenharmony_ci
1886cd6a6acSopenharmony_ci	cfg = fopen(selinux_usersconf_path(), "re");
1896cd6a6acSopenharmony_ci	if (!cfg)
1906cd6a6acSopenharmony_ci		goto nomatch;
1916cd6a6acSopenharmony_ci
1926cd6a6acSopenharmony_ci	__fsetlocking(cfg, FSETLOCKING_BYCALLER);
1936cd6a6acSopenharmony_ci	while (getline(&buffer, &size, cfg) > 0) {
1946cd6a6acSopenharmony_ci		++lineno;
1956cd6a6acSopenharmony_ci		rc = process_seusers(buffer, &username, &seuser, &level,
1966cd6a6acSopenharmony_ci				     mls_enabled);
1976cd6a6acSopenharmony_ci		if (rc == -1)
1986cd6a6acSopenharmony_ci			continue;	/* comment, skip */
1996cd6a6acSopenharmony_ci		if (rc == -2) {
2006cd6a6acSopenharmony_ci			fprintf(stderr, "%s:  error on line %lu, skipping...\n",
2016cd6a6acSopenharmony_ci				selinux_usersconf_path(), lineno);
2026cd6a6acSopenharmony_ci			continue;
2036cd6a6acSopenharmony_ci		}
2046cd6a6acSopenharmony_ci
2056cd6a6acSopenharmony_ci		if (!strcmp(username, name))
2066cd6a6acSopenharmony_ci			break;
2076cd6a6acSopenharmony_ci
2086cd6a6acSopenharmony_ci		if (username[0] == '%' &&
2096cd6a6acSopenharmony_ci		    !groupseuser &&
2106cd6a6acSopenharmony_ci		    check_group(&username[1], name, gid)) {
2116cd6a6acSopenharmony_ci				groupseuser = seuser;
2126cd6a6acSopenharmony_ci				grouplevel = level;
2136cd6a6acSopenharmony_ci		} else {
2146cd6a6acSopenharmony_ci			if (!defaultseuser &&
2156cd6a6acSopenharmony_ci			    !strcmp(username, "__default__")) {
2166cd6a6acSopenharmony_ci				defaultseuser = seuser;
2176cd6a6acSopenharmony_ci				defaultlevel = level;
2186cd6a6acSopenharmony_ci			} else {
2196cd6a6acSopenharmony_ci				free(seuser);
2206cd6a6acSopenharmony_ci				free(level);
2216cd6a6acSopenharmony_ci			}
2226cd6a6acSopenharmony_ci		}
2236cd6a6acSopenharmony_ci		free(username);
2246cd6a6acSopenharmony_ci		username = NULL;
2256cd6a6acSopenharmony_ci		seuser = NULL;
2266cd6a6acSopenharmony_ci	}
2276cd6a6acSopenharmony_ci
2286cd6a6acSopenharmony_ci	free(buffer);
2296cd6a6acSopenharmony_ci	fclose(cfg);
2306cd6a6acSopenharmony_ci
2316cd6a6acSopenharmony_ci	if (seuser) {
2326cd6a6acSopenharmony_ci		free(username);
2336cd6a6acSopenharmony_ci		free(defaultseuser);
2346cd6a6acSopenharmony_ci		free(defaultlevel);
2356cd6a6acSopenharmony_ci		free(groupseuser);
2366cd6a6acSopenharmony_ci		free(grouplevel);
2376cd6a6acSopenharmony_ci		*r_seuser = seuser;
2386cd6a6acSopenharmony_ci		*r_level = level;
2396cd6a6acSopenharmony_ci		return 0;
2406cd6a6acSopenharmony_ci	}
2416cd6a6acSopenharmony_ci
2426cd6a6acSopenharmony_ci	if (groupseuser) {
2436cd6a6acSopenharmony_ci		free(defaultseuser);
2446cd6a6acSopenharmony_ci		free(defaultlevel);
2456cd6a6acSopenharmony_ci		*r_seuser = groupseuser;
2466cd6a6acSopenharmony_ci		*r_level = grouplevel;
2476cd6a6acSopenharmony_ci		return 0;
2486cd6a6acSopenharmony_ci	}
2496cd6a6acSopenharmony_ci
2506cd6a6acSopenharmony_ci	if (defaultseuser) {
2516cd6a6acSopenharmony_ci		*r_seuser = defaultseuser;
2526cd6a6acSopenharmony_ci		*r_level = defaultlevel;
2536cd6a6acSopenharmony_ci		return 0;
2546cd6a6acSopenharmony_ci	}
2556cd6a6acSopenharmony_ci
2566cd6a6acSopenharmony_ci      nomatch:
2576cd6a6acSopenharmony_ci	if (require_seusers)
2586cd6a6acSopenharmony_ci		return -1;
2596cd6a6acSopenharmony_ci
2606cd6a6acSopenharmony_ci	/* Fall back to the Linux username and no level. */
2616cd6a6acSopenharmony_ci	*r_seuser = strdup(name);
2626cd6a6acSopenharmony_ci	if (!(*r_seuser))
2636cd6a6acSopenharmony_ci		return -1;
2646cd6a6acSopenharmony_ci	*r_level = NULL;
2656cd6a6acSopenharmony_ci	return 0;
2666cd6a6acSopenharmony_ci}
2676cd6a6acSopenharmony_ci
2686cd6a6acSopenharmony_ciint getseuser(const char *username, const char *service,
2696cd6a6acSopenharmony_ci	      char **r_seuser, char **r_level) {
2706cd6a6acSopenharmony_ci	int ret = -1;
2716cd6a6acSopenharmony_ci	int len = 0;
2726cd6a6acSopenharmony_ci	char *seuser = NULL;
2736cd6a6acSopenharmony_ci	char *level = NULL;
2746cd6a6acSopenharmony_ci	char *buffer = NULL;
2756cd6a6acSopenharmony_ci	size_t size = 0;
2766cd6a6acSopenharmony_ci	char *rec = NULL;
2776cd6a6acSopenharmony_ci	char *path = NULL;
2786cd6a6acSopenharmony_ci	FILE *fp = NULL;
2796cd6a6acSopenharmony_ci	if (asprintf(&path,"%s/logins/%s", selinux_policy_root(), username) <  0)
2806cd6a6acSopenharmony_ci		goto err;
2816cd6a6acSopenharmony_ci	fp = fopen(path, "re");
2826cd6a6acSopenharmony_ci	free(path);
2836cd6a6acSopenharmony_ci	if (fp == NULL) goto err;
2846cd6a6acSopenharmony_ci	__fsetlocking(fp, FSETLOCKING_BYCALLER);
2856cd6a6acSopenharmony_ci	while (getline(&buffer, &size, fp) > 0) {
2866cd6a6acSopenharmony_ci		if (strncmp(buffer, "*:", 2) == 0) {
2876cd6a6acSopenharmony_ci			free(rec);
2886cd6a6acSopenharmony_ci			rec = strdup(buffer);
2896cd6a6acSopenharmony_ci			continue;
2906cd6a6acSopenharmony_ci		}
2916cd6a6acSopenharmony_ci		if (!service)
2926cd6a6acSopenharmony_ci			continue;
2936cd6a6acSopenharmony_ci		len = strlen(service);
2946cd6a6acSopenharmony_ci		if ((strncmp(buffer, service, len) == 0) &&
2956cd6a6acSopenharmony_ci		    (buffer[len] == ':')) {
2966cd6a6acSopenharmony_ci			free(rec);
2976cd6a6acSopenharmony_ci			rec = strdup(buffer);
2986cd6a6acSopenharmony_ci			break;
2996cd6a6acSopenharmony_ci		}
3006cd6a6acSopenharmony_ci	}
3016cd6a6acSopenharmony_ci
3026cd6a6acSopenharmony_ci	if (! rec)  goto err;
3036cd6a6acSopenharmony_ci	seuser = strchr(rec, ':');
3046cd6a6acSopenharmony_ci	if (! seuser) goto err;
3056cd6a6acSopenharmony_ci
3066cd6a6acSopenharmony_ci	seuser++;
3076cd6a6acSopenharmony_ci	level = strchr(seuser, ':');
3086cd6a6acSopenharmony_ci	if (! level) goto err;
3096cd6a6acSopenharmony_ci	*level = 0;
3106cd6a6acSopenharmony_ci	level++;
3116cd6a6acSopenharmony_ci	*r_seuser = strdup(seuser);
3126cd6a6acSopenharmony_ci	if (! *r_seuser) goto err;
3136cd6a6acSopenharmony_ci
3146cd6a6acSopenharmony_ci	len = strlen(level);
3156cd6a6acSopenharmony_ci	if (len && level[len-1] == '\n')
3166cd6a6acSopenharmony_ci		level[len-1] = 0;
3176cd6a6acSopenharmony_ci
3186cd6a6acSopenharmony_ci	*r_level = strdup(level);
3196cd6a6acSopenharmony_ci	if (! *r_level) {
3206cd6a6acSopenharmony_ci		free(*r_seuser);
3216cd6a6acSopenharmony_ci		goto err;
3226cd6a6acSopenharmony_ci	}
3236cd6a6acSopenharmony_ci	ret = 0;
3246cd6a6acSopenharmony_ci
3256cd6a6acSopenharmony_ci	err:
3266cd6a6acSopenharmony_ci	free(buffer);
3276cd6a6acSopenharmony_ci	if (fp) fclose(fp);
3286cd6a6acSopenharmony_ci	free(rec);
3296cd6a6acSopenharmony_ci
3306cd6a6acSopenharmony_ci	return (ret ? getseuserbyname(username, r_seuser, r_level) : ret);
3316cd6a6acSopenharmony_ci}
332