*/
#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,
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;
#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
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
return status;
}
+static bool grent_initialized = false;
static service_user *grent_startp, *grent_nip;
static void *grent_fct_start;
static union {
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;
enum nss_status status;
const struct walk_nss w = {
.lookup = &__nss_group_nonlocal_lookup, .fct_name = "endgrent",
- .status = &status
+ .status = &status, .all_values = 1,
};
const __typeof__(&_nss_nonlocal_endgrent) self = NULL;
enum nss_status status;
const struct walk_nss w = {
.lookup = &__nss_group_nonlocal_lookup, .fct_name = "initgroups_dyn",
- .status = &status, .errnop = errnop
+ .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);
"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;
#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) {
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) {