#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <ctype.h>
#include <errno.h>
#include <selinux/selinux.h>
#include <selinux/context.h>
#include "selinux_internal.h"
/* Process line from seusers.conf and split into its fields.
Returns 0 on success, -1 on comments, and -2 on error. */
static int process_seusers(const char *buffer,
char **luserp,
char **seuserp, char **levelp, int mls_enabled)
{
char *newbuf = strdup(buffer);
char *luser = NULL, *seuser = NULL, *level = NULL;
char *start, *end;
int mls_found = 1;
if (!newbuf)
goto err;
start = newbuf;
while (isspace(*start))
start++;
if (*start == '#' || *start == 0) {
free(newbuf);
return -1; /* Comment or empty line, skip over */
}
end = strchr(start, ':');
if (!end)
goto err;
*end = 0;
luser = strdup(start);
if (!luser)
goto err;
start = end + 1;
end = strchr(start, ':');
if (!end) {
mls_found = 0;
end = start;
while (*end && !isspace(*end))
end++;
}
*end = 0;
seuser = strdup(start);
if (!seuser)
goto err;
if (!strcmp(seuser, ""))
goto err;
/* Skip MLS if disabled, or missing. */
if (!mls_enabled || !mls_found)
goto out;
start = ++end;
while (*end && !isspace(*end))
end++;
*end = 0;
level = strdup(start);
if (!level)
goto err;
if (!strcmp(level, ""))
goto err;
out:
free(newbuf);
*luserp = luser;
*seuserp = seuser;
*levelp = level;
return 0;
err:
free(newbuf);
free(luser);
free(seuser);
free(level);
return -2; /* error */
}
int require_seusers hidden = 0;
#include <pwd.h>
#include <grp.h>
static gid_t get_default_gid(const char *name) {
struct passwd pwstorage, *pwent = NULL;
gid_t gid = -1;
/* Allocate space for the getpwnam_r buffer */
long rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
if (rbuflen <= 0) return -1;
char *rbuf = malloc(rbuflen);
if (rbuf == NULL) return -1;
int retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
if (retval == 0 && pwent) {
gid = pwent->pw_gid;
}
free(rbuf);
return gid;
}
static int check_group(const char *group, const char *name, const gid_t gid) {
int match = 0;
int i, ng = 0;
gid_t *groups = NULL;
struct group gbuf, *grent = NULL;
long rbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
if (rbuflen <= 0)
return 0;
char *rbuf;
while(1) {
rbuf = malloc(rbuflen);
if (rbuf == NULL)
return 0;
int retval = getgrnam_r(group, &gbuf, rbuf,
rbuflen, &grent);
if ( retval == ERANGE )
{
free(rbuf);
rbuflen = rbuflen * 2;
} else if ( retval != 0 || grent == NULL )
{
goto done;
} else
{
break;
}
}
if (getgrouplist(name, gid, NULL, &ng) < 0) {
if (ng == 0)
goto done;
groups = calloc(ng, sizeof(*groups));
if (!groups)
goto done;
if (getgrouplist(name, gid, groups, &ng) < 0)
goto done;
} else {
/* WTF? ng was 0 and we didn't fail? Are we in 0 groups? */
goto done;
}
for (i = 0; i < ng; i++) {
if (grent->gr_gid == groups[i]) {
match = 1;
goto done;
}
}
done:
free(groups);
free(rbuf);
return match;
}
int getseuserbyname(const char *name, char **r_seuser, char **r_level)
{
FILE *cfg = NULL;
size_t size = 0;
char *buffer = NULL;
int rc;
unsigned long lineno = 0;
int mls_enabled = is_selinux_mls_enabled();
char *username = NULL;
char *seuser = NULL;
char *level = NULL;
char *groupseuser = NULL;
char *grouplevel = NULL;
char *defaultseuser = NULL;
char *defaultlevel = NULL;
gid_t gid = get_default_gid(name);
cfg = fopen(selinux_usersconf_path(), "re");
if (!cfg)
goto nomatch;
__fsetlocking(cfg, FSETLOCKING_BYCALLER);
while (getline(&buffer, &size, cfg) > 0) {
++lineno;
rc = process_seusers(buffer, &username, &seuser, &level,
mls_enabled);
if (rc == -1)
continue; /* comment, skip */
if (rc == -2) {
fprintf(stderr, "%s: error on line %lu, skipping...\n",
selinux_usersconf_path(), lineno);
continue;
}
if (!strcmp(username, name))
break;
if (username[0] == '%' &&
!groupseuser &&
check_group(&username[1], name, gid)) {
groupseuser = seuser;
grouplevel = level;
} else {
if (!defaultseuser &&
!strcmp(username, "__default__")) {
defaultseuser = seuser;
defaultlevel = level;
} else {
free(seuser);
free(level);
}
}
free(username);
username = NULL;
seuser = NULL;
}
free(buffer);
fclose(cfg);
if (seuser) {
free(username);
free(defaultseuser);
free(defaultlevel);
free(groupseuser);
free(grouplevel);
*r_seuser = seuser;
*r_level = level;
return 0;
}
if (groupseuser) {
free(defaultseuser);
free(defaultlevel);
*r_seuser = groupseuser;
*r_level = grouplevel;
return 0;
}
if (defaultseuser) {
*r_seuser = defaultseuser;
*r_level = defaultlevel;
return 0;
}
nomatch:
if (require_seusers)
return -1;
/* Fall back to the Linux username and no level. */
*r_seuser = strdup(name);
if (!(*r_seuser))
return -1;
*r_level = NULL;
return 0;
}
int getseuser(const char *username, const char *service,
char **r_seuser, char **r_level) {
int ret = -1;
int len = 0;
char *seuser = NULL;
char *level = NULL;
char *buffer = NULL;
size_t size = 0;
char *rec = NULL;
char *path=NULL;
FILE *fp = NULL;
if (asprintf(&path,"%s/logins/%s", selinux_policy_root(), username) < 0)
goto err;
fp = fopen(path, "re");
free(path);
if (fp == NULL) goto err;
__fsetlocking(fp, FSETLOCKING_BYCALLER);
while (getline(&buffer, &size, fp) > 0) {
if (strncmp(buffer, "*:", 2) == 0) {
free(rec);
rec = strdup(buffer);
continue;
}
if (!service)
continue;
len = strlen(service);
if ((strncmp(buffer, service, len) == 0) &&
(buffer[len] == ':')) {
free(rec);
rec = strdup(buffer);
break;
}
}
if (! rec) goto err;
seuser = strchr(rec, ':');
if (! seuser) goto err;
seuser++;
level = strchr(seuser, ':');
if (! level) goto err;
*level = 0;
level++;
*r_seuser = strdup(seuser);
if (! *r_seuser) goto err;
len = strlen(level);
if (len && level[len-1] == '\n')
level[len-1] = 0;
*r_level = strdup(level);
if (! *r_level) {
free(*r_seuser);
goto err;
}
ret = 0;
err:
free(buffer);
if (fp) fclose(fp);
free(rec);
return (ret ? getseuserbyname(username, r_seuser, r_level) : ret);
}