diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2006-10-11 13:34:15 +0000 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2006-10-11 13:34:15 +0000 |
commit | 6f17403298cf33747a45fb5ecbe78bf7632531f9 (patch) | |
tree | a5fc4cfdc3b091a0ee86f3c5c8d5e0ea8fc2c564 /ldap-netgrp.c |
import release 251 of nss-ldap
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss_ldap-251@1 ef36b2f9-881f-0410-afb5-c4e39611909c
Diffstat (limited to 'ldap-netgrp.c')
-rw-r--r-- | ldap-netgrp.c | 993 |
1 files changed, 993 insertions, 0 deletions
diff --git a/ldap-netgrp.c b/ldap-netgrp.c new file mode 100644 index 0000000..bb5c63c --- /dev/null +++ b/ldap-netgrp.c @@ -0,0 +1,993 @@ +/* Copyright (C) 2002-2005 Luke Howard. + This file is part of the nss_ldap library. + Linux support contributed by Larry Lile, <llile@dreamworks.com>, 2002. + Solaris support contributed by Luke Howard, <lukeh@padl.com>, 2004. + + The nss_ldap library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The nss_ldap library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the nss_ldap library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + $Id: ldap-netgrp.c,v 2.44 2006/01/11 18:03:48 lukeh Exp $ + */ + +static char rcsId[] = + "$Id: ldap-netgrp.c,v 2.44 2006/01/11 18:03:48 lukeh Exp $"; + +#include "config.h" + +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> + +#ifdef HAVE_PORT_BEFORE_H +#include <port_before.h> +#endif + +#if defined(HAVE_THREAD_H) && !defined(_AIX) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif + +#ifndef HAVE_SNPRINTF +#include "snprintf.h" +#endif + +#include "ldap-nss.h" +#include "ldap-netgrp.h" +#include "util.h" + +#ifdef HAVE_PORT_AFTER_H +#include <port_after.h> +#endif + +#ifdef HAVE_NSS_H +static ent_context_t *_ngbe = NULL; +#endif + +#ifdef HAVE_IRS_H +enum nss_netgr_status { + NSS_NETGR_FOUND, + NSS_NETGR_NO, + NSS_NETGR_NOMEM +}; + +struct pvt; /* forward declaration for IRS backend type */ +#endif /* HAVE_IRS_H */ + +#ifdef HAVE_NSSWITCH_H +static nss_backend_op_t netgroup_ops[]; +#endif + +#if defined(HAVE_NSSWITCH_H) || defined(HAVE_IRS_H) +struct ldap_innetgr_args +{ + const char *lia_netgroup; + enum nss_netgr_status lia_netgr_status; + int lia_depth; + int lia_erange; +}; + +typedef struct ldap_innetgr_args ldap_innetgr_args_t; + +static NSS_STATUS do_innetgr_nested (ldap_innetgr_args_t * li_args, + const char *nested); +#endif /* HAVE_NSSWITCH_H || HAVE_IRS_H */ + +/* + * I pulled the following macro (EXPAND), functions (strip_whitespace and + * _nss_netgroup_parseline) and structures (name_list and __netgrent) from + * glibc-2.2.x. _nss_netgroup_parseline became _nss_ldap_parse_netgr after + * some modification. + * + * The rest of the code is modeled on various other _nss_ldap functions. + */ + +#define EXPAND(needed) \ + do \ + { \ + size_t old_cursor = result->cursor - result->data; \ + \ + result->data_size += 512 > 2 * needed ? 512 : 2 * needed; \ + result->data = realloc (result->data, result->data_size); \ + \ + if (result->data == NULL) \ + { \ + stat = NSS_UNAVAIL; \ + goto out; \ + } \ + \ + result->cursor = result->data + old_cursor; \ + } \ + while (0) + +/* A netgroup can consist of names of other netgroups. We have to + track which netgroups were read and which still have to be read. */ + + +/* Dataset for iterating netgroups. */ +struct __netgrent +{ + enum + { triple_val, group_val } + type; + + union + { + struct + { + const char *host; + const char *user; + const char *domain; + } + triple; + + const char *group; + } + val; + + /* Room for the data kept between the calls to the netgroup + functions. We must avoid global variables. */ + char *data; + size_t data_size; + char *cursor; + int first; + + struct name_list *known_groups; + struct name_list *needed_groups; +}; + +static char * +strip_whitespace (char *str) +{ + char *cp = str; + + /* Skip leading spaces. */ + while (isspace ((int) *cp)) + cp++; + + str = cp; + while (*cp != '\0' && !isspace ((int) *cp)) + cp++; + + /* Null-terminate, stripping off any trailing spaces. */ + *cp = '\0'; + + return *str == '\0' ? NULL : str; +} + +static NSS_STATUS +_nss_ldap_parse_netgr (void *vresultp, char *buffer, size_t buflen) +{ + struct __netgrent *result = (struct __netgrent *) vresultp; + char *cp = result->cursor; + char *user, *host, *domain; + + /* The netgroup either doesn't exist or is empty. */ + if (cp == NULL) + return NSS_RETURN; + + /* First skip leading spaces. */ + while (isspace ((int) *cp)) + ++cp; + + if (*cp != '(') + { + /* We have a list of other netgroups. */ + char *name = cp; + + while (*cp != '\0' && !isspace ((int) *cp)) + ++cp; + + if (name != cp) + { + /* It is another netgroup name. */ + int last = *cp == '\0'; + + result->type = group_val; + result->val.group = name; + *cp = '\0'; + if (!last) + ++cp; + result->cursor = cp; + result->first = 0; + + return NSS_SUCCESS; + } + return result->first ? NSS_NOTFOUND : NSS_RETURN; + } + + /* Match host name. */ + host = ++cp; + while (*cp != ',') + if (*cp++ == '\0') + return result->first ? NSS_NOTFOUND : NSS_RETURN; + + /* Match user name. */ + user = ++cp; + while (*cp != ',') + if (*cp++ == '\0') + return result->first ? NSS_NOTFOUND : NSS_RETURN; + + /* Match domain name. */ + domain = ++cp; + while (*cp != ')') + if (*cp++ == '\0') + return result->first ? NSS_NOTFOUND : NSS_RETURN; + ++cp; + + /* When we got here we have found an entry. Before we can copy it + to the private buffer we have to make sure it is big enough. */ + if (cp - host > buflen) + return NSS_TRYAGAIN; + + strncpy (buffer, host, cp - host); + result->type = triple_val; + + buffer[(user - host) - 1] = '\0'; + result->val.triple.host = strip_whitespace (buffer); + + buffer[(domain - host) - 1] = '\0'; + result->val.triple.user = strip_whitespace (buffer + (user - host)); + + buffer[(cp - host) - 1] = '\0'; + result->val.triple.domain = strip_whitespace (buffer + (domain - host)); + + /* Remember where we stopped reading. */ + result->cursor = cp; + result->first = 0; + + return NSS_SUCCESS; +} + +#ifdef HAVE_NSS_H +static NSS_STATUS +_nss_ldap_load_netgr (LDAPMessage * e, + ldap_state_t * pvt, + void *vresultp, char *buffer, size_t buflen) +{ + int attr; + int nvals; + int valcount = 0; + char **vals; + char **valiter; + struct __netgrent *result = vresultp; + NSS_STATUS stat = NSS_SUCCESS; + + for (attr = 0; attr < 2; attr++) + { + switch (attr) + { + case 1: + vals = _nss_ldap_get_values (e, AT (nisNetgroupTriple)); + break; + default: + vals = _nss_ldap_get_values (e, AT (memberNisNetgroup)); + break; + } + + nvals = ldap_count_values (vals); + + if (vals == NULL) + continue; + + if (nvals == 0) + { + ldap_value_free (vals); + continue; + } + + if (result->data_size > 0 + && result->cursor - result->data + 1 > result->data_size) + EXPAND (1); + + if (result->data_size > 0) + *result->cursor++ = ' '; + + valcount += nvals; + valiter = vals; + + while (*valiter != NULL) + { + int curlen = strlen (*valiter); + if (result->cursor - result->data + curlen + 1 > result->data_size) + EXPAND (curlen + 1); + memcpy (result->cursor, *valiter, curlen + 1); + result->cursor += curlen; + valiter++; + if (*valiter != NULL) + *result->cursor++ = ' '; + } + ldap_value_free (vals); + } + + result->first = 1; + result->cursor = result->data; + +out: + + return stat; +} + +NSS_STATUS +_nss_ldap_endnetgrent (struct __netgrent * result) +{ + if (result->data != NULL) + { + free (result->data); + result->data = NULL; + result->data_size = 0; + result->cursor = NULL; + } + + LOOKUP_ENDENT (_ngbe); +} + +NSS_STATUS +_nss_ldap_setnetgrent (char *group, struct __netgrent *result) +{ + int errnop = 0, buflen = 0; + char *buffer = (char *) NULL; + ldap_args_t a; + NSS_STATUS stat = NSS_SUCCESS; + + if (group[0] == '\0') + return NSS_UNAVAIL; + + if (result->data != NULL) + free (result->data); + result->data = result->cursor = NULL; + result->data_size = 0; + + LA_INIT (a); + LA_STRING (a) = group; + LA_TYPE (a) = LA_TYPE_STRING; + + stat = + _nss_ldap_getbyname (&a, result, buffer, buflen, &errnop, + _nss_ldap_filt_getnetgrent, LM_NETGROUP, + _nss_ldap_load_netgr); + + LOOKUP_SETENT (_ngbe); +} + +NSS_STATUS +_nss_ldap_getnetgrent_r (struct __netgrent *result, + char *buffer, size_t buflen, int *errnop) +{ + return _nss_ldap_parse_netgr (result, buffer, buflen); +} +#endif /* HAVE_NSS_H */ + +#if defined(HAVE_NSSWITCH_H) || defined(HAVE_IRS_H) +/* + * Chase nested netgroups. If we can't find a nested netgroup, we try + * the next one - don't want to fail authoritatively because of bad + * user data. + */ +static NSS_STATUS +nn_chase (nss_ldap_netgr_backend_t * ngbe, LDAPMessage ** pEntry) +{ + ldap_args_t a; + NSS_STATUS stat = NSS_NOTFOUND; + + debug ("==> nn_chase"); + + if (ngbe->state->ec_res != NULL) + { + ldap_msgfree (ngbe->state->ec_res); + ngbe->state->ec_res = NULL; + } + + while (ngbe->needed_groups != NULL) + { + /* If this netgroup has already been seen, avoid it */ + if (_nss_ldap_namelist_find (ngbe->known_groups, ngbe->needed_groups->name)) + { + _nss_ldap_namelist_pop (&ngbe->needed_groups); + continue; + } + + LA_INIT (a); + LA_TYPE (a) = LA_TYPE_STRING; + LA_STRING (a) = ngbe->needed_groups->name; + + debug (":== nn_chase: nested netgroup=%s", LA_STRING (a)); + + _nss_ldap_enter (); + stat = _nss_ldap_search_s (&a, _nss_ldap_filt_getnetgrent, + LM_NETGROUP, NULL, 1, &ngbe->state->ec_res); + _nss_ldap_leave (); + + if (stat == NSS_SUCCESS) + { + /* we have "seen" this netgroup; track it for loop detection */ + stat = _nss_ldap_namelist_push (&ngbe->known_groups, ngbe->needed_groups->name); + if (stat != NSS_SUCCESS) + { + _nss_ldap_namelist_pop (&ngbe->needed_groups); + break; + } + } + + _nss_ldap_namelist_pop (&ngbe->needed_groups); + + if (stat == NSS_SUCCESS) + { + /* Check we got an entry, not just a result. */ + *pEntry = _nss_ldap_first_entry (ngbe->state->ec_res); + if (*pEntry == NULL) + { + ldap_msgfree (ngbe->state->ec_res); + ngbe->state->ec_res = NULL; + stat = NSS_NOTFOUND; + } + } + + if (stat == NSS_SUCCESS) + { + /* found one. */ + break; + } + } + + debug ("<== nn_chase result=%d", stat); + + return stat; +} +#endif /* HAVE_NSSWITCH_H || HAVE_IRS_H */ + +#if defined(HAVE_NSSWITCH_H) || defined(HAVE_IRS_H) +/* + * getnetgrent() inner implementation, used by both Solaris NSS + * and IRS/AIX + */ +static NSS_STATUS +do_getnetgrent (nss_ldap_netgr_backend_t *be, + char *buffer, size_t buflen, + enum nss_netgr_status *status, + char **machine, char **user, char **domain) +{ + ent_context_t *ctx; + NSS_STATUS parseStat = NSS_NOTFOUND; + + /* + * This function is called with the pseudo-backend that + * we created in _nss_ldap_setnetgrent() (see below) + */ + debug ("==> do_getnetgrent"); + + ctx = be->state; + assert (ctx != NULL); + + *status = NSS_NETGR_NO; + *machine = NULL; + *user = NULL; + *domain = NULL; + + do + { + NSS_STATUS resultStat = NSS_SUCCESS; + char **vals, **p; + ldap_state_t *state = &ctx->ec_state; + struct __netgrent __netgrent; + LDAPMessage *e; + + if (state->ls_retry == 0 && state->ls_info.ls_index == -1) + { + resultStat = NSS_NOTFOUND; + + if (ctx->ec_res != NULL) + { + e = _nss_ldap_first_entry (ctx->ec_res); + if (e != NULL) + resultStat = NSS_SUCCESS; + } + + if (resultStat != NSS_SUCCESS) + { + /* chase nested netgroups */ + resultStat = nn_chase (be, &e); + } + + if (resultStat != NSS_SUCCESS) + { + parseStat = resultStat; + break; + } + + assert (e != NULL); + + /* Push nested netgroups onto stack for deferred chasing */ + vals = _nss_ldap_get_values (e, AT (memberNisNetgroup)); + if (vals != NULL) + { + for (p = vals; *p != NULL; p++) + { + parseStat = _nss_ldap_namelist_push (&be->needed_groups, *p); + if (parseStat != NSS_SUCCESS) + break; + } + ldap_value_free (vals); + + if (parseStat != NSS_SUCCESS) + break; /* out of memory */ + } + } + else + { + assert (ctx->ec_res != NULL); + e = _nss_ldap_first_entry (ctx->ec_res); + if (e == NULL) + { + /* This should never happen, but we fail gracefully. */ + parseStat = NSS_UNAVAIL; + break; + } + } + + /* We have an entry; now, try to parse it. */ + vals = _nss_ldap_get_values (e, AT (nisNetgroupTriple)); + if (vals == NULL) + { + state->ls_info.ls_index = -1; + parseStat = NSS_NOTFOUND; + ldap_msgfree (ctx->ec_res); + ctx->ec_res = NULL; + continue; + } + + switch (state->ls_info.ls_index) + { + case 0: + /* last time. decrementing ls_index to -1 AND returning + * an error code will force this entry to be discared. + */ + parseStat = NSS_NOTFOUND; + break; + case -1: + /* first time */ + state->ls_info.ls_index = ldap_count_values (vals); + /* fall off to default... */ + default: + __netgrent.data = vals[state->ls_info.ls_index - 1]; + __netgrent.data_size = strlen (vals[state->ls_info.ls_index - 1]); + __netgrent.cursor = __netgrent.data; + __netgrent.first = 1; + + parseStat = _nss_ldap_parse_netgr (&__netgrent, buffer, buflen); + if (parseStat != NSS_SUCCESS) + { + break; + } + if (__netgrent.type != triple_val) + { + parseStat = NSS_NOTFOUND; + break; + } + *machine = (char *) __netgrent.val.triple.host; + *user = (char *) __netgrent.val.triple.user; + *domain = (char *) __netgrent.val.triple.domain; + break; + } + + ldap_value_free (vals); + state->ls_info.ls_index--; + + /* hold onto the state if we're out of memory XXX */ + state->ls_retry = (parseStat == NSS_TRYAGAIN ? 1 : 0); + *status = (parseStat == NSS_SUCCESS) ? NSS_NETGR_FOUND : NSS_NETGR_NOMEM; + + if (state->ls_retry == 0 && state->ls_info.ls_index == -1) + { + ldap_msgfree (ctx->ec_res); + ctx->ec_res = NULL; + } + } + while (parseStat == NSS_NOTFOUND); + + if (parseStat == NSS_TRYAGAIN) + { + errno = ERANGE; + } + + debug ("<== do_getnetgrent"); + + return parseStat; +} + +/* + * Test a 4-tuple + */ +static NSS_STATUS +do_parse_innetgr (LDAPMessage * e, ldap_state_t * pvt, + void *result, char *buffer, size_t buflen) +{ + ldap_innetgr_args_t *li_args = (ldap_innetgr_args_t *) result; + int count; + char **values = NULL; + NSS_STATUS stat = NSS_NOTFOUND; + + debug ("==> do_parse_innetgr"); + + values = _nss_ldap_get_values (e, ATM (LM_NETGROUP, cn)); + if (values == NULL) + return NSS_NOTFOUND; + + count = ldap_count_values (values); + + while (--count >= 0) + { + assert (values[count] != NULL); + + if (strcasecmp (li_args->lia_netgroup, values[count]) == 0) + { + li_args->lia_netgr_status = NSS_NETGR_FOUND; + stat = NSS_SUCCESS; + } + else + { + stat = do_innetgr_nested (li_args, values[count]); + } + + if (stat == NSS_SUCCESS) + break; + } + + ldap_value_free (values); + + debug ("<== do_parse_innetgr"); + + return stat; +} + +/* + * NB: caller has acquired the global lock + */ +static NSS_STATUS +do_innetgr_nested (ldap_innetgr_args_t * li_args, const char *nested) +{ + NSS_STATUS stat; + ldap_args_t a; + ent_context_t *ctx = NULL; + + debug ("==> do_innetgr_nested netgroup=%s assertion=%s", + li_args->lia_netgroup, nested); + + if (li_args->lia_depth >= LDAP_NSS_MAXNETGR_DEPTH) + { + debug ("<== do_innetgr_nested: maximum depth exceeded"); + return NSS_NOTFOUND; + } + + LA_INIT (a); + LA_TYPE (a) = LA_TYPE_STRING; + LA_STRING (a) = nested; /* memberNisNetgroup */ + + if (_nss_ldap_ent_context_init_locked (&ctx) == NULL) + { + debug ("<== do_innetgr_nested: failed to initialize context"); + return NSS_UNAVAIL; + } + + li_args->lia_depth++; + + stat = _nss_ldap_getent_ex (&a, &ctx, (void *) li_args, NULL, 0, + &li_args->lia_erange, _nss_ldap_filt_innetgr, + LM_NETGROUP, NULL, do_parse_innetgr); + + li_args->lia_depth--; + + _nss_ldap_ent_context_release (ctx); + free (ctx); + + debug ("<== do_innetgr_nested status=%d netgr_status=%d", + stat, li_args->lia_netgr_status); + + return stat; +} + +/* + * NB: caller has acquired the global lock + */ +static NSS_STATUS +do_innetgr (ldap_innetgr_args_t * li_args, + const char *machine, const char *user, const char *domain) +{ + NSS_STATUS stat; + ldap_args_t a; + ent_context_t *ctx = NULL; + + debug ("==> do_innetgr netgroup=%s", li_args->lia_netgroup); + + /* + * First, find which netgroup the 3-tuple belongs to. + */ + LA_INIT (a); + LA_TYPE (a) = LA_TYPE_TRIPLE; + LA_TRIPLE (a).user = user; + LA_TRIPLE (a).host = machine; + LA_TRIPLE (a).domain = domain; + + if (_nss_ldap_ent_context_init_locked (&ctx) == NULL) + { + debug ("<== do_innetgr: failed to initialize context"); + return NSS_UNAVAIL; + } + + stat = _nss_ldap_getent_ex (&a, &ctx, (void *) li_args, NULL, 0, + &li_args->lia_erange, NULL, LM_NETGROUP, + NULL, do_parse_innetgr); + + _nss_ldap_ent_context_release (ctx); + free (ctx); + + debug ("<== do_innetgr status=%d netgr_status=%d", + stat, li_args->lia_netgr_status); + + return stat; +} +#endif /* HAVE_NSSWITCH_H || HAVE_IRS_H */ + +#ifdef HAVE_NSSWITCH_H +static NSS_STATUS +_nss_ldap_getnetgroup_endent (nss_backend_t * be, void *_args) +{ + LOOKUP_ENDENT (be); +} + +static NSS_STATUS +_nss_ldap_getnetgroup_setent (nss_backend_t * be, void *_args) +{ + return NSS_SUCCESS; +} + +static NSS_STATUS +_nss_ldap_getnetgroup_getent (nss_backend_t * _be, void *_args) +{ + nss_ldap_netgr_backend_t *be = (nss_ldap_netgr_backend_t *) _be; + struct nss_getnetgrent_args *args = (struct nss_getnetgrent_args *) _args; + NSS_STATUS stat; + + _nss_ldap_enter (); + + stat = do_getnetgrent (be, + args->buffer, + args->buflen, + &args->status, + &args->retp[NSS_NETGR_MACHINE], + &args->retp[NSS_NETGR_USER], + &args->retp[NSS_NETGR_DOMAIN]); + + _nss_ldap_leave (); + + return stat; +} + +static NSS_STATUS +_nss_ldap_innetgr (nss_backend_t * be, void *_args) +{ + NSS_STATUS stat = NSS_NOTFOUND; + struct nss_innetgr_args *args = (struct nss_innetgr_args *) _args; + int i; + + /* + * Enumerate the groups in args structure and see whether + * any 4-tuple was satisfied. This really needs LDAP + * component matching to be done efficiently. + */ + + debug + ("==> _nss_ldap_innetgr MACHINE.argc=%d USER.argc=%d DOMAIN.argc=%d groups.argc=%d", + args->arg[NSS_NETGR_MACHINE].argc, args->arg[NSS_NETGR_USER].argc, + args->arg[NSS_NETGR_DOMAIN].argc, args->groups.argc); + + /* Presume these are harmonized -- this is a strange interface */ + assert (args->arg[NSS_NETGR_MACHINE].argc == 0 || + args->arg[NSS_NETGR_MACHINE].argc == args->groups.argc); + assert (args->arg[NSS_NETGR_USER].argc == 0 || + args->arg[NSS_NETGR_USER].argc == args->groups.argc); + assert (args->arg[NSS_NETGR_DOMAIN].argc == 0 || + args->arg[NSS_NETGR_DOMAIN].argc == args->groups.argc); + + _nss_ldap_enter (); + + for (i = 0; i < args->groups.argc; i++) + { + NSS_STATUS parseStat; + ldap_innetgr_args_t li_args; + + const char *machine = (args->arg[NSS_NETGR_MACHINE].argc != 0) ? + args->arg[NSS_NETGR_MACHINE].argv[i] : NULL; + const char *user = (args->arg[NSS_NETGR_USER].argc != 0) ? + args->arg[NSS_NETGR_USER].argv[i] : NULL; + const char *domain = (args->arg[NSS_NETGR_DOMAIN].argc != 0) ? + args->arg[NSS_NETGR_DOMAIN].argv[i] : NULL; + + li_args.lia_netgroup = args->groups.argv[i]; + li_args.lia_netgr_status = NSS_NETGR_NO; + li_args.lia_depth = 0; + li_args.lia_erange = 0; + + parseStat = do_innetgr (&li_args, machine, user, domain); + if (parseStat != NSS_SUCCESS && parseStat != NSS_NOTFOUND) + { + /* fatal error */ + if (li_args.lia_erange != 0) + errno = ERANGE; + break; + } + + args->status = li_args.lia_netgr_status; + + if (args->status == NSS_NETGR_FOUND) + { + stat = NSS_SUCCESS; + } + } + + _nss_ldap_leave (); + + debug ("<== _nss_ldap_innetgr"); + + return stat; +} + +/* + * According to the "documentation", setnetgrent() is really + * a getXXXbyYYY() operation that returns a pseudo-backend + * through which one may enumerate the netgroup's members. + * + * ie. this is the constructor for the pseudo-backend. + */ +static NSS_STATUS +_nss_ldap_setnetgrent (nss_backend_t * be, void *_args) +{ + NSS_STATUS stat; + struct nss_setnetgrent_args *args; + nss_ldap_netgr_backend_t *ngbe; + ldap_args_t a; + + debug ("==> _nss_ldap_setnetgrent"); + + args = (struct nss_setnetgrent_args *) _args; + args->iterator = NULL; /* initialize */ + + /* + * This retrieves the top-level netgroup; nested netgroups + * are chased inside the pseudo-backend. + */ + + LA_INIT (a); + LA_TYPE (a) = LA_TYPE_STRING; + LA_STRING (a) = args->netgroup; /* cn */ + + ngbe = (nss_ldap_netgr_backend_t *) malloc (sizeof (*ngbe)); + if (ngbe == NULL) + { + debug ("<== _nss_ldap_setnetgrent"); + return NSS_UNAVAIL; + } + + ngbe->ops = netgroup_ops; + ngbe->n_ops = 6; + ngbe->state = NULL; + ngbe->known_groups = NULL; + ngbe->needed_groups = NULL; + + stat = _nss_ldap_default_constr ((nss_ldap_backend_t *) ngbe); + if (stat != NSS_SUCCESS) + { + free (ngbe); + debug ("<== _nss_ldap_setnetgrent"); + return stat; + } + + if (_nss_ldap_ent_context_init (&ngbe->state) == NULL) + { + _nss_ldap_default_destr ((nss_backend_t *) ngbe, NULL); + debug ("<== _nss_ldap_setnetgrent"); + return NSS_UNAVAIL; + } + + assert (ngbe->state != NULL); + assert (ngbe->state->ec_res == NULL); + + _nss_ldap_enter (); + stat = _nss_ldap_search_s (&a, _nss_ldap_filt_getnetgrent, + LM_NETGROUP, NULL, 1, &ngbe->state->ec_res); + _nss_ldap_leave (); + + if (stat == NSS_SUCCESS) + { + /* we have "seen" this netgroup; track it for loop detection */ + stat = _nss_ldap_namelist_push (&ngbe->known_groups, args->netgroup); + } + + if (stat == NSS_SUCCESS) + { + args->iterator = (nss_backend_t *) ngbe; + } + else + { + _nss_ldap_default_destr ((nss_backend_t *) ngbe, NULL); + } + + debug ("<== _nss_ldap_setnetgrent"); + + return stat; +} + +static NSS_STATUS +_nss_ldap_netgroup_destr (nss_backend_t * _ngbe, void *args) +{ + nss_ldap_netgr_backend_t *ngbe = (nss_ldap_netgr_backend_t *) _ngbe; + + /* free list of nested netgroups */ + _nss_ldap_namelist_destroy (&ngbe->known_groups); + _nss_ldap_namelist_destroy (&ngbe->needed_groups); + + return _nss_ldap_default_destr (_ngbe, args); +} + +static nss_backend_op_t netgroup_ops[] = { + _nss_ldap_netgroup_destr, /* NSS_DBOP_DESTRUCTOR */ + _nss_ldap_getnetgroup_endent, /* NSS_DBOP_ENDENT */ + _nss_ldap_getnetgroup_setent, /* NSS_DBOP_SETENT */ + _nss_ldap_getnetgroup_getent, /* NSS_DBOP_GETENT */ + _nss_ldap_innetgr, /* NSS_DBOP_NETGROUP_IN */ + _nss_ldap_setnetgrent /* NSS_DBOP_NETGROUP_SET */ +}; + +nss_backend_t * +_nss_ldap_netgroup_constr (const char *db_name, + const char *src_name, const char *cfg_args) +{ + nss_ldap_netgr_backend_t *be; + + if (!(be = (nss_ldap_netgr_backend_t *) malloc (sizeof (*be)))) + return NULL; + + be->ops = netgroup_ops; + be->n_ops = sizeof (netgroup_ops) / sizeof (nss_backend_op_t); + be->known_groups = NULL; + be->needed_groups = NULL; + + if (_nss_ldap_default_constr ((nss_ldap_backend_t *) be) != NSS_SUCCESS) + { + free (be); + return NULL; + } + + return (nss_backend_t *) be; +} + +#endif /* !HAVE_NSS_H */ + +#ifdef HAVE_IRS_H +#include "irs-netgrp.c" +#endif /* HAVE_IRS_H */ |