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