Print this page
NEX-14547 Get UNIX group info. from AD/LDAP with partial RFC2307 schema
NEX-13132 smbd dumping core in nss_ldap.so.1`getbymember
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-13132 smbd dumping core in nss_ldap.so.1`getbymember disables ALL shares
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/nsswitch/ldap/common/getgrent.c
          +++ new/usr/src/lib/nsswitch/ldap/common/getgrent.c
↓ open down ↓ 29 lines elided ↑ open up ↑
  30   30  #include <string.h>
  31   31  
  32   32  /* String which may need to be removed from beginning of group password */
  33   33  #define _CRYPT          "{CRYPT}"
  34   34  #define _NO_PASSWD_VAL  ""
  35   35  
  36   36  /* Group attributes filters */
  37   37  #define _G_NAME         "cn"
  38   38  #define _G_GID          "gidnumber"
  39   39  #define _G_PASSWD       "userpassword"
  40      -#define _G_MEM          "memberuid"
       40 +#define _G_MEMUID       "memberuid"
       41 +#define _G_MEM_DN       "member"        /* DN */
  41   42  
  42   43  #define _F_GETGRNAM     "(&(objectClass=posixGroup)(cn=%s))"
  43   44  #define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))"
  44   45  #define _F_GETGRGID     "(&(objectClass=posixGroup)(gidNumber=%u))"
  45   46  #define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%u))"
  46      -/*
  47      - * Group membership can be defined by either username or DN, so when searching
  48      - * for groups by member we need to consider both. The first parameter in the
  49      - * filter is replaced by username, the second by DN.
  50      - */
  51      -#define _F_GETGRMEM \
  52      -        "(&(objectClass=posixGroup)(|(memberUid=%s)(memberUid=%s)))"
  53      -#define _F_GETGRMEM_SSD "(&(%%s)(|(memberUid=%s)(memberUid=%s)))"
  54   47  
  55   48  /*
  56      - * Copied from getpwnam.c, needed to look up user DN.
  57      - * Would it be better to move to ldap_common.h rather than duplicate?
       49 + * When searching for groups in which a specified user is a member,
       50 + * there are a few different membership schema that might be in use.
       51 + * We'll use a filter that should work with an of the common ones:
       52 + * "memberUid=NAME", or "member=DN" (try uniquemember too?)
       53 + * The first parameter in the filter string is replaced by username,
       54 + * and the remaining ones by the full DN.
  58   55   */
  59      -#define _F_GETPWNAM     "(&(objectClass=posixAccount)(uid=%s))"
  60      -#define _F_GETPWNAM_SSD "(&(%%s)(uid=%s))"
       56 +#define _F_GETGRMEM "(&(objectClass=posixGroup)" \
       57 +        "(|(memberUid=%s)(member=%s)))"
       58 +#define _F_GETGRMEM_SSD "(&(%%s)" \
       59 +        "(|(memberUid=%s)(member=%s)))"
  61   60  
  62   61  static const char *gr_attrs[] = {
  63   62          _G_NAME,
  64   63          _G_GID,
  65   64          _G_PASSWD,
  66      -        _G_MEM,
       65 +        _G_MEMUID,
       66 +        _G_MEM_DN,
  67   67          (char *)NULL
  68   68  };
  69   69  
       70 +static int
       71 +getmembers_UID(char **bufpp, int *lenp, ns_ldap_attr_t *members);
       72 +static int
       73 +getmembers_DN(char **bufpp, int *lenp, ns_ldap_attr_t *members);
  70   74  
       75 +
  71   76  /*
  72   77   * _nss_ldap_group2str is the data marshaling method for the group getXbyY
  73   78   * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method
  74   79   * is called after a successful ldap search has been performed. This method
  75   80   * will parse the ldap search values into the file format.
  76   81   * e.g.
  77   82   *
  78   83   * adm::4:root,adm,daemon
  79   84   *
  80   85   */
  81   86  
  82   87  static int
  83   88  _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp)
  84   89  {
  85   90          int             i;
  86   91          int             nss_result;
  87   92          int             buflen = 0, len;
  88      -        int             firstime = 1;
  89   93          char            *buffer = NULL;
  90   94          ns_ldap_result_t        *result = be->result;
  91   95          char            **gname, **passwd, **gid, *password, *end;
  92   96          char            gid_nobody[NOBODY_STR_LEN];
  93   97          char            *gid_nobody_v[1];
  94      -        char            *member_str, *strtok_state;
  95   98          ns_ldap_attr_t  *members;
  96   99  
  97  100          (void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY);
  98  101          gid_nobody_v[0] = gid_nobody;
  99  102  
 100  103          if (result == NULL)
 101  104                  return (NSS_STR_PARSE_PARSE);
 102  105          buflen = argp->buf.buflen;
 103  106  
 104  107          if (argp->buf.result != NULL) {
↓ open down ↓ 34 lines elided ↑ open up ↑
 139  142          if (gid == NULL || gid[0] == NULL || (strlen(gid[0]) < 1)) {
 140  143                  nss_result = NSS_STR_PARSE_PARSE;
 141  144                  goto result_grp2str;
 142  145          }
 143  146          /* Validate GID */
 144  147          if (strtoul(gid[0], &end, 10) > MAXUID)
 145  148                  gid = gid_nobody_v;
 146  149          len = snprintf(buffer, buflen, "%s:%s:%s:", gname[0], password, gid[0]);
 147  150          TEST_AND_ADJUST(len, buffer, buflen, result_grp2str);
 148  151  
 149      -        members = __ns_ldap_getAttrStruct(result->entry, _G_MEM);
 150      -        if (members == NULL || members->attrvalue == NULL) {
 151      -                /* no member is fine, skip processing the member list */
 152      -                goto nomember;
      152 +        members = __ns_ldap_getAttrStruct(result->entry, _G_MEMUID);
      153 +        if (members != NULL && members->attrvalue != NULL) {
      154 +                nss_result = getmembers_UID(&buffer, &buflen, members);
      155 +                if (nss_result != 0)
      156 +                        goto result_grp2str;
 153  157          }
 154  158  
 155      -        for (i = 0; i < members->value_count; i++) {
 156      -                if (members->attrvalue[i] == NULL) {
 157      -                        nss_result = NSS_STR_PARSE_PARSE;
      159 +        members = __ns_ldap_getAttrStruct(result->entry, _G_MEM_DN);
      160 +        if (members != NULL && members->attrvalue != NULL) {
      161 +                nss_result = getmembers_DN(&buffer, &buflen, members);
      162 +                if (nss_result != 0)
 158  163                          goto result_grp2str;
 159      -                }
 160      -                /*
 161      -                 * If we find an '=' in the member attribute value, treat it as
 162      -                 * a DN, otherwise as a username.
 163      -                 */
 164      -                if (member_str = strchr(members->attrvalue[i], '=')) {
 165      -                        member_str++; /* skip over the '=' */
 166      -                        /* Fail if we can't pull a username out of the RDN */
 167      -                        if (! (member_str = strtok_r(member_str,
 168      -                            ",", &strtok_state))) {
 169      -                                nss_result = NSS_STR_PARSE_PARSE;
 170      -                                goto result_grp2str;
 171      -                        }
 172      -                } else {
 173      -                        member_str = members->attrvalue[i];
 174      -                }
 175      -                if (*member_str != '\0') {
 176      -                        if (firstime) {
 177      -                                len = snprintf(buffer, buflen, "%s",
 178      -                                    member_str);
 179      -                                TEST_AND_ADJUST(len, buffer, buflen,
 180      -                                    result_grp2str);
 181      -                                firstime = 0;
 182      -                        } else {
 183      -                                len = snprintf(buffer, buflen, ",%s",
 184      -                                    member_str);
 185      -                                TEST_AND_ADJUST(len, buffer, buflen,
 186      -                                    result_grp2str);
 187      -                        }
 188      -                }
 189  164          }
 190      -nomember:
      165 +
 191  166          /* The front end marshaller doesn't need the trailing nulls */
 192  167          if (argp->buf.result != NULL)
 193  168                  be->buflen = strlen(be->buffer);
 194  169  result_grp2str:
 195  170          (void) __ns_ldap_freeResult(&be->result);
 196  171          return (nss_result);
 197  172  }
 198  173  
 199  174  /*
      175 + * Process the list values from the "memberUid" attribute of the
      176 + * current group.  Note that this list is often empty, and we
      177 + * get the real list of members via getmember_DN (see below).
      178 + */
      179 +static int
      180 +getmembers_UID(char **bufpp, int *lenp, ns_ldap_attr_t *members)
      181 +{
      182 +        char    *member_str, *strtok_state;
      183 +        char    *buffer;
      184 +        int     buflen;
      185 +        int     i, len;
      186 +        int     nss_result = 0;
      187 +        int     firsttime;
      188 +
      189 +        buffer = *bufpp;
      190 +        buflen = *lenp;
      191 +        firsttime = (buffer[-1] == ':');
      192 +
      193 +        for (i = 0; i < members->value_count; i++) {
      194 +                member_str = members->attrvalue[i];
      195 +                if (member_str == NULL)
      196 +                        goto out;
      197 +
      198 +#ifdef DEBUG
      199 +                (void) fprintf(stdout, "getmembers_UID: uid=<%s>\n",
      200 +                    member_str);
      201 +#endif
      202 +                /*
      203 +                 * If not a valid Unix user name, or
      204 +                 * not valid in ldap, just skip.
      205 +                 */
      206 +                if (member_str[0] == '\0' ||
      207 +                    strpbrk(member_str, " ,:=") != NULL)
      208 +                        continue;
      209 +
      210 +                if (firsttime)
      211 +                        len = snprintf(buffer, buflen, "%s", member_str);
      212 +                else
      213 +                        len = snprintf(buffer, buflen, ",%s", member_str);
      214 +                TEST_AND_ADJUST(len, buffer, buflen, out);
      215 +        }
      216 +
      217 +out:
      218 +        *bufpp = buffer;
      219 +        *lenp = buflen;
      220 +        return (nss_result);
      221 +}
      222 +
      223 +/*
      224 + * Process the list values from the "member" attribute of the
      225 + * current group.  Note that this list is ONLY one that can be
      226 + * assumed to be non-empty.  The problem here is that this list
      227 + * contains the list of members as "distinguished names" (DN),
      228 + * and we want the Unix names (known here as "uid").  We must
      229 + * lookup the "uid" for each DN in the member list.  Example:
      230 + * CN=Doe\, John,OU=Users,DC=contoso,DC=com => john.doe
      231 + */
      232 +static int
      233 +getmembers_DN(char **bufpp, int *lenp, ns_ldap_attr_t *members)
      234 +{
      235 +        ns_ldap_error_t *error = NULL;
      236 +        char    *member_dn, *member_uid;
      237 +        char    *buffer;
      238 +        int     buflen;
      239 +        int     i, len;
      240 +        int     nss_result = 0;
      241 +        int     firsttime;
      242 +
      243 +        buffer = *bufpp;
      244 +        buflen = *lenp;
      245 +        firsttime = (buffer[-1] == ':');
      246 +
      247 +        for (i = 0; i < members->value_count; i++) {
      248 +                member_dn = members->attrvalue[i];
      249 +                if (member_dn == NULL)
      250 +                        goto out;
      251 +
      252 +                /*
      253 +                 * The attribute name was "member", so these should be
      254 +                 * full distinguisned names (DNs).  We need to loookup
      255 +                 * the Unix UID (name) for each.
      256 +                 */
      257 +#ifdef DEBUG
      258 +                (void) fprintf(stdout, "getmembers_DN: dn=%s\n",
      259 +                    member_dn);
      260 +#endif
      261 +                if (member_dn[0] == '\0')
      262 +                        continue;
      263 +
      264 +                nss_result = __ns_ldap_dn2uid(member_dn,
      265 +                    &member_uid, NULL, &error);
      266 +                if (nss_result != NS_LDAP_SUCCESS) {
      267 +                        (void) __ns_ldap_freeError(&error);
      268 +                        error = NULL;
      269 +                        continue;
      270 +                }
      271 +#ifdef DEBUG
      272 +                (void) fprintf(stdout, "getmembers_DN: uid=<%s>\n",
      273 +                    member_uid);
      274 +#endif
      275 +                /* Skip invalid names. */
      276 +                if (member_uid[0] == '\0' ||
      277 +                    strpbrk(member_uid, " ,:=") != NULL) {
      278 +                        free(member_uid);
      279 +                        continue;
      280 +                }
      281 +
      282 +                if (firsttime)
      283 +                        len = snprintf(buffer, buflen, "%s", member_uid);
      284 +                else
      285 +                        len = snprintf(buffer, buflen, ",%s", member_uid);
      286 +                free(member_uid);
      287 +                TEST_AND_ADJUST(len, buffer, buflen, out);
      288 +        }
      289 +
      290 +out:
      291 +        *bufpp = buffer;
      292 +        *lenp = buflen;
      293 +        return (nss_result);
      294 +}
      295 +
      296 +/*
 200  297   * getbynam gets a group entry by name. This function constructs an ldap
 201  298   * search filter using the name invocation parameter and the getgrnam search
 202  299   * filter defined. Once the filter is constructed, we searche for a matching
 203  300   * entry and marshal the data results into struct group for the frontend
 204  301   * process. The function _nss_ldap_group2ent performs the data marshaling.
 205  302   */
 206  303  
 207  304  static nss_status_t
 208  305  getbynam(ldap_backend_ptr be, void *a)
 209  306  {
↓ open down ↓ 50 lines elided ↑ open up ↑
 260  357          if (ret >= sizeof (userdata) || ret < 0)
 261  358                  return ((nss_status_t)NSS_NOTFOUND);
 262  359  
 263  360          return ((nss_status_t)_nss_ldap_lookup(be, argp,
 264  361              _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata));
 265  362  
 266  363  }
 267  364  
 268  365  
 269  366  /*
      367 + * Use a custom attributes list for getbymember, because the LDAP
      368 + * query for this requests a list of groups, and the result can be
      369 + * very large if it includes the list of members with each group.
      370 + * We don't need or want the list of members in this case.
      371 + */
      372 +static const char *grbymem_attrs[] = {
      373 +        _G_NAME,        /* cn */
      374 +        _G_GID,         /* gidnumber */
      375 +        (char *)NULL
      376 +};
      377 +
      378 +/*
 270  379   * getbymember returns all groups a user is defined in. This function
 271  380   * uses different architectural procedures than the other group backend
 272  381   * system calls because it's a private interface. This function constructs
 273  382   * an ldap search filter using the name invocation parameter. Once the
 274  383   * filter is constructed, we search for all matching groups counting
 275  384   * and storing each group name, gid, etc. Data marshaling is used for
 276  385   * group processing. The function _nss_ldap_group2ent() performs the
 277  386   * data marshaling.
 278  387   *
 279  388   * (const char *)argp->username;        (size_t)strlen(argp->username);
 280  389   * (gid_t)argp->gid_array;              (int)argp->maxgids;
 281  390   * (int)argp->numgids;
 282  391   */
 283  392  
 284  393  static nss_status_t
 285  394  getbymember(ldap_backend_ptr be, void *a)
 286  395  {
      396 +        ns_ldap_error_t         *error = NULL;
 287  397          int                     i, j, k;
 288  398          int                     gcnt = (int)0;
 289      -        char                    **groupvalue, **membervalue, *member_str;
 290      -        char                    *strtok_state;
      399 +        char                    **groupvalue;
 291  400          nss_status_t            lstat;
 292  401          struct nss_groupsbymem  *argp = (struct nss_groupsbymem *)a;
 293  402          char                    searchfilter[SEARCHFILTERLEN];
 294  403          char                    userdata[SEARCHFILTERLEN];
 295  404          char                    name[SEARCHFILTERLEN];
      405 +        char                    escdn[SEARCHFILTERLEN];
 296  406          ns_ldap_result_t        *result;
 297  407          ns_ldap_entry_t         *curEntry;
 298      -        char                    *username, **dn_attr, *dn;
      408 +        char                    *dn;
 299  409          gid_t                   gid;
 300      -        int                     ret;
      410 +        int                     ret1, ret2;
 301  411  
 302  412          if (strcmp(argp->username, "") == 0 ||
 303  413              strcmp(argp->username, "root") == 0)
 304  414                  return ((nss_status_t)NSS_NOTFOUND);
 305  415  
 306  416          if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0)
 307  417                  return ((nss_status_t)NSS_NOTFOUND);
 308  418  
 309      -        ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETPWNAM, name);
 310      -        if (ret >= sizeof (searchfilter) || ret < 0)
 311      -                return ((nss_status_t)NSS_NOTFOUND);
 312      -
 313      -        ret = snprintf(userdata, sizeof (userdata), _F_GETPWNAM_SSD, name);
 314      -        if (ret >= sizeof (userdata) || ret < 0)
 315      -                return ((nss_status_t)NSS_NOTFOUND);
 316      -
 317  419          /*
 318  420           * Look up the user DN in ldap. If it's not found, search solely by
 319  421           * username.
 320  422           */
 321      -        lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL,
 322      -            _PASSWD, searchfilter, NULL, _merge_SSD_filter, userdata);
 323      -        if (lstat != (nss_status_t)NS_LDAP_SUCCESS)
 324      -                return ((nss_status_t)lstat);
 325      -
 326      -        if (be->result == NULL ||
 327      -            !(dn_attr = __ns_ldap_getAttr(be->result->entry, "dn")))
      423 +        lstat = __ns_ldap_uid2dn(name, &dn, NULL, &error);
      424 +        if (lstat != (nss_status_t)NS_LDAP_SUCCESS) {
      425 +                /* Can't get DN.  Use bare name */
      426 +                (void) __ns_ldap_freeError(&error);
 328  427                  dn = name;
 329      -        else
 330      -                dn = dn_attr[0];
      428 +        }
      429 +        /* Note: must free dn if != name */
 331  430  
 332      -        ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name,
 333      -            dn);
 334      -        if (ret >= sizeof (searchfilter) || ret < 0)
      431 +        /*
      432 +         * Compose filter patterns
      433 +         */
      434 +        ret1 = snprintf(searchfilter, sizeof (searchfilter),
      435 +            _F_GETGRMEM, name, dn);
      436 +        ret2 = snprintf(userdata, sizeof (userdata),
      437 +            _F_GETGRMEM_SSD, name, dn);
      438 +        if (dn != name)
      439 +                free(dn);
      440 +        if (ret1 >= sizeof (searchfilter) || ret1 < 0)
 335  441                  return ((nss_status_t)NSS_NOTFOUND);
 336      -
 337      -        ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name,
 338      -            dn);
 339      -        if (ret >= sizeof (userdata) || ret < 0)
      442 +        if (ret2 >= sizeof (userdata) || ret2 < 0)
 340  443                  return ((nss_status_t)NSS_NOTFOUND);
 341  444  
 342  445          /*
 343      -         * Free up resources from user DN search before performing group
 344      -         * search.
      446 +         * Query for groups matching the filter.
 345  447           */
 346      -        (void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result);
 347      -
 348      -        gcnt = (int)argp->numgids;
 349  448          lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL,
 350      -            _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata);
      449 +            _GROUP, searchfilter, grbymem_attrs,
      450 +            _merge_SSD_filter, userdata);
 351  451          if (lstat != (nss_status_t)NS_LDAP_SUCCESS)
 352  452                  return ((nss_status_t)lstat);
 353  453          if (be->result == NULL)
 354  454                  return (NSS_NOTFOUND);
 355      -        username = (char *)argp->username;
      455 +
      456 +        /*
      457 +         * Walk the query result, collecting GIDs.
      458 +         */
 356  459          result = (ns_ldap_result_t *)be->result;
 357  460          curEntry = (ns_ldap_entry_t *)result->entry;
 358      -        for (i = 0; i < result->entries_count && curEntry != NULL; i++) {
 359      -                membervalue = __ns_ldap_getAttr(curEntry, "memberUid");
 360      -                if (membervalue == NULL) {
 361      -                        curEntry = curEntry->next;
 362      -                        continue;
      461 +        gcnt = (int)argp->numgids;
      462 +        for (i = 0; i < result->entries_count; i++) {
      463 +
      464 +                /*
      465 +                 * Does this group have a gidNumber attr?
      466 +                 */
      467 +                groupvalue = __ns_ldap_getAttr(curEntry, _G_GID);
      468 +                if (groupvalue == NULL || groupvalue[0] == NULL) {
      469 +                        /* Drop this group from the list */
      470 +                        goto next_group;
 363  471                  }
 364      -                for (j = 0; membervalue[j]; j++) {
 365      -                        /*
 366      -                         * If we find an '=' in the member attribute
 367      -                         * value, treat it as a DN, otherwise as a
 368      -                         * username.
 369      -                         */
 370      -                        if (member_str = strchr(membervalue[j], '=')) {
 371      -                                member_str++; /* skip over the '=' */
 372      -                                member_str = strtok_r(member_str, ",",
 373      -                                    &strtok_state);
 374      -                        } else {
 375      -                                member_str = membervalue[j];
 376      -                        }
 377      -                        if (member_str != NULL &&
 378      -                            strcmp(member_str, username) == 0) {
 379      -                                groupvalue = __ns_ldap_getAttr(curEntry,
 380      -                                    "gidnumber");
 381      -                                if (groupvalue == NULL ||
 382      -                                    groupvalue[0] == NULL) {
 383      -                                        /* Drop this group from the list */
 384      -                                        break;
 385      -                                }
 386      -                                errno = 0;
 387      -                                gid = (gid_t)strtol(groupvalue[0],
 388      -                                    (char **)NULL, 10);
 389  472  
 390      -                                if (errno == 0 &&
 391      -                                    argp->numgids < argp->maxgids) {
 392      -                                        for (k = 0; k < argp->numgids; k++) {
 393      -                                                if (argp->gid_array[k] == gid)
 394      -                                                        /* already exists */
 395      -                                                        break;
 396      -                                        }
 397      -                                        if (k == argp->numgids)
 398      -                                                argp->gid_array[argp->numgids++]
 399      -                                                    = gid;
      473 +                /*
      474 +                 * Convert it to a numeric GID
      475 +                 */
      476 +                errno = 0;
      477 +                gid = (gid_t)strtol(groupvalue[0], (char **)NULL, 10);
      478 +                if (errno != 0)
      479 +                        goto next_group;
      480 +
      481 +                /*
      482 +                 * If we don't already have this GID, add it.
      483 +                 */
      484 +                if (argp->numgids < argp->maxgids) {
      485 +                        for (k = 0; k < argp->numgids; k++) {
      486 +                                if (argp->gid_array[k] == gid) {
      487 +                                        /* already have it */
      488 +                                        goto next_group;
 400  489                                  }
 401      -                                break;
 402  490                          }
      491 +                        argp->gid_array[argp->numgids++] = gid;
 403  492                  }
      493 +
      494 +        next_group:
 404  495                  curEntry = curEntry->next;
 405  496          }
 406  497  
 407  498          (void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result);
 408  499          if (gcnt == argp->numgids)
 409  500                  return ((nss_status_t)NSS_NOTFOUND);
 410  501  
 411  502          /*
 412  503           * Return NSS_SUCCESS only if array is full.
 413  504           * Explained in <nss_dbdefs.h>.
↓ open down ↓ 10 lines elided ↑ open up ↑
 424  515          _nss_ldap_getent,
 425  516          getbynam,
 426  517          getbygid,
 427  518          getbymember
 428  519  };
 429  520  
 430  521  
 431  522  /*ARGSUSED0*/
 432  523  nss_backend_t *
 433  524  _nss_ldap_group_constr(const char *dummy1, const char *dummy2,
 434      -    const char *dummy3)
      525 +                        const char *dummy3)
 435  526  {
 436  527  
 437  528          return ((nss_backend_t *)_nss_ldap_constr(gr_ops,
 438  529              sizeof (gr_ops)/sizeof (gr_ops[0]), _GROUP, gr_attrs,
 439  530              _nss_ldap_group2str));
 440  531  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX