#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);
|
}
|