]> andersk Git - nss_nonlocal.git/blobdiff - nonlocal-group.c
Switch from __nss_next API to __nss_next2 API
[nss_nonlocal.git] / nonlocal-group.c
index 09fa6f205d057fbc824839e7d1f0cf48a0ab87d1..ecf6fed686e132c0903131cdfc478afaf222ffd6 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,
@@ -55,8 +77,8 @@ static service_user *__nss_group_nonlocal_database;
 
 static int
 internal_function
-__nss_group_nonlocal_lookup(service_user **ni, const char *fct_name,
-                           void **fctp)
+__nss_group_nonlocal_lookup2(service_user **ni, const char *fct_name,
+                            const char *fct2_name, void **fctp)
 {
     if (__nss_group_nonlocal_database == NULL
        && __nss_database_lookup("group_nonlocal", NULL, NULL,
@@ -66,19 +88,21 @@ __nss_group_nonlocal_lookup(service_user **ni, const char *fct_name,
     *ni = __nss_group_nonlocal_database;
 
     *fctp = __nss_lookup_function(*ni, fct_name);
+    if (*fctp == NULL && fct2_name != NULL)
+       *fctp = __nss_lookup_function(*ni, fct2_name);
     return 0;
 }
 
 
 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;
     char *buf;
     size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
     const struct walk_nss w = {
-       .lookup = &__nss_group_lookup, .fct_name = "getgrgid_r",
+       .lookup2 = &__nss_group_lookup2, .fct_name = "getgrgid_r",
        .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
     };
     const __typeof__(&_nss_nonlocal_getgrgid_r) self = &_nss_nonlocal_getgrgid_r;
@@ -86,15 +110,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 +148,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
@@ -124,7 +163,7 @@ get_local_group(const char *name, struct group *grp, char **buffer, int *errnop)
     enum nss_status status;
     size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
     const struct walk_nss w = {
-       .lookup = &__nss_group_lookup, .fct_name = "getgrnam_r",
+       .lookup2 = &__nss_group_lookup2, .fct_name = "getgrnam_r",
        .status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen
     };
     const __typeof__(&_nss_nonlocal_getgrnam_r) self = &_nss_nonlocal_getgrnam_r;
@@ -134,6 +173,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 {
@@ -148,7 +188,7 @@ _nss_nonlocal_setgrent(int stayopen)
 {
     enum nss_status status;
     const struct walk_nss w = {
-       .lookup = &__nss_group_nonlocal_lookup, .fct_name = "setgrent",
+       .lookup2 = &__nss_group_nonlocal_lookup2, .fct_name = "setgrent",
        .status = &status
     };
     const __typeof__(&_nss_nonlocal_setgrent) self = NULL;
@@ -158,9 +198,12 @@ _nss_nonlocal_setgrent(int stayopen)
     if (status != NSS_STATUS_SUCCESS)
        return status;
 
-    if (grent_fct_start == NULL)
-       __nss_group_nonlocal_lookup(&grent_startp, grent_fct_name,
-                                   &grent_fct_start);
+    if (!grent_initialized) {
+       __nss_group_nonlocal_lookup2(&grent_startp, grent_fct_name, NULL,
+                                    &grent_fct_start);
+       __sync_synchronize();
+       grent_initialized = true;
+    }
     grent_nip = grent_startp;
     grent_fct.ptr = grent_fct_start;
     return NSS_STATUS_SUCCESS;
@@ -171,8 +214,8 @@ _nss_nonlocal_endgrent(void)
 {
     enum nss_status status;
     const struct walk_nss w = {
-       .lookup = &__nss_group_nonlocal_lookup, .fct_name = "endgrent",
-       .status = &status
+       .lookup2 = &__nss_group_nonlocal_lookup2, .fct_name = "endgrent",
+       .status = &status, .all_values = 1,
     };
     const __typeof__(&_nss_nonlocal_endgrent) self = NULL;
 
@@ -214,7 +257,8 @@ _nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
 
        if (status == NSS_STATUS_SUCCESS)
            return NSS_STATUS_SUCCESS;
-    } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
+    } while (__nss_next2(&grent_nip, grent_fct_name, NULL, &grent_fct.ptr,
+                        status, 0) == 0);
 
     grent_nip = NULL;
     return NSS_STATUS_NOTFOUND;
@@ -227,7 +271,7 @@ _nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
 {
     enum nss_status status;
     const struct walk_nss w = {
-       .lookup = &__nss_group_nonlocal_lookup, .fct_name = "getgrnam_r",
+       .lookup2 = &__nss_group_nonlocal_lookup2, .fct_name = "getgrnam_r",
        .status = &status, .errnop = errnop
     };
     const __typeof__(&_nss_nonlocal_getgrnam_r) self = NULL;
@@ -256,7 +300,7 @@ _nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
 {
     enum nss_status status;
     const struct walk_nss w = {
-       .lookup = &__nss_group_nonlocal_lookup, .fct_name = "getgrgid_r",
+       .lookup2 = &__nss_group_nonlocal_lookup2, .fct_name = "getgrgid_r",
        .status = &status, .errnop = errnop
     };
     const __typeof__(&_nss_nonlocal_getgrgid_r) self = NULL;
@@ -319,22 +363,23 @@ _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
 {
     enum nss_status status;
     const struct walk_nss w = {
-       .lookup = &__nss_group_nonlocal_lookup, .fct_name = "initgroups_dyn",
-       .status = &status, .errnop = errnop
+       .lookup2 = &__nss_group_nonlocal_lookup2, .fct_name = "initgroups_dyn",
+       .status = &status, .all_values = 1, .errnop = errnop
     };
     const __typeof__(&_nss_nonlocal_initgroups_dyn) self = NULL;
 
     struct group local_users_group, nonlocal_users_group;
-    int is_local = 0;
+    bool is_nonlocal = true;
     char *buffer;
     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;
+       is_nonlocal = false;
 
        status = get_local_group(MAGIC_LOCAL_GROUPNAME,
                                 &local_users_group, &buffer, errnop);
@@ -350,23 +395,57 @@ _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
                   "nss_nonlocal: Group %s does not exist locally!",
                   MAGIC_LOCAL_GROUPNAME);
        }
-    } else {
-       status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
-                                &nonlocal_users_group, &buffer, errnop);
-       if (status == NSS_STATUS_SUCCESS) {
-           free(buffer);
+    }
+
+    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 if (status == NSS_STATUS_TRYAGAIN) {
-           return status;
        } else {
-           syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
-                  MAGIC_NONLOCAL_GROUPNAME);
+           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);
+               }
+
+               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;
+               }
+           }
        }
+    } 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;
@@ -374,7 +453,9 @@ _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
 #define args (user, group, start, size, groupsp, limit, errnop)
 #include "walk_nss.h"
 #undef args
-    if (status != NSS_STATUS_SUCCESS)
+    if (status == NSS_STATUS_NOTFOUND || status == NSS_STATUS_UNAVAIL)
+       return NSS_STATUS_SUCCESS;
+    else if (status != NSS_STATUS_SUCCESS)
         return status;
 
     for (; in < *start; ++in) {
@@ -386,7 +467,8 @@ _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
        if (i < out)
            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.296534 seconds and 4 git commands to generate.