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