void internal_endnetgrent (struct __netgrent *datap) { service_user *old_nip; union { enum nss_status (*f) (struct __netgrent *); void *ptr; } fct; /* Remember which was the last used service. */ old_nip = datap->nip; /* Cycle through all the services and run their endnetgrent functions. */ int no_more = setup (&fct.ptr, "endnetgrent", 1, &datap->nip); while (! no_more) { /* Ignore status, we force check in `__nss_next'. */ (void) (*fct.f) (datap); no_more = (datap->nip == old_nip || __nss_next (&datap->nip, "endnetgrent", &fct.ptr, 0, 1)); } /* Now free list of all netgroup names from last run. */ free_memory (datap); }
int netname2user (const char netname[MAXNETNAMELEN + 1], uid_t * uidp, gid_t * gidp, int *gidlenp, gid_t * gidlist) { static service_user *startp; static netname2user_function start_fct; service_user *nip; union { netname2user_function f; void *ptr; } fct; enum nss_status status = NSS_STATUS_UNAVAIL; int no_more; if (startp == NULL) { no_more = __nss_publickey_lookup (&nip, "netname2user", &fct.ptr); if (no_more) startp = (service_user *) - 1; else { startp = nip; start_fct = fct.f; } } else { fct.f = start_fct; no_more = (nip = startp) == (service_user *) - 1; } while (!no_more) { status = (*fct.f) (netname, uidp, gidp, gidlenp, gidlist); no_more = __nss_next (&nip, "netname2user", &fct.ptr, status, 0); } return status == NSS_STATUS_SUCCESS; }
static int internal_function __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap, int *errnop) { union { enum nss_status (*f) (const char *, struct __netgrent *); void *ptr; } fct; enum nss_status status = NSS_STATUS_UNAVAIL; struct name_list *new_elem; /* Cycle through all the services and run their setnetgrent functions. */ int no_more = setup (&fct.ptr, "setnetgrent", 1, &datap->nip); while (! no_more) { /* Ignore status, we force check in `__nss_next'. */ status = (*fct.f) (group, datap); no_more = __nss_next (&datap->nip, "setnetgrent", &fct.ptr, status, 0); } /* Add the current group to the list of known groups. */ size_t group_len = strlen (group) + 1; new_elem = (struct name_list *) malloc (sizeof (struct name_list) + group_len); if (new_elem == NULL) { *errnop = errno; status = NSS_STATUS_TRYAGAIN; } else { new_elem->next = datap->known_groups; memcpy (new_elem->name, group, group_len); datap->known_groups = new_elem; } return status == NSS_STATUS_SUCCESS; }
int getsecretkey (const char *name, char *key, const char *passwd) { static service_user *startp; static secret_function start_fct; service_user *nip; union { secret_function f; void *ptr; } fct; enum nss_status status = NSS_STATUS_UNAVAIL; int no_more; if (startp == NULL) { no_more = __nss_publickey_lookup (&nip, "getsecretkey", &fct.ptr); if (no_more) startp = (service_user *) -1; else { startp = nip; start_fct = fct.f; } } else { fct.f = start_fct; no_more = (nip = startp) == (service_user *) -1; } while (! no_more) { status = (*fct.f) (name, key, passwd, &errno); no_more = __nss_next (&nip, "getsecretkey", &fct.ptr, status, 0); } return status == NSS_STATUS_SUCCESS; }
enum nss_status check_nonlocal_uid(const char *user, uid_t uid, int *errnop) { enum nss_status status; struct passwd pwbuf; char *buf; size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); const struct walk_nss w = { .lookup = &__nss_passwd_lookup, .fct_name = "getpwuid_r", .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen }; const __typeof__(&_nss_nonlocal_getpwuid_r) self = &_nss_nonlocal_getpwuid_r; #define args (uid, &pwbuf, buf, buflen, errnop) #include "walk_nss.h" #undef args if (status == NSS_STATUS_SUCCESS) { syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name); free(buf); status = NSS_STATUS_NOTFOUND; } else if (status != NSS_STATUS_TRYAGAIN) { status = NSS_STATUS_SUCCESS; } return status; } enum nss_status check_nonlocal_passwd(const char *user, struct passwd *pwd, int *errnop) { enum nss_status status = NSS_STATUS_SUCCESS; int old_errno = errno; char *end; unsigned long uid; errno = 0; uid = strtoul(pwd->pw_name, &end, 10); if (errno == 0 && *end == '\0' && (uid_t)uid == uid) { errno = old_errno; status = check_nonlocal_uid(user, uid, errnop); } else { errno = old_errno; } if (status != NSS_STATUS_SUCCESS) return status; return check_nonlocal_uid(user, pwd->pw_uid, errnop); } enum nss_status check_nonlocal_user(const char *user, int *errnop) { enum nss_status status; struct passwd pwbuf; char *buf; size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); const struct walk_nss w = { .lookup = __nss_passwd_lookup, .fct_name = "getpwnam_r", .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen }; const __typeof__(&_nss_nonlocal_getpwnam_r) self = &_nss_nonlocal_getpwnam_r; #define args (user, &pwbuf, buf, buflen, errnop) #include "walk_nss.h" #undef args if (status == NSS_STATUS_SUCCESS) { free(buf); status = NSS_STATUS_NOTFOUND; } else if (status != NSS_STATUS_TRYAGAIN) { status = NSS_STATUS_SUCCESS; } return status; } enum nss_status get_nonlocal_passwd(const char *name, struct passwd *pwd, char **buffer, int *errnop) { enum nss_status status; size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); const struct walk_nss w = { .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r", .status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen }; const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL; #define args (name, pwd, *buffer, buflen, errnop) #include "walk_nss.h" #undef args return status; } static bool pwent_initialized = false; static service_user *pwent_startp, *pwent_nip; static void *pwent_fct_start; static union { enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen, int *errnop); void *ptr; } pwent_fct; static const char *pwent_fct_name = "getpwent_r"; enum nss_status _nss_nonlocal_setpwent(int stayopen) { enum nss_status status; const struct walk_nss w = { .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "setpwent", .status = &status }; const __typeof__(&_nss_nonlocal_setpwent) self = NULL; #define args (stayopen) #include "walk_nss.h" #undef args if (status != NSS_STATUS_SUCCESS) return status; if (!pwent_initialized) { __nss_passwd_nonlocal_lookup(&pwent_startp, pwent_fct_name, &pwent_fct_start); __sync_synchronize(); pwent_initialized = true; } pwent_nip = pwent_startp; pwent_fct.ptr = pwent_fct_start; return NSS_STATUS_SUCCESS; } enum nss_status _nss_nonlocal_endpwent(void) { enum nss_status status; const struct walk_nss w = { .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "endpwent", .status = &status, .all_values = 1, }; const __typeof__(&_nss_nonlocal_endpwent) self = NULL; pwent_nip = NULL; #define args () #include "walk_nss.h" #undef args return status; } enum nss_status _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen, int *errnop) { enum nss_status status; char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV); if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0') return NSS_STATUS_UNAVAIL; if (pwent_nip == NULL) { status = _nss_nonlocal_setpwent(0); if (status != NSS_STATUS_SUCCESS) return status; } do { if (pwent_fct.ptr == NULL) status = NSS_STATUS_UNAVAIL; else { int nonlocal_errno; do status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop)); while (status == NSS_STATUS_SUCCESS && check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS); } if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) return status; if (status == NSS_STATUS_SUCCESS) return NSS_STATUS_SUCCESS; } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0); pwent_nip = NULL; return NSS_STATUS_NOTFOUND; } enum nss_status _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t buflen, int *errnop) { enum nss_status status; int group_errno; const struct walk_nss w = { .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r", .status = &status, .errnop = errnop }; const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL; char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV); if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0') return NSS_STATUS_UNAVAIL; #define args (name, pwd, buffer, buflen, errnop) #include "walk_nss.h" #undef args if (status != NSS_STATUS_SUCCESS) return status; if (strcmp(name, pwd->pw_name) != 0) { syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name); return NSS_STATUS_NOTFOUND; } status = check_nonlocal_passwd(name, pwd, errnop); if (status != NSS_STATUS_SUCCESS) return status; if (check_nonlocal_gid(name, NULL, pwd->pw_gid, &group_errno) != NSS_STATUS_SUCCESS) pwd->pw_gid = 65534 /* nogroup */; return NSS_STATUS_SUCCESS; } enum nss_status _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t buflen, int *errnop) { enum nss_status status; int group_errno; const struct walk_nss w = { .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "getpwuid_r", .status = &status, .errnop = errnop }; const __typeof__(&_nss_nonlocal_getpwuid_r) self = NULL; char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV); if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0') return NSS_STATUS_UNAVAIL; #define args (uid, pwd, buffer, buflen, errnop) #include "walk_nss.h" #undef args if (status != NSS_STATUS_SUCCESS) return status; if (uid != pwd->pw_uid) { syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid); return NSS_STATUS_NOTFOUND; } status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop); if (status != NSS_STATUS_SUCCESS) return status; if (check_nonlocal_gid(pwd->pw_name, NULL, pwd->pw_gid, &group_errno) != NSS_STATUS_SUCCESS) pwd->pw_gid = 65534 /* nogroup */; return NSS_STATUS_SUCCESS; }
enum nss_status _nss_nonlocal_setspent(int stayopen) { enum nss_status status; const struct walk_nss w = { .lookup = &__nss_shadow_nonlocal_lookup, .fct_name = "setspent", .status = &status }; const __typeof__(&_nss_nonlocal_setspent) self = NULL; #define args (stayopen) #include "walk_nss.h" #undef args if (status != NSS_STATUS_SUCCESS) return status; if (spent_fct_start == NULL) __nss_shadow_nonlocal_lookup(&spent_startp, spent_fct_name, &spent_fct_start); spent_nip = spent_startp; spent_fct.ptr = spent_fct_start; return NSS_STATUS_SUCCESS; } enum nss_status _nss_nonlocal_endspent(void) { enum nss_status status; const struct walk_nss w = { .lookup = &__nss_shadow_nonlocal_lookup, .fct_name = "endspent", .status = &status }; const __typeof__(&_nss_nonlocal_endspent) self = NULL; spent_nip = NULL; #define args () #include "walk_nss.h" #undef args return status; } enum nss_status _nss_nonlocal_getspent_r(struct spwd *pwd, char *buffer, size_t buflen, int *errnop) { enum nss_status status; if (spent_nip == NULL) { status = _nss_nonlocal_setspent(0); if (status != NSS_STATUS_SUCCESS) return status; } do { if (spent_fct.ptr == NULL) status = NSS_STATUS_UNAVAIL; else status = DL_CALL_FCT(spent_fct.l, (pwd, buffer, buflen, errnop)); if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) return status; if (status == NSS_STATUS_SUCCESS) return NSS_STATUS_SUCCESS; } while (__nss_next(&spent_nip, spent_fct_name, &spent_fct.ptr, status, 0) == 0); spent_nip = NULL; return NSS_STATUS_NOTFOUND; } enum nss_status _nss_nonlocal_getspnam_r(const char *name, struct spwd *pwd, char *buffer, size_t buflen, int *errnop) { enum nss_status status; const struct walk_nss w = { .lookup = __nss_shadow_nonlocal_lookup, .fct_name = "getspnam_r", .status = &status, .errnop = errnop }; const __typeof__(&_nss_nonlocal_getspnam_r) self = NULL; #define args (name, pwd, buffer, buflen, errnop) #include "walk_nss.h" #undef args if (status != NSS_STATUS_SUCCESS) return status; if (strcmp(name, pwd->sp_namp) != 0) { syslog(LOG_ERR, "nss_nonlocal: discarding shadow %s from lookup for shadow %s\n", pwd->sp_namp, name); return NSS_STATUS_NOTFOUND; } return NSS_STATUS_SUCCESS; }
/* Test whether given (host,user,domain) triple is in NETGROUP. */ int innetgr (const char *netgroup, const char *host, const char *user, const char *domain) { union { int (*f) (const char *, struct __netgrent *); void *ptr; } setfct; union { void (*f) (struct __netgrent *); void *ptr; } endfct; union { int (*f) (struct __netgrent *, char *, size_t, int *); void *ptr; } getfct; struct __netgrent entry; int result = 0; const char *current_group = netgroup; int real_entry = 0; memset (&entry, '\0', sizeof (entry)); /* Walk through the services until we found an answer or we shall not work further. We can do some optimization here. Since all services must provide the `setnetgrent' function we can do all the work during one walk through the service list. */ while (1) { int no_more = setup (&setfct.ptr, "setnetgrent", 1, &entry.nip); while (! no_more) { /* Open netgroup. */ enum nss_status status = (*setfct.f) (current_group, &entry); if (status == NSS_STATUS_SUCCESS && __nss_lookup (&entry.nip, "getnetgrent_r", &getfct.ptr) == 0) { char buffer[1024]; while ((*getfct.f) (&entry, buffer, sizeof buffer, &errno) == NSS_STATUS_SUCCESS) { if (entry.type == group_val) { /* Make sure we haven't seen the name before. */ struct name_list *namep; for (namep = entry.known_groups; namep != NULL; namep = namep->next) if (strcmp (entry.val.group, namep->name) == 0) break; if (namep == NULL && strcmp (netgroup, entry.val.group) != 0) { size_t group_len = strlen (entry.val.group) + 1; namep = (struct name_list *) malloc (sizeof (*namep) + group_len); if (namep == NULL) { /* Out of memory, simply return. */ result = -1; break; } namep->next = entry.needed_groups; memcpy (namep->name, entry.val.group, group_len); entry.needed_groups = namep; } } else { real_entry = 1; if ((entry.val.triple.host == NULL || host == NULL || __strcasecmp (entry.val.triple.host, host) == 0) && (entry.val.triple.user == NULL || user == NULL || strcmp (entry.val.triple.user, user) == 0) && (entry.val.triple.domain == NULL || domain == NULL || __strcasecmp (entry.val.triple.domain, domain) == 0)) { result = 1; break; } } } if (result != 0) break; /* If we found one service which does know the given netgroup we don't try further. */ status = NSS_STATUS_RETURN; } /* Free all resources of the service. */ if (__nss_lookup (&entry.nip, "endnetgrent", &endfct.ptr) == 0) (*endfct.f) (&entry); /* Look for the next service. */ no_more = __nss_next (&entry.nip, "setnetgrent", &setfct.ptr, status, 0); } if (result == 0 && entry.needed_groups != NULL) { struct name_list *tmp = entry.needed_groups; entry.needed_groups = tmp->next; tmp->next = entry.known_groups; entry.known_groups = tmp; current_group = entry.known_groups->name; continue; } /* No way out. */ break; } /* Free the memory. */ free_memory (&entry); return result; }
int internal_getnetgrent_r (char **hostp, char **userp, char **domainp, struct __netgrent *datap, char *buffer, size_t buflen, int *errnop) { union { enum nss_status (*f) (struct __netgrent *, char *, size_t, int *); void *ptr; } fct; /* Initialize status to return if no more functions are found. */ enum nss_status status = NSS_STATUS_NOTFOUND; /* Run through available functions, starting with the same function last run. We will repeat each function as long as it succeeds, and then go on to the next service action. */ int no_more = setup (&fct.ptr, "getnetgrent_r", 0, &datap->nip); while (! no_more) { status = (*fct.f) (datap, buffer, buflen, &errno); if (status == NSS_STATUS_RETURN) { /* This was the last one for this group. Look at next group if available. */ int found = 0; while (datap->needed_groups != NULL && ! found) { struct name_list *tmp = datap->needed_groups; datap->needed_groups = datap->needed_groups->next; tmp->next = datap->known_groups; datap->known_groups = tmp; found = __internal_setnetgrent_reuse (datap->known_groups->name, datap, errnop); } if (found) continue; } else if (status == NSS_STATUS_SUCCESS && datap->type == group_val) { /* The last entry was a name of another netgroup. */ struct name_list *namep; /* Ignore if we've seen the name before. */ for (namep = datap->known_groups; namep != NULL; namep = namep->next) if (strcmp (datap->val.group, namep->name) == 0) break; if (namep != NULL) /* Really ignore. */ continue; size_t group_len = strlen (datap->val.group) + 1; namep = (struct name_list *) malloc (sizeof (struct name_list) + group_len); if (namep == NULL) /* We are out of memory. */ status = NSS_STATUS_RETURN; else { namep->next = datap->needed_groups; memcpy (namep->name, datap->val.group, group_len); datap->needed_groups = namep; /* And get the next entry. */ continue; } } no_more = __nss_next (&datap->nip, "getnetgrent_r", &fct.ptr, status, 0); } if (status == NSS_STATUS_SUCCESS) { *hostp = (char *) datap->val.triple.host; *userp = (char *) datap->val.triple.user; *domainp = (char *) datap->val.triple.domain; } return status == NSS_STATUS_SUCCESS ? 1 : 0; }