]> andersk Git - nss_nonlocal.git/blobdiff - nonlocal-group.c
Guard one-time initialization with memory barriers
[nss_nonlocal.git] / nonlocal-group.c
index fa7fbaf5c2400ae8d4efad6f0a3868afd6ce4d87..9bbe156183ccb6db5bc2e325eb2641e866a0d1d6 100644 (file)
  */
 
 #define _GNU_SOURCE
+
 #include <sys/types.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
 #include <dlfcn.h>
-#include <stdio.h>
-#include <syslog.h>
 #include <errno.h>
 #include <grp.h>
 #include <nss.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
 #include "nsswitch-internal.h"
 #include "nonlocal.h"
 
+/*
+ * If the MAGIC_NONLOCAL_GROUPNAME local group exists, then nonlocal
+ * users will be automatically added to it.  Furthermore, if a local
+ * user is added to this group, then that user will inherit any
+ * nonlocal gids from a nonlocal user of the same name, as
+ * supplementary gids.
+ */
 #define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
+
+/*
+ * If the MAGIC_LOCAL_GROUPNAME local group exists, then local users
+ * will be automatically added to it.
+ */
 #define MAGIC_LOCAL_GROUPNAME "nss-local-users"
 
+/*
+ * If the MAGIC_NONLOCAL_USERNAME local user is added to a local
+ * group, then the local group will inherit the nonlocal membership of
+ * a group of the same gid.
+ */
+#define MAGIC_NONLOCAL_USERNAME "nss-nonlocal-users"
+
 
 enum nss_status
 _nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
@@ -71,7 +93,7 @@ __nss_group_nonlocal_lookup(service_user **ni, const char *fct_name,
 
 
 enum nss_status
-check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
+check_nonlocal_gid(const char *user, const char *group, gid_t gid, int *errnop)
 {
     enum nss_status status;
     struct group gbuf;
@@ -86,15 +108,30 @@ check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
 #include "walk_nss.h"
 #undef args
 
-    if (status == NSS_STATUS_SUCCESS) {
-       syslog(LOG_DEBUG, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
-       free(buf);
-       status = NSS_STATUS_NOTFOUND;
-    } else if (status != NSS_STATUS_TRYAGAIN) {
-       status = NSS_STATUS_SUCCESS;
+    if (status == NSS_STATUS_TRYAGAIN)
+       return status;
+    else if (status != NSS_STATUS_SUCCESS)
+       return NSS_STATUS_SUCCESS;
+
+    if (group == NULL || strcmp(gbuf.gr_name, group) == 0) {
+       char *const *mem;
+       for (mem = gbuf.gr_mem; *mem != NULL; mem++)
+           if (strcmp(*mem, MAGIC_NONLOCAL_USERNAME) == 0) {
+               status = check_nonlocal_user(*mem, errnop);
+               if (status == NSS_STATUS_TRYAGAIN) {
+                   free(buf);
+                   return status;
+               } else if (status == NSS_STATUS_NOTFOUND) {
+                   free(buf);
+                   return NSS_STATUS_SUCCESS;
+               }
+               break;
+           }
     }
 
-    return status;
+    syslog(LOG_DEBUG, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
+    free(buf);
+    return NSS_STATUS_NOTFOUND;
 }
 
 enum nss_status
@@ -109,13 +146,13 @@ check_nonlocal_group(const char *user, struct group *grp, int *errnop)
     gid = strtoul(grp->gr_name, &end, 10);
     if (errno == 0 && *end == '\0' && (gid_t)gid == gid) {
        errno = old_errno;
-       status = check_nonlocal_gid(user, gid, errnop);
+       status = check_nonlocal_gid(user, grp->gr_name, gid, errnop);
     } else
        errno = old_errno;
     if (status != NSS_STATUS_SUCCESS)
        return status;
 
-    return check_nonlocal_gid(user, grp->gr_gid, errnop);
+    return check_nonlocal_gid(user, grp->gr_name, grp->gr_gid, errnop);
 }
 
 enum nss_status
@@ -134,6 +171,7 @@ get_local_group(const char *name, struct group *grp, char **buffer, int *errnop)
     return status;
 }
 
+static bool grent_initialized = false;
 static service_user *grent_startp, *grent_nip;
 static void *grent_fct_start;
 static union {
@@ -158,9 +196,12 @@ _nss_nonlocal_setgrent(int stayopen)
     if (status != NSS_STATUS_SUCCESS)
        return status;
 
-    if (grent_fct_start == NULL)
+    if (!grent_initialized) {
        __nss_group_nonlocal_lookup(&grent_startp, grent_fct_name,
                                    &grent_fct_start);
+       __sync_synchronize();
+       grent_initialized = true;
+    }
     grent_nip = grent_startp;
     grent_fct.ptr = grent_fct_start;
     return NSS_STATUS_SUCCESS;
@@ -279,6 +320,39 @@ _nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
     return check_nonlocal_group(grp->gr_name, grp, errnop);
 }
 
+static bool
+add_group(gid_t group, long int *start, long int *size, gid_t **groupsp,
+         long int limit, int *errnop, enum nss_status *status)
+{
+    int i, old_errno = errno;
+    for (i = 0; i < *start; ++i)
+       if ((*groupsp)[i] == group)
+           return true;
+    if (*start + 1 > *size) {
+       gid_t *newgroups;
+       long int newsize = 2 * *size;
+       if (limit > 0) {
+           if (*size >= limit) {
+               *status = NSS_STATUS_SUCCESS;
+               return false;
+           }
+           if (newsize > limit)
+               newsize = limit;
+       }
+       newgroups = realloc(*groupsp, newsize * sizeof((*groupsp)[0]));
+       errno = old_errno;
+       if (newgroups == NULL) {
+           *errnop = ENOMEM;
+           *status = NSS_STATUS_TRYAGAIN;
+           return false;
+       }
+       *groupsp = newgroups;
+       *size = newsize;
+    }
+    (*groupsp)[(*start)++] = group;
+    return true;
+}
+
 enum nss_status
 _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
                             long int *size, gid_t **groupsp, long int limit,
@@ -292,80 +366,83 @@ _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
     const __typeof__(&_nss_nonlocal_initgroups_dyn) self = NULL;
 
     struct group local_users_group, nonlocal_users_group;
-    gid_t local_users_gid, gid;
-    int is_local = 0;
+    bool is_nonlocal = true;
     char *buffer;
-    int old_errno;
     int in, out, i;
 
-    /* Check that the user is a nonlocal user before adding any groups. */
+    /* Check that the user is a nonlocal user, or a member of the
+     * MAGIC_NONLOCAL_GROUPNAME group, before adding any groups. */
     status = check_nonlocal_user(user, errnop);
-    if (status == NSS_STATUS_TRYAGAIN)
-       return status;
-    else if (status != NSS_STATUS_SUCCESS)
-       is_local = 1;
-
-    old_errno = errno;
-
-    status = get_local_group(MAGIC_LOCAL_GROUPNAME,
-                            &local_users_group, &buffer, errnop);
-    if (status == NSS_STATUS_SUCCESS) {
-       local_users_gid = local_users_group.gr_gid;
-       free(buffer);
-    } else if (status == NSS_STATUS_TRYAGAIN) {
+    if (status == NSS_STATUS_TRYAGAIN) {
        return status;
-    } else {
-       syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
-              MAGIC_LOCAL_GROUPNAME);
-       local_users_gid = -1;
-    }
+    } else if (status != NSS_STATUS_SUCCESS) {
+       is_nonlocal = false;
 
-    if (is_local) {
-       gid = local_users_gid;
-    } else {
-       status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
-                                &nonlocal_users_group, &buffer, errnop);
+       status = get_local_group(MAGIC_LOCAL_GROUPNAME,
+                                &local_users_group, &buffer, errnop);
        if (status == NSS_STATUS_SUCCESS) {
-           gid = nonlocal_users_group.gr_gid;
            free(buffer);
+           if (!add_group(local_users_group.gr_gid, start, size, groupsp,
+                          limit, errnop, &status))
+               return status;
        } else if (status == NSS_STATUS_TRYAGAIN) {
            return status;
        } else {
-           syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
-                  MAGIC_NONLOCAL_GROUPNAME);
-           gid = -1;
+           syslog(LOG_WARNING,
+                  "nss_nonlocal: Group %s does not exist locally!",
+                  MAGIC_LOCAL_GROUPNAME);
        }
     }
 
