On Wed, Jul 27, 2016 at 10:25:06PM +0100, Gary Tierney wrote: > semanage-login supports login mappings using the %group syntax, but > genhomedircon does not expand groups to the users belonging to them. > > This commit adds support for generating home directory contexts for login > mappings using the group syntax and adds error reporting for handling cases > where there is ambiguity due to a user belonging to multiple groups mapped by > semanage-login. If a login mapping is added for the user which belongs to > multiple groups it will take precedence and resolve the ambiguity issue. > > Signed-off-by: Gary Tierney <gary.tierney@xxxxxxx> Thanks for this, it was on my todo list so glad you did it for me :D. Some *super* minor issues below. And feel free to add: Reviewed-By: Jason Zaman <jason@xxxxxxxxxxxxx> > --- > libsemanage/src/genhomedircon.c | 319 +++++++++++++++++++++++++++++++--------- > 1 file changed, 247 insertions(+), 72 deletions(-) > > diff --git a/libsemanage/src/genhomedircon.c b/libsemanage/src/genhomedircon.c > index c5ea436..2955b19 100644 > --- a/libsemanage/src/genhomedircon.c > +++ b/libsemanage/src/genhomedircon.c > @@ -48,6 +48,8 @@ > #include <errno.h> > #include <unistd.h> > #include <regex.h> > +#include <grp.h> > +#include <search.h> > > /* paths used in get_home_dirs() */ > #define PATH_ETC_USERADD "/etc/default/useradd" > @@ -98,6 +100,10 @@ typedef struct user_entry { > char *prefix; > char *home; > char *level; > + > + // The login identifier that was used > + // in semanage-login / seusers the comment makes the struct annoying to read. its obvious enough without it. > + char *login; > struct user_entry *next; > } genhomedircon_user_entry_t; > > @@ -486,6 +492,11 @@ static int USER_CONTEXT_PRED(const char *string) > return (int)(strstr(string, TEMPLATE_USER) != NULL); > } > > +static int STR_COMPARATOR(const void *a, const void *b) > +{ > + return strcmp((const char *) a, (const char *) b); > +} > + > /* make_tempate > * @param s the settings holding the paths to various files > * @param pred function pointer to function to use as filter for slurp > @@ -652,6 +663,24 @@ static int write_user_context(genhomedircon_settings_t * s, FILE * out, > return write_replacements(s, out, tpl, repl); > } > > +static int seuser_sort_func(const void *arg1, const void *arg2) > +{ > + const semanage_seuser_t **u1 = (const semanage_seuser_t **) arg1; > + const semanage_seuser_t **u2 = (const semanage_seuser_t **) arg2;; > + const char *name1 = semanage_seuser_get_name(*u1); > + const char *name2 = semanage_seuser_get_name(*u2); > + > + if (name1[0] == '%' && name2[0] == '%') { > + return 0; > + } else if (name1[0] == '%') { > + return 1; > + } else if (name2[0] == '%') { > + return -1; > + } > + > + return strcmp(name1, name2); > +} > + > static int user_sort_func(semanage_user_t ** arg1, semanage_user_t ** arg2) > { > return strcmp(semanage_user_get_name(*arg1), > @@ -665,7 +694,8 @@ static int name_user_cmp(char *key, semanage_user_t ** val) > > static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n, > const char *u, const char *g, const char *sen, > - const char *pre, const char *h, const char *l) > + const char *pre, const char *h, const char *l, > + const char *ln) > { > genhomedircon_user_entry_t *temp = NULL; > char *name = NULL; > @@ -675,6 +705,7 @@ static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n, > char *prefix = NULL; > char *home = NULL; > char *level = NULL; > + char *lname = NULL; > > temp = malloc(sizeof(genhomedircon_user_entry_t)); > if (!temp) > @@ -700,6 +731,9 @@ static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n, > level = strdup(l); > if (!level) > goto cleanup; > + lname = strdup(ln); > + if (!lname) > + goto cleanup; > > temp->name = name; > temp->uid = uid; > @@ -708,6 +742,7 @@ static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n, > temp->prefix = prefix; > temp->home = home; > temp->level = level; > + temp->login = lname; > temp->next = (*list); > (*list) = temp; > > @@ -721,6 +756,7 @@ static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n, > free(prefix); > free(home); > free(level); > + free(lname); > free(temp); > return STATUS_ERR; > } > @@ -741,6 +777,7 @@ static void pop_user_entry(genhomedircon_user_entry_t ** list) > free(temp->prefix); > free(temp->home); > free(temp->level); > + free(temp->login); > free(temp); > } > > @@ -790,7 +827,8 @@ static int setup_fallback_user(genhomedircon_settings_t * s) > > if (push_user_entry(&(s->fallback), FALLBACK_NAME, > FALLBACK_UIDGID, FALLBACK_UIDGID, > - seuname, prefix, "", level) != 0) > + seuname, prefix, "", level, > + FALLBACK_NAME) != 0) > errors = STATUS_ERR; > semanage_user_key_free(key); > if (u) > @@ -806,6 +844,201 @@ static int setup_fallback_user(genhomedircon_settings_t * s) > return errors; > } > > +static genhomedircon_user_entry_t *find_user(genhomedircon_user_entry_t *head, > + const char *name) > +{ > + for(; head; head = head->next) { > + if (strcmp(head->name, name) == 0) { > + return head; > + } > + } > + > + return NULL; > +} > + > +static int add_user(genhomedircon_settings_t * s, > + genhomedircon_user_entry_t **head, > + semanage_user_t *user, > + const char *name, > + const char *sename, > + const char *selogin) > +{ > + if (selogin[0] == '%') { > + genhomedircon_user_entry_t *orig = find_user(*head, name); > + if (orig != NULL && orig->login[0] == '%') { > + ERR(s->h_semanage, "User %s is already mapped to" > + "group %s, but also belongs to group %s. Add an" > + "explicit mapping for this user to" > + "override group mappings.", > + name, orig->login + 1, selogin + 1); > + return STATUS_ERR; > + } else if (orig != NULL) { > + // user mappings take precedence > + return STATUS_SUCCESS; > + } > + } > + > + int retval = STATUS_ERR; > + > + char *rbuf = NULL; > + long rbuflen; > + struct passwd pwstorage, *pwent = NULL; > + const char *prefix = NULL; > + const char *level = NULL; > + char uid[11]; > + char gid[11]; > + > + /* Allocate space for the getpwnam_r buffer */ > + rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); > + if (rbuflen <= 0) > + goto cleanup; > + rbuf = malloc(rbuflen); > + if (rbuf == NULL) > + goto cleanup; > + > + if (user) { > + prefix = semanage_user_get_prefix(user); > + level = semanage_user_get_mlslevel(user); > + > + if (!level) { > + level = FALLBACK_LEVEL; > + } > + } else { > + prefix = name; > + level = FALLBACK_LEVEL; > + } > + > + retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent); > + if (retval != 0 || pwent == NULL) { > + if (retval != 0 && retval != ENOENT) { > + goto cleanup; > + } > + > + WARN(s->h_semanage, > + "user %s not in password file", name); > + retval = STATUS_SUCCESS; > + goto cleanup; > + } > + > + int len = strlen(pwent->pw_dir) -1; > + for(; len > 0 && pwent->pw_dir[len] == '/'; len--) { > + pwent->pw_dir[len] = '\0'; > + } > + > + if (strcmp(pwent->pw_dir, "/") == 0) { > + /* don't relabel / genhomdircon checked to see if root > + * was the user and if so, set his home directory to > + * /root */ > + retval = STATUS_SUCCESS; > + goto cleanup; > + } > + > + if (ignore(pwent->pw_dir)) { > + retval = STATUS_SUCCESS; > + goto cleanup; > + } > + > + len = snprintf(uid, sizeof(uid), "%u", pwent->pw_uid); > + if (len < 0 || len >= (int)sizeof(uid)) { > + goto cleanup; > + } > + > + len = snprintf(gid, sizeof(gid), "%u", pwent->pw_gid); > + if (len < 0 || len >= (int)sizeof(gid)) { > + goto cleanup; > + } > + > + retval = push_user_entry(head, name, uid, gid, sename, prefix, > + pwent->pw_dir, level, selogin); > +cleanup: > + free(rbuf); > + return retval; > +} > + > +static int get_group_users(genhomedircon_settings_t * s, > + genhomedircon_user_entry_t **head, > + semanage_user_t *user, > + const char *sename, > + const char *selogin) > +{ > + int retval = STATUS_ERR; > + > + long grbuflen; > + char *grbuf = NULL; > + struct group grstorage, *group = NULL; > + > + long prbuflen; > + char *pwbuf = NULL; > + struct passwd pwstorage, *pw = NULL; > + > + grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX); > + if (grbuflen <= 0) > + goto cleanup; > + grbuf = malloc(grbuflen); > + if (grbuf == NULL) > + goto cleanup; > + > + const char *grname = selogin + 1; > + > + if (getgrnam_r(grname, &grstorage, grbuf, > + (size_t) grbuflen, &group) != 0) { > + goto cleanup; > + } > + > + if (group == NULL) { > + ERR(s->h_semanage, "Can't find group named %s\n", grname); > + goto cleanup; > + } > + > + size_t nmembers = 0; > + char **members = group->gr_mem; > + > + while (*members != NULL) { > + nmembers++; > + members++; > + } > + > + for (unsigned int i = 0; i < nmembers; i++) { My machine threw an error with this, move the unsigned int i to the top of the func instead. genhomedircon.c: In function ‘get_group_users’: genhomedircon.c:1001:2: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode > + const char *uname = group->gr_mem[i]; > + > + if (add_user(s, head, user, uname, sename, selogin) < 0) { > + goto cleanup; > + } > + } > + > + prbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); > + if (prbuflen <= 0) > + goto cleanup; > + pwbuf = malloc(prbuflen); > + if (pwbuf == NULL) > + goto cleanup; > + > + setpwent(); > + while ((retval = getpwent_r(&pwstorage, pwbuf, prbuflen, &pw)) == 0) { > + // skip users who also have this group as their > + // primary group > + if (lfind(pw->pw_name, group->gr_mem, &nmembers, > + sizeof(char *), &STR_COMPARATOR)) { > + continue; > + } > + > + if (group->gr_gid == pw->pw_gid) { > + if (add_user(s, head, user, pw->pw_name, > + sename, selogin) < 0) { > + goto cleanup; > + } > + } > + } > + > + retval = STATUS_SUCCESS; > +cleanup: > + endpwent(); > + free(pwbuf); > + free(grbuf); > + > + return retval; > +} > + > static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s, > int *errors) > { > @@ -817,14 +1050,7 @@ static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s, > semanage_user_t **u = NULL; > const char *name = NULL; > const char *seuname = NULL; > - const char *prefix = NULL; > - const char *level = NULL; > - char uid[11]; > - char gid[11]; > - struct passwd pwstorage, *pwent = NULL; > unsigned int i; > - long rbuflen; > - char *rbuf = NULL; > int retval; > > *errors = 0; > @@ -838,94 +1064,43 @@ static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s, > nusers = 0; > } > > + qsort(seuser_list, nseusers, sizeof(semanage_seuser_t *), > + &seuser_sort_func); > qsort(user_list, nusers, sizeof(semanage_user_t *), > (int (*)(const void *, const void *))&user_sort_func); > > - /* Allocate space for the getpwnam_r buffer */ > - rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); > - if (rbuflen <= 0) > - goto cleanup; > - rbuf = malloc(rbuflen); > - if (rbuf == NULL) > - goto cleanup; > - > for (i = 0; i < nseusers; i++) { > seuname = semanage_seuser_get_sename(seuser_list[i]); > name = semanage_seuser_get_name(seuser_list[i]); > > - if (strcmp(name,"root") && strcmp(seuname, s->fallback->sename) == 0) > - continue; > - > if (strcmp(name, DEFAULT_LOGIN) == 0) > continue; > > if (strcmp(name, TEMPLATE_SEUSER) == 0) > continue; > > - /* %groupname syntax */ > - if (name[0] == '%') > - continue; > - > /* find the user structure given the name */ > - u = bsearch(seuname, user_list, nusers, sizeof(semanage_user_t *), > + u = bsearch(seuname, user_list, nusers, > + sizeof(semanage_user_t *), Undo this one, its just code formatting and adds noise to the patch. > (int (*)(const void *, const void *)) > &name_user_cmp); > - if (u) { > - prefix = semanage_user_get_prefix(*u); > - level = semanage_user_get_mlslevel(*u); > - if (!level) > - level = FALLBACK_LEVEL; > - } else { > - prefix = name; > - level = FALLBACK_LEVEL; > - } > - > - retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent); > - if (retval != 0 || pwent == NULL) { > - if (retval != 0 && retval != ENOENT) { > - *errors = STATUS_ERR; > - goto cleanup; > - } > - > - WARN(s->h_semanage, > - "user %s not in password file", name); > - continue; > - } > > - int len = strlen(pwent->pw_dir) -1; > - for(; len > 0 && pwent->pw_dir[len] == '/'; len--) { > - pwent->pw_dir[len] = '\0'; > + /* %groupname syntax */ > + if (name[0] == '%') { > + retval = get_group_users(s, &head, *u, seuname, > + name); > + } else { > + retval = add_user(s, &head, *u, name, > + seuname, name); > } > > - if (strcmp(pwent->pw_dir, "/") == 0) { > - /* don't relabel / genhomdircon checked to see if root > - * was the user and if so, set his home directory to > - * /root */ > - continue; > - } > - if (ignore(pwent->pw_dir)) > - continue; > - > - len = snprintf(uid, sizeof(uid), "%u", pwent->pw_uid); > - if (len < 0 || len >= (int)sizeof(uid)) { > + if (retval != 0) { > *errors = STATUS_ERR; > goto cleanup; > } > - len = snprintf(gid, sizeof(gid), "%u", pwent->pw_gid); > - if (len < 0 || len >= (int)sizeof(gid)) { > - *errors = STATUS_ERR; > - goto cleanup; > - } > - > - if (push_user_entry(&head, name, uid, gid, seuname, > - prefix, pwent->pw_dir, level) != STATUS_SUCCESS) { > - *errors = STATUS_ERR; > - break; > - } > } > > cleanup: > - free(rbuf); > if (*errors) { > for (; head; pop_user_entry(&head)) { > /* the pop function takes care of all the cleanup > -- > 2.7.4 > > _______________________________________________ > Selinux mailing list > Selinux@xxxxxxxxxxxxx > To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. > To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx. _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.