/* -1 == not found 0 == function found 1 == finished */ int __nss_lookup (service_user **ni, const char *fct_name, void **fctp) { *fctp = __nss_lookup_function (*ni, fct_name); while (*fctp == NULL && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_CONTINUE && (*ni)->next != NULL) { *ni = (*ni)->next; *fctp = __nss_lookup_function (*ni, fct_name); } return *fctp != NULL ? 0 : (*ni)->next == NULL ? 1 : -1; }
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; /* Free data from previous service. */ endnetgrent_hook (datap); /* Cycle through all the services and run their setnetgrent functions. */ int no_more = setup (&fct.ptr, &datap->nip); while (! no_more) { assert (datap->data == NULL); /* Ignore status, we force check in `__nss_next2'. */ status = DL_CALL_FCT (*fct.f, (group, datap)); service_user *old_nip = datap->nip; no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr, status, 0); if (status == NSS_STATUS_SUCCESS && ! no_more) { enum nss_status (*endfct) (struct __netgrent *); endfct = __nss_lookup_function (old_nip, "endnetgrent"); if (endfct != NULL) (void) DL_CALL_FCT (*endfct, (datap)); } } /* 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; }
/* -1 == not found 0 == adjusted for next function 1 == finished */ int __nss_next2 (service_user **ni, const char *fct_name, const char *fct2_name, void **fctp, int status, int all_values) { if (all_values) { if (nss_next_action (*ni, NSS_STATUS_TRYAGAIN) == NSS_ACTION_RETURN && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_RETURN && nss_next_action (*ni, NSS_STATUS_NOTFOUND) == NSS_ACTION_RETURN && nss_next_action (*ni, NSS_STATUS_SUCCESS) == NSS_ACTION_RETURN) return 1; } else { /* This is really only for debugging. */ if (__builtin_expect (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN, 0)) __libc_fatal ("illegal status in __nss_next"); if (nss_next_action (*ni, status) == NSS_ACTION_RETURN) return 1; } if ((*ni)->next == NULL) return -1; do { *ni = (*ni)->next; *fctp = __nss_lookup_function (*ni, fct_name); if (*fctp == NULL && fct2_name != NULL) *fctp = __nss_lookup_function (*ni, fct2_name); } while (*fctp == NULL && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_CONTINUE && (*ni)->next != NULL); return *fctp != NULL ? 0 : -1; }
static void endnetgrent_hook (struct __netgrent *datap) { enum nss_status (*endfct) (struct __netgrent *); if (datap->nip == NULL || datap->nip == (service_user *) -1l) return; endfct = __nss_lookup_function (datap->nip, "endnetgrent"); if (endfct != NULL) (void) (*endfct) (datap); datap->nip = NULL; }
static int internal_function __nss_passwd_nonlocal_lookup(service_user **ni, const char *fct_name, void **fctp) { if (__nss_passwd_nonlocal_database == NULL && __nss_database_lookup("passwd_nonlocal", NULL, NULL, &__nss_passwd_nonlocal_database) < 0) return -1; *ni = __nss_passwd_nonlocal_database; *fctp = __nss_lookup_function(*ni, fct_name); return 0; }
/* Initialize the NSS interface/functions. The calling function must hold the lock. */ static void init_nss_interface (void) { __libc_lock_lock (lock); /* Retest. */ if (ni == NULL && __nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0) { nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn"); nss_setgrent = __nss_lookup_function (ni, "setgrent"); nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r"); nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r"); nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r"); nss_endgrent = __nss_lookup_function (ni, "endgrent"); } __libc_lock_unlock (lock); }
/* Test whether given (host,user,domain) triple is in NETGROUP. */ int innetgr (const char *netgroup, const char *host, const char *user, const char *domain) { #ifdef USE_NSCD if (__nss_not_use_nscd_netgroup > 0 && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY) __nss_not_use_nscd_netgroup = 0; if (!__nss_not_use_nscd_netgroup && !__nss_database_custom[NSS_DBSIDX_netgroup]) { int result = __nscd_innetgr (netgroup, host, user, domain); if (result >= 0) return result; } #endif union { enum nss_status (*f) (const char *, struct __netgrent *); void *ptr; } setfct; void (*endfct) (struct __netgrent *); int (*getfct) (struct __netgrent *, char *, size_t, int *); struct __netgrent entry; int result = 0; const char *current_group = netgroup; 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, &entry.nip); while (! no_more) { assert (entry.data == NULL); /* Open netgroup. */ enum nss_status status = DL_CALL_FCT (*setfct.f, (current_group, &entry)); if (status == NSS_STATUS_SUCCESS && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r")) != NULL) { char buffer[1024]; while (DL_CALL_FCT (*getfct, (&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) for (namep = entry.needed_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 { 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 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. */ endfct = __nss_lookup_function (entry.nip, "endnetgrent"); if (endfct != NULL) DL_CALL_FCT (*endfct, (&entry)); if (result != 0) break; /* Look for the next service. */ no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL, &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 = tmp->name; continue; } /* No way out. */ break; } /* Free the memory. */ free_memory (&entry); return result == 1; }
int internal_function __internal_getnetgrent_r (char **hostp, char **userp, char **domainp, struct __netgrent *datap, char *buffer, size_t buflen, int *errnop) { enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *); /* 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 = datap->nip == NULL; if (! no_more) { #ifdef USE_NSCD /* This bogus function pointer is a special marker left by __nscd_setnetgrent to tell us to use the data it left before considering any modules. */ if (datap->nip == (service_user *) -1l) fct = nscd_getnetgrent; else #endif { fct = __nss_lookup_function (datap->nip, "getnetgrent_r"); no_more = fct == NULL; } while (! no_more) { status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno)); if (status == NSS_STATUS_RETURN /* The service returned a NOTFOUND, but there are more groups that we need to resolve before we give up. */ || (status == NSS_STATUS_NOTFOUND && datap->needed_groups != NULL)) { /* 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 && datap->nip != NULL) { fct = __nss_lookup_function (datap->nip, "getnetgrent_r"); if (fct != NULL) 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) for (namep = datap->needed_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; } } break; } } 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; }
static int internal_getgrouplist (const char *user, gid_t group, long int *size, gid_t **groupsp, long int limit) { #ifdef USE_NSCD if (__nss_not_use_nscd_group > 0 && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY) __nss_not_use_nscd_group = 0; if (!__nss_not_use_nscd_group && !__nss_database_custom[NSS_DBSIDX_group]) { int n = __nscd_getgrouplist (user, group, size, groupsp, limit); if (n >= 0) return n; /* nscd is not usable. */ __nss_not_use_nscd_group = 1; } #endif enum nss_status status = NSS_STATUS_UNAVAIL; int no_more = 0; /* Never store more than the starting *SIZE number of elements. */ assert (*size > 0); (*groupsp)[0] = group; /* Start is one, because we have the first group as parameter. */ long int start = 1; if (__nss_initgroups_database == NULL) { if (__nss_database_lookup ("initgroups", NULL, "", &__nss_initgroups_database) < 0) { if (__nss_group_database == NULL) no_more = __nss_database_lookup ("group", NULL, "compat files", &__nss_group_database); __nss_initgroups_database = __nss_group_database; } else use_initgroups_entry = true; } else /* __nss_initgroups_database might have been set through __nss_configure_lookup in which case use_initgroups_entry was not set here. */ use_initgroups_entry = __nss_initgroups_database != __nss_group_database; service_user *nip = __nss_initgroups_database; while (! no_more) { long int prev_start = start; initgroups_dyn_function fct = __nss_lookup_function (nip, "initgroups_dyn"); if (fct == NULL) status = compat_call (nip, user, group, &start, size, groupsp, limit, &errno); else status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp, limit, &errno)); /* Remove duplicates. */ long int cnt = prev_start; while (cnt < start) { long int inner; for (inner = 0; inner < prev_start; ++inner) if ((*groupsp)[inner] == (*groupsp)[cnt]) break; if (inner < prev_start) (*groupsp)[cnt] = (*groupsp)[--start]; else ++cnt; } /* This is really only for debugging. */ if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN) __libc_fatal ("illegal status in internal_getgrouplist"); /* For compatibility reason we will continue to look for more entries using the next service even though data has already been found if the nsswitch.conf file contained only a 'groups' line and no 'initgroups' line. If the latter is available we always respect the status. This means that the default for successful lookups is to return. */ if ((use_initgroups_entry || status != NSS_STATUS_SUCCESS) && nss_next_action (nip, status) == NSS_ACTION_RETURN) break; if (nip->next == NULL) no_more = -1; else nip = nip->next; } return start; }
int internal_getnetgrent_r (char **hostp, char **userp, char **domainp, struct __netgrent *datap, char *buffer, size_t buflen, int *errnop) { enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *); /* 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 = (datap->nip == NULL || (fct = __nss_lookup_function (datap->nip, "getnetgrent_r")) == NULL); while (! no_more) { status = (*fct) (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 && datap->nip != NULL) { fct = __nss_lookup_function (datap->nip, "getnetgrent_r"); if (fct != NULL) 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; } } break; } 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; }