-    if (gid != -1) {
-       int i;
-       for (i = 0; i < *start; ++i)
-           if ((*groupsp)[i] == gid)
-               break;
-       if (i >= *start) {
-           if (*start + 1 > *size) {
-               gid_t *newgroups;
-               long int newsize = 2 * *size;
-               if (limit > 0) {
-                   if (*size >= limit)
-                       return NSS_STATUS_SUCCESS;
-                   if (newsize > limit)
-                       newsize = limit;
+    status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
+                            &nonlocal_users_group, &buffer, errnop);
+    if (status == NSS_STATUS_SUCCESS) {
+       free(buffer);
+       if (is_nonlocal) {
+           if (!add_group(nonlocal_users_group.gr_gid, start, size, groupsp,
+                          limit, errnop, &status))
+               return status;
+       } else {
+           int i;
+           for (i = 0; i < *start; ++i) {
+               if ((*groupsp)[i] == nonlocal_users_group.gr_gid) {
+                   is_nonlocal = true;
+                   break;
+               }
+           }
+
+           if (is_nonlocal) {
+               struct passwd pwbuf;
+               char *buf;
+               int nonlocal_errno = *errnop;
+               status = get_nonlocal_passwd(user, &pwbuf, &buf, errnop);
+
+               if (status == NSS_STATUS_SUCCESS) {
+                   nonlocal_errno = *errnop;
+                   status = check_nonlocal_gid(user, NULL, pwbuf.pw_gid,
+                                               &nonlocal_errno);
+                   free(buf);
                }
-               newgroups = realloc(*groupsp, newsize * sizeof((*groupsp)[0]));
-               errno = old_errno;
-               if (newgroups == NULL) {
-                   *errnop = ENOMEM;
-                   return NSS_STATUS_TRYAGAIN;
+
+               if (status == NSS_STATUS_SUCCESS) {
+                   if (!add_group(pwbuf.pw_gid, start, size, groupsp, limit,
+                                  errnop, &status))
+                       return status;
+               } else if (status == NSS_STATUS_TRYAGAIN) {
+                   *errnop = nonlocal_errno;
+                   return status;
                }
-               *groupsp = newgroups;
-               *size = newsize;
            }
-           (*groupsp)[(*start)++] = gid;
        }
+    } else if (status == NSS_STATUS_TRYAGAIN) {
+       if (is_nonlocal)
+           return status;
+    } else {
+       syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
+              MAGIC_NONLOCAL_GROUPNAME);
     }
 
-    if (is_local)
+    if (!is_nonlocal)
        return NSS_STATUS_SUCCESS;
 
     in = out = *start;
@@ -385,14 +462,8 @@ _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
        if (i < out)
            continue;
 
-       /* Don't let users get into MAGIC_LOCAL_GROUPNAME from nonlocal reasons. */
-       if (local_users_gid == (*groupsp)[in]) {
-           syslog(LOG_WARNING, "nss_nonlocal: Nonlocal user %s removed from special local users group %s",
-                  user, MAGIC_LOCAL_GROUPNAME);
-           continue;
-       }
-
-       status = check_nonlocal_gid(user, (*groupsp)[in], &nonlocal_errno);
+       status = check_nonlocal_gid(user, NULL, (*groupsp)[in],
+                                   &nonlocal_errno);
        if (status == NSS_STATUS_SUCCESS) {
            (*groupsp)[out++] = (*groupsp)[in];
        } else if (status == NSS_STATUS_TRYAGAIN) {
This page took 0.375823 seconds and 4 git commands to generate.