1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  25  */
  26 
  27 /*
  28  * native LDAP related utility routines
  29  */
  30 
  31 #include "idmapd.h"
  32 #include "idmap_priv.h"
  33 #include "ns_sldap.h"
  34 #include "nldaputils.h"
  35 #include <assert.h>
  36 
  37 /*
  38  * The following are format strings used to construct LDAP search filters
  39  * when looking up Native LDAP directory service. The _F_XXX_SSD format
  40  * is used by the libsldap API if a corresponding SSD is defined in
  41  * Native LDAP configuration. The SSD contains a string that replaces
  42  * the first %s in _F_XXX_SSD. If no SSD is defined then the regular
  43  * _F_XXX format is used.
  44  *
  45  * Note that '\\' needs to be represented as "\\5c" in LDAP filters.
  46  */
  47 
  48 /* Native LDAP lookup using UNIX username */
  49 #define _F_GETPWNAM             "(&(objectClass=posixAccount)(uid=%s))"
  50 #define _F_GETPWNAM_SSD         "(&(%%s)(uid=%s))"
  51 
  52 /*
  53  * Native LDAP user lookup using names of well-known SIDs
  54  * Note the use of 1$, 2$ in the format string which basically
  55  * allows snprintf to re-use its first two arguments.
  56  */
  57 #define _F_GETPWWNAMWK \
  58                 "(&(objectClass=posixAccount)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
  59 #define _F_GETPWWNAMWK_SSD      "(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
  60 
  61 /* Native LDAP user lookup using winname@windomain OR windomain\winname */
  62 #define _F_GETPWWNAMDOM \
  63         "(&(objectClass=posixAccount)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
  64 #define _F_GETPWWNAMDOM_SSD     "(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
  65 
  66 /* Native LDAP lookup using UID */
  67 #define _F_GETPWUID             "(&(objectClass=posixAccount)(uidNumber=%u))"
  68 #define _F_GETPWUID_SSD         "(&(%%s)(uidNumber=%u))"
  69 
  70 /* Native LDAP lookup using UNIX groupname */
  71 #define _F_GETGRNAM             "(&(objectClass=posixGroup)(cn=%s))"
  72 #define _F_GETGRNAM_SSD         "(&(%%s)(cn=%s))"
  73 
  74 /* Native LDAP group lookup using names of well-known SIDs */
  75 #define _F_GETGRWNAMWK \
  76                 "(&(objectClass=posixGroup)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
  77 #define _F_GETGRWNAMWK_SSD      "(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
  78 
  79 /* Native LDAP group lookup using winname@windomain OR windomain\winname */
  80 #define _F_GETGRWNAMDOM \
  81                 "(&(objectClass=posixGroup)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
  82 #define _F_GETGRWNAMDOM_SSD     "(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
  83 
  84 /* Native LDAP lookup using GID */
  85 #define _F_GETGRGID             "(&(objectClass=posixGroup)(gidNumber=%u))"
  86 #define _F_GETGRGID_SSD         "(&(%%s)(gidNumber=%u))"
  87 
  88 /* Native LDAP attribute names */
  89 #define UID                     "uid"
  90 #define CN                      "cn"
  91 #define UIDNUMBER               "uidnumber"
  92 #define GIDNUMBER               "gidnumber"
  93 #define DN                      "dn"
  94 
  95 #define IS_NLDAP_RC_FATAL(x)    ((x == NS_LDAP_MEMORY) ? 1 : 0)
  96 
  97 typedef struct idmap_nldap_q {
  98         char                    **winname;
  99         char                    **windomain;
 100         char                    **unixname;
 101         uid_t                   *pid;
 102         char                    **dn;
 103         char                    **attr;
 104         char                    **value;
 105         int                     is_user;
 106         idmap_retcode           *rc;
 107         int                     lrc;
 108         ns_ldap_result_t        *result;
 109         ns_ldap_error_t         *errorp;
 110         char                    *filter;
 111         char                    *udata;
 112 } idmap_nldap_q_t;
 113 
 114 typedef struct idmap_nldap_query_state {
 115         const char              *nldap_winname_attr;
 116         const char              *defdom;
 117         int                     nqueries;
 118         int                     qid;
 119         int                     flag;
 120         ns_ldap_list_batch_t    *batch;
 121         idmap_nldap_q_t         queries[1];
 122 } idmap_nldap_query_state_t;
 123 
 124 /*
 125  * This routine has been copied from lib/nsswitch/ldap/common/ldap_utils.c
 126  * after removing the debug statements.
 127  *
 128  * This is a generic filter callback function for merging the filter
 129  * from service search descriptor with an existing search filter. This
 130  * routine expects userdata to contain a format string with a single %s
 131  * in it, and will use the format string with sprintf() to insert the
 132  * SSD filter.
 133  *
 134  * This routine and userdata are passed to the __ns_ldap_list_batch_add()
 135  * API.
 136  *
 137  * Consider an example that uses __ns_ldap_list_batch_add() to lookup
 138  * native LDAP directory using a given userid 'xy12345'. In this
 139  * example the userdata will contain the filter "(&(%s)(cn=xy1234))".
 140  * If a SSD is defined to replace the rfc2307bis specified filter
 141  * i.e. (objectClass=posixAccount) by a site-specific filter
 142  * say (department=sds) then this routine when called will produce
 143  * "(&(department=sds)(uid=xy1234))" as the real search filter.
 144  */
 145 static
 146 int
 147 merge_SSD_filter(const ns_ldap_search_desc_t *desc,
 148         char **realfilter, const void *userdata)
 149 {
 150         int     len;
 151         char *checker;
 152 
 153         if (realfilter == NULL)
 154                 return (NS_LDAP_INVALID_PARAM);
 155         *realfilter = NULL;
 156         if (desc == NULL || desc->filter == NULL || userdata == NULL)
 157                 return (NS_LDAP_INVALID_PARAM);
 158 
 159         /* Parameter check.  We only want one %s here, otherwise bail. */
 160         len = 0;        /* Reuse 'len' as "Number of %s hits"... */
 161         checker = (char *)userdata;
 162         do {
 163                 checker = strchr(checker, '%');
 164                 if (checker != NULL) {
 165                         if (len > 0 || *(checker + 1) != 's')
 166                                 return (NS_LDAP_INVALID_PARAM);
 167                         len++;  /* Got our %s. */
 168                         checker += 2;
 169                 } else if (len != 1)
 170                         return (NS_LDAP_INVALID_PARAM);
 171         } while (checker != NULL);
 172 
 173         len = strlen(userdata) + strlen(desc->filter) + 1;
 174         *realfilter = (char *)malloc(len);
 175         if (*realfilter == NULL)
 176                 return (NS_LDAP_MEMORY);
 177         (void) sprintf(*realfilter, (char *)userdata, desc->filter);
 178         return (NS_LDAP_SUCCESS);
 179 }
 180 
 181 static
 182 char
 183 hex_char(int n)
 184 {
 185         return ("0123456789abcdef"[n & 0xf]);
 186 }
 187 
 188 /*
 189  * If the input string contains special characters that needs to be
 190  * escaped before the string can be used in a LDAP filter then this
 191  * function will return a new sanitized string. Otherwise this function
 192  * returns the input string (This saves us un-necessary memory allocations
 193  * especially when processing a batch of requests). The caller must free
 194  * the returned string if it isn't the input string.
 195  *
 196  * The escape mechanism for LDAP filter is described in RFC2254 basically
 197  * it's \hh where hh are the two hexadecimal digits representing the ASCII
 198  * value of the encoded character (case of hh is not significant).
 199  * Example: * -> \2a, ( -> \28, ) -> \29, \ -> \5c,
 200  *
 201  * outstring = sanitize_for_ldap_filter(instring);
 202  * if (outstring == NULL)
 203  *      Out of memory
 204  * else
 205  *      Use outstring
 206  *      if (outstring != instring)
 207  *              free(outstring);
 208  * done
 209  */
 210 char *
 211 sanitize_for_ldap_filter(const char *str)
 212 {
 213         const char      *p;
 214         char            *q, *s_str = NULL;
 215         int             n;
 216 
 217         /* Get a count of special characters */
 218         for (p = str, n = 0; *p; p++)
 219                 if (*p == '*' || *p == '(' || *p == ')' ||
 220                     *p == '\\' || *p == '%')
 221                         n++;
 222         /* If count is zero then no need to sanitize */
 223         if (n == 0)
 224                 return ((char *)str);
 225         /* Create output buffer that will contain the sanitized value */
 226         s_str = calloc(1, n * 2 + strlen(str) + 1);
 227         if (s_str == NULL)
 228                 return (NULL);
 229         for (p = str, q = s_str; *p; p++) {
 230                 if (*p == '*' || *p == '(' || *p == ')' ||
 231                     *p == '\\' || *p == '%') {
 232                         *q++ = '\\';
 233                         *q++ = hex_char(*p >> 4);
 234                         *q++ = hex_char(*p & 0xf);
 235                 } else
 236                         *q++ = *p;
 237         }
 238         return (s_str);
 239 }
 240 
 241 /*
 242  * Map libsldap status to idmap  status
 243  */
 244 static
 245 idmap_retcode
 246 nldaprc2retcode(int rc)
 247 {
 248         switch (rc) {
 249         case NS_LDAP_SUCCESS:
 250         case NS_LDAP_SUCCESS_WITH_INFO:
 251                 return (IDMAP_SUCCESS);
 252         case NS_LDAP_NOTFOUND:
 253                 return (IDMAP_ERR_NOTFOUND);
 254         case NS_LDAP_MEMORY:
 255                 return (IDMAP_ERR_MEMORY);
 256         case NS_LDAP_CONFIG:
 257                 return (IDMAP_ERR_NS_LDAP_CFG);
 258         case NS_LDAP_OP_FAILED:
 259                 return (IDMAP_ERR_NS_LDAP_OP_FAILED);
 260         case NS_LDAP_PARTIAL:
 261                 return (IDMAP_ERR_NS_LDAP_PARTIAL);
 262         case NS_LDAP_INTERNAL:
 263                 return (IDMAP_ERR_INTERNAL);
 264         case NS_LDAP_INVALID_PARAM:
 265                 return (IDMAP_ERR_ARG);
 266         default:
 267                 return (IDMAP_ERR_OTHER);
 268         }
 269         /*NOTREACHED*/
 270 }
 271 
 272 /*
 273  * Create a batch for native LDAP lookup.
 274  */
 275 static
 276 idmap_retcode
 277 idmap_nldap_lookup_batch_start(int nqueries, idmap_nldap_query_state_t **qs)
 278 {
 279         idmap_nldap_query_state_t       *s;
 280 
 281         s = calloc(1, sizeof (*s) +
 282             (nqueries - 1) * sizeof (idmap_nldap_q_t));
 283         if (s == NULL)
 284                 return (IDMAP_ERR_MEMORY);
 285         if (__ns_ldap_list_batch_start(&s->batch) != NS_LDAP_SUCCESS) {
 286                 free(s);
 287                 return (IDMAP_ERR_MEMORY);
 288         }
 289         s->nqueries = nqueries;
 290         s->flag = NS_LDAP_KEEP_CONN;
 291         *qs = s;
 292         return (IDMAP_SUCCESS);
 293 }
 294 
 295 /*
 296  * Add a lookup by winname request to the batch.
 297  */
 298 static
 299 idmap_retcode
 300 idmap_nldap_bywinname_batch_add(idmap_nldap_query_state_t *qs,
 301         const char *winname, const char *windomain, int is_user,
 302         char **dn, char **attr, char **value,
 303         char **unixname, uid_t *pid, idmap_retcode *rc)
 304 {
 305         idmap_nldap_q_t         *q;
 306         const char              *db, *filter, *udata;
 307         int                     flen, ulen, wksid = 0;
 308         char                    *s_winname, *s_windomain;
 309         const char              **attrs;
 310         const char              *pwd_attrs[] = {UID, UIDNUMBER, NULL, NULL};
 311         const char              *grp_attrs[] = {CN, GIDNUMBER, NULL, NULL};
 312 
 313         s_winname = s_windomain = NULL;
 314         q = &(qs->queries[qs->qid++]);
 315         q->unixname = unixname;
 316         q->pid = pid;
 317         q->rc = rc;
 318         q->is_user = is_user;
 319         q->dn = dn;
 320         q->attr = attr;
 321         q->value = value;
 322 
 323         if (is_user) {
 324                 db = "passwd";
 325                 if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
 326                     NULL, NULL) == IDMAP_SUCCESS) {
 327                         filter = _F_GETPWWNAMWK;
 328                         udata = _F_GETPWWNAMWK_SSD;
 329                         wksid = 1;
 330                 } else if (windomain != NULL) {
 331                         filter = _F_GETPWWNAMDOM;
 332                         udata = _F_GETPWWNAMDOM_SSD;
 333                 } else {
 334                         *q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
 335                         goto errout;
 336                 }
 337                 pwd_attrs[2] = qs->nldap_winname_attr;
 338                 attrs = pwd_attrs;
 339         } else {
 340                 db = "group";
 341                 if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
 342                     NULL, NULL) == IDMAP_SUCCESS) {
 343                         filter = _F_GETGRWNAMWK;
 344                         udata = _F_GETGRWNAMWK_SSD;
 345                         wksid = 1;
 346                 } else if (windomain != NULL) {
 347                         filter = _F_GETGRWNAMDOM;
 348                         udata = _F_GETGRWNAMDOM_SSD;
 349                 } else {
 350                         *q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
 351                         goto errout;
 352                 }
 353                 grp_attrs[2] = qs->nldap_winname_attr;
 354                 attrs = grp_attrs;
 355         }
 356 
 357         /*
 358          * Sanitize names. No need to sanitize qs->nldap_winname_attr
 359          * because if it contained any of the special characters then
 360          * it would have been rejected by the function that reads it
 361          * from the SMF config. LDAP attribute names can only contain
 362          * letters, digits or hyphens.
 363          */
 364         s_winname = sanitize_for_ldap_filter(winname);
 365         if (s_winname == NULL) {
 366                 *q->rc = IDMAP_ERR_MEMORY;
 367                 goto errout;
 368         }
 369         /* windomain could be NULL for names of well-known SIDs */
 370         if (windomain != NULL) {
 371                 s_windomain = sanitize_for_ldap_filter(windomain);
 372                 if (s_windomain == NULL) {
 373                         *q->rc = IDMAP_ERR_MEMORY;
 374                         goto errout;
 375                 }
 376         }
 377 
 378         /* Construct the filter and udata using snprintf. */
 379         if (wksid) {
 380                 flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
 381                     s_winname) + 1;
 382                 ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
 383                     s_winname) + 1;
 384         } else {
 385                 flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
 386                     s_winname, s_windomain) + 1;
 387                 ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
 388                     s_winname, s_windomain) + 1;
 389         }
 390 
 391         q->filter = malloc(flen);
 392         if (q->filter == NULL) {
 393                 *q->rc = IDMAP_ERR_MEMORY;
 394                 goto errout;
 395         }
 396         q->udata = malloc(ulen);
 397         if (q->udata == NULL) {
 398                 *q->rc = IDMAP_ERR_MEMORY;
 399                 goto errout;
 400         }
 401 
 402         if (wksid) {
 403                 (void) snprintf(q->filter, flen, filter,
 404                     qs->nldap_winname_attr, s_winname);
 405                 (void) snprintf(q->udata, ulen, udata,
 406                     qs->nldap_winname_attr, s_winname);
 407         } else {
 408                 (void) snprintf(q->filter, flen, filter,
 409                     qs->nldap_winname_attr, s_winname, s_windomain);
 410                 (void) snprintf(q->udata, ulen, udata,
 411                     qs->nldap_winname_attr, s_winname, s_windomain);
 412         }
 413 
 414         if (s_winname != winname)
 415                 free(s_winname);
 416         if (s_windomain != windomain)
 417                 free(s_windomain);
 418 
 419         q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
 420             merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
 421             &q->errorp, &q->lrc, NULL, q->udata);
 422 
 423         if (IS_NLDAP_RC_FATAL(q->lrc))
 424                 return (nldaprc2retcode(q->lrc));
 425         return (IDMAP_SUCCESS);
 426 
 427 errout:
 428         /* query q and its content will be freed by batch_release */
 429         if (s_winname != winname)
 430                 free(s_winname);
 431         if (s_windomain != windomain)
 432                 free(s_windomain);
 433         return (*q->rc);
 434 }
 435 
 436 /*
 437  * Add a lookup by uid/gid request to the batch.
 438  */
 439 static
 440 idmap_retcode
 441 idmap_nldap_bypid_batch_add(idmap_nldap_query_state_t *qs,
 442         uid_t pid, int is_user, char **dn, char **attr, char **value,
 443         char **winname, char **windomain,
 444         char **unixname, idmap_retcode *rc)
 445 {
 446         idmap_nldap_q_t         *q;
 447         const char              *db, *filter, *udata;
 448         int                     len;
 449         const char              **attrs;
 450         const char              *pwd_attrs[] = {UID, NULL, NULL};
 451         const char              *grp_attrs[] = {CN, NULL, NULL};
 452 
 453         q = &(qs->queries[qs->qid++]);
 454         q->winname = winname;
 455         q->windomain = windomain;
 456         q->unixname = unixname;
 457         q->rc = rc;
 458         q->is_user = is_user;
 459         q->dn = dn;
 460         q->attr = attr;
 461         q->value = value;
 462 
 463         if (is_user) {
 464                 db = "passwd";
 465                 filter = _F_GETPWUID;
 466                 udata = _F_GETPWUID_SSD;
 467                 pwd_attrs[1] = qs->nldap_winname_attr;
 468                 attrs = pwd_attrs;
 469         } else {
 470                 db = "group";
 471                 filter = _F_GETGRGID;
 472                 udata = _F_GETGRGID_SSD;
 473                 grp_attrs[1] = qs->nldap_winname_attr;
 474                 attrs = grp_attrs;
 475         }
 476 
 477         len = snprintf(NULL, 0, filter, pid) + 1;
 478         q->filter = malloc(len);
 479         if (q->filter == NULL) {
 480                 *q->rc = IDMAP_ERR_MEMORY;
 481                 return (IDMAP_ERR_MEMORY);
 482         }
 483         (void) snprintf(q->filter, len, filter, pid);
 484 
 485         len = snprintf(NULL, 0, udata, pid) + 1;
 486         q->udata = malloc(len);
 487         if (q->udata == NULL) {
 488                 *q->rc = IDMAP_ERR_MEMORY;
 489                 return (IDMAP_ERR_MEMORY);
 490         }
 491         (void) snprintf(q->udata, len, udata, pid);
 492 
 493         q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
 494             merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
 495             &q->errorp, &q->lrc, NULL, q->udata);
 496 
 497         if (IS_NLDAP_RC_FATAL(q->lrc))
 498                 return (nldaprc2retcode(q->lrc));
 499         return (IDMAP_SUCCESS);
 500 }
 501 
 502 /*
 503  * Add a lookup by user/group name request to the batch.
 504  */
 505 static
 506 idmap_retcode
 507 idmap_nldap_byunixname_batch_add(idmap_nldap_query_state_t *qs,
 508         const char *unixname, int is_user,
 509         char **dn, char **attr, char **value,
 510         char **winname, char **windomain, uid_t *pid, idmap_retcode *rc)
 511 {
 512         idmap_nldap_q_t         *q;
 513         const char              *db, *filter, *udata;
 514         int                     len;
 515         char                    *s_unixname = NULL;
 516         const char              **attrs;
 517         const char              *pwd_attrs[] = {UIDNUMBER, NULL, NULL};
 518         const char              *grp_attrs[] = {GIDNUMBER, NULL, NULL};
 519 
 520         q = &(qs->queries[qs->qid++]);
 521         q->winname = winname;
 522         q->windomain = windomain;
 523         q->pid = pid;
 524         q->rc = rc;
 525         q->is_user = is_user;
 526         q->dn = dn;
 527         q->attr = attr;
 528         q->value = value;
 529 
 530         if (is_user) {
 531                 db = "passwd";
 532                 filter = _F_GETPWNAM;
 533                 udata = _F_GETPWNAM_SSD;
 534                 pwd_attrs[1] = qs->nldap_winname_attr;
 535                 attrs = pwd_attrs;
 536         } else {
 537                 db = "group";
 538                 filter = _F_GETGRNAM;
 539                 udata = _F_GETGRNAM_SSD;
 540                 grp_attrs[1] = qs->nldap_winname_attr;
 541                 attrs = grp_attrs;
 542         }
 543 
 544         s_unixname = sanitize_for_ldap_filter(unixname);
 545         if (s_unixname == NULL) {
 546                 *q->rc = IDMAP_ERR_MEMORY;
 547                 return (IDMAP_ERR_MEMORY);
 548         }
 549 
 550         len = snprintf(NULL, 0, filter, s_unixname) + 1;
 551         q->filter = malloc(len);
 552         if (q->filter == NULL) {
 553                 if (s_unixname != unixname)
 554                         free(s_unixname);
 555                 *q->rc = IDMAP_ERR_MEMORY;
 556                 return (IDMAP_ERR_MEMORY);
 557         }
 558         (void) snprintf(q->filter, len, filter, s_unixname);
 559 
 560         len = snprintf(NULL, 0, udata, s_unixname) + 1;
 561         q->udata = malloc(len);
 562         if (q->udata == NULL) {
 563                 if (s_unixname != unixname)
 564                         free(s_unixname);
 565                 *q->rc = IDMAP_ERR_MEMORY;
 566                 return (IDMAP_ERR_MEMORY);
 567         }
 568         (void) snprintf(q->udata, len, udata, s_unixname);
 569 
 570         if (s_unixname != unixname)
 571                 free(s_unixname);
 572 
 573         q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
 574             merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
 575             &q->errorp, &q->lrc, NULL, q->udata);
 576 
 577         if (IS_NLDAP_RC_FATAL(q->lrc))
 578                 return (nldaprc2retcode(q->lrc));
 579         return (IDMAP_SUCCESS);
 580 }
 581 
 582 /*
 583  * Free the batch
 584  */
 585 static
 586 void
 587 idmap_nldap_lookup_batch_release(idmap_nldap_query_state_t *qs)
 588 {
 589         idmap_nldap_q_t         *q;
 590         int                     i;
 591 
 592         if (qs->batch != NULL)
 593                 (void) __ns_ldap_list_batch_release(qs->batch);
 594         for (i = 0; i < qs->qid; i++) {
 595                 q = &(qs->queries[i]);
 596                 free(q->filter);
 597                 free(q->udata);
 598                 if (q->errorp != NULL)
 599                         (void) __ns_ldap_freeError(&q->errorp);
 600                 if (q->result != NULL)
 601                         (void) __ns_ldap_freeResult(&q->result);
 602         }
 603         free(qs);
 604 }
 605 
 606 /*
 607  * Process all requests added to the batch and then free the batch.
 608  * The results for individual requests will be accessible using the
 609  * pointers passed during idmap_nldap_lookup_batch_end.
 610  */
 611 static
 612 idmap_retcode
 613 idmap_nldap_lookup_batch_end(idmap_nldap_query_state_t *qs)
 614 {
 615         idmap_nldap_q_t         *q;
 616         int                     i;
 617         ns_ldap_entry_t         *entry;
 618         char                    **val, *end, *str, *name, *dom;
 619         idmap_retcode           rc = IDMAP_SUCCESS;
 620 
 621         (void) __ns_ldap_list_batch_end(qs->batch);
 622         qs->batch = NULL;
 623         for (i = 0; i < qs->qid; i++) {
 624                 q = &(qs->queries[i]);
 625                 *q->rc = nldaprc2retcode(q->lrc);
 626                 if (*q->rc != IDMAP_SUCCESS)
 627                         continue;
 628                 if (q->result == NULL ||
 629                     !q->result->entries_count ||
 630                     (entry = q->result->entry) == NULL ||
 631                     !entry->attr_count) {
 632                         *q->rc = IDMAP_ERR_NOTFOUND;
 633                         continue;
 634                 }
 635                 /* Get uid/gid */
 636                 if (q->pid != NULL) {
 637                         val = __ns_ldap_getAttr(entry,
 638                             (q->is_user) ? UIDNUMBER : GIDNUMBER);
 639                         if (val != NULL && *val != NULL)
 640                                 *q->pid = strtoul(*val, &end, 10);
 641                 }
 642                 /* Get unixname */
 643                 if (q->unixname != NULL) {
 644                         val = __ns_ldap_getAttr(entry,
 645                             (q->is_user) ? UID : CN);
 646                         if (val != NULL && *val != NULL) {
 647                                 *q->unixname = strdup(*val);
 648                                 if (*q->unixname == NULL) {
 649                                         rc = *q->rc = IDMAP_ERR_MEMORY;
 650                                         goto out;
 651                                 }
 652                         }
 653                 }
 654                 /* Get DN for how info */
 655                 if (q->dn != NULL) {
 656                         val = __ns_ldap_getAttr(entry, DN);
 657                         if (val != NULL && *val != NULL) {
 658                                 *q->dn = strdup(*val);
 659                                 if (*q->dn == NULL) {
 660                                         rc = *q->rc = IDMAP_ERR_MEMORY;
 661                                         goto out;
 662                                 }
 663                         }
 664                 }
 665                 /* Get nldap name mapping attr name for how info */
 666                 if (q->attr != NULL) {
 667                         *q->attr = strdup(qs->nldap_winname_attr);
 668                         if (*q->attr == NULL) {
 669                                 rc = *q->rc = IDMAP_ERR_MEMORY;
 670                                 goto out;
 671                         }
 672                 }
 673                 /* Get nldap name mapping attr value for how info */
 674                 val =  __ns_ldap_getAttr(entry, qs->nldap_winname_attr);
 675                 if (val == NULL || *val == NULL)
 676                         continue;
 677                 if (q->value != NULL) {
 678                         *q->value = strdup(*val);
 679                         if (*q->value == NULL) {
 680                                 rc = *q->rc = IDMAP_ERR_MEMORY;
 681                                 goto out;
 682                         }
 683                 }
 684 
 685                 /* Get winname and windomain */
 686                 if (q->winname == NULL && q->windomain == NULL)
 687                         continue;
 688                 /*
 689                  * We need to split the value into winname and
 690                  * windomain. The value could be either in NT4
 691                  * style (i.e. dom\name) or AD-style (i.e. name@dom).
 692                  * We choose the first '\\' if it's in NT4 style and
 693                  * the last '@' if it's in AD-style for the split.
 694                  */
 695                 name = dom = NULL;
 696                 if (lookup_wksids_name2sid(*val, NULL, NULL, NULL, NULL, NULL,
 697                     NULL) == IDMAP_SUCCESS) {
 698                         name = *val;
 699                         dom = NULL;
 700                 } else if ((str = strchr(*val, '\\')) != NULL) {
 701                         *str = '\0';
 702                         name = str + 1;
 703                         dom = *val;
 704                 } else if ((str = strrchr(*val, '@')) != NULL) {
 705                         *str = '\0';
 706                         name = *val;
 707                         dom = str + 1;
 708                 } else {
 709                         idmapdlog(LOG_INFO, "Domain-less "
 710                             "winname (%s) found in Native LDAP", *val);
 711                         *q->rc = IDMAP_ERR_NS_LDAP_BAD_WINNAME;
 712                         continue;
 713                 }
 714                 if (q->winname != NULL) {
 715                         *q->winname = strdup(name);
 716                         if (*q->winname == NULL) {
 717                                 rc = *q->rc = IDMAP_ERR_MEMORY;
 718                                 goto out;
 719                         }
 720                 }
 721                 if (q->windomain != NULL && dom != NULL) {
 722                         *q->windomain = strdup(dom);
 723                         if (*q->windomain == NULL) {
 724                                 rc = *q->rc = IDMAP_ERR_MEMORY;
 725                                 goto out;
 726                         }
 727                 }
 728         }
 729 
 730 out:
 731         (void) idmap_nldap_lookup_batch_release(qs);
 732         return (rc);
 733 }
 734 
 735 /* ARGSUSED */
 736 idmap_retcode
 737 nldap_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch,
 738                 idmap_ids_res *result)
 739 {
 740         idmap_retcode                   retcode, rc1;
 741         int                             i, add;
 742         idmap_mapping                   *req;
 743         idmap_id_res                    *res;
 744         idmap_nldap_query_state_t       *qs = NULL;
 745         idmap_how                       *how;
 746 
 747         if (state->nldap_nqueries == 0)
 748                 return (IDMAP_SUCCESS);
 749 
 750         /* Create nldap lookup batch */
 751         retcode = idmap_nldap_lookup_batch_start(state->nldap_nqueries, &qs);
 752         if (retcode != IDMAP_SUCCESS) {
 753                 idmapdlog(LOG_ERR,
 754                     "Failed to create batch for native LDAP lookup");
 755                 goto out;
 756         }
 757 
 758         qs->nldap_winname_attr = state->nldap_winname_attr;
 759         qs->defdom = state->defdom;
 760 
 761         /* Add requests to the batch */
 762         for (i = 0, add = 0; i < batch->idmap_mapping_batch_len; i++) {
 763                 req = &batch->idmap_mapping_batch_val[i];
 764                 res = &result->ids.ids_val[i];
 765                 retcode = IDMAP_SUCCESS;
 766 
 767                 /* Skip if not marked for nldap lookup */
 768                 if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
 769                         continue;
 770 
 771                 if (IS_ID_SID(req->id1)) {
 772 
 773                         /* win2unix request: */
 774 
 775                         /*
 776                          * When processing a win2unix request, nldap lookup
 777                          * is performed after AD lookup or a successful
 778                          * name-cache lookup. Therefore we should already
 779                          * have sid, winname and sidtype. Note that
 780                          * windomain could be NULL e.g. well-known SIDs.
 781                          */
 782                         assert(req->id1name != NULL &&
 783                             (res->id.idtype == IDMAP_UID ||
 784                             res->id.idtype == IDMAP_GID));
 785 
 786                         /* Skip if we already have pid and unixname */
 787                         if (req->id2name != NULL &&
 788                             res->id.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
 789                                 res->retcode = IDMAP_SUCCESS;
 790                                 continue;
 791                         }
 792 
 793                         /* Clear leftover value */
 794                         free(req->id2name);
 795                         req->id2name = NULL;
 796 
 797                         /* Lookup nldap by winname to get pid and unixname */
 798                         add = 1;
 799                         idmap_how_clear(&res->info.how);
 800                         res->info.src = IDMAP_MAP_SRC_NEW;
 801                         how = &res->info.how;
 802                         how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
 803                         retcode = idmap_nldap_bywinname_batch_add(
 804                             qs, req->id1name, req->id1domain,
 805                             (res->id.idtype == IDMAP_UID) ? 1 : 0,
 806                             &how->idmap_how_u.nldap.dn,
 807                             &how->idmap_how_u.nldap.attr,
 808                             &how->idmap_how_u.nldap.value,
 809                             &req->id2name, &res->id.idmap_id_u.uid,
 810                             &res->retcode);
 811 
 812                 } else if (IS_ID_UID(req->id1) || IS_ID_GID(req->id1)) {
 813 
 814                         /* unix2win request: */
 815 
 816                         /* Skip if we already have winname */
 817                         if (req->id2name != NULL) {
 818                                 res->retcode = IDMAP_SUCCESS;
 819                                 continue;
 820                         }
 821 
 822                         /* Clear old value */
 823                         free(req->id2domain);
 824                         req->id2domain = NULL;
 825 
 826                         /* Set how info */
 827                         idmap_how_clear(&res->info.how);
 828                         res->info.src = IDMAP_MAP_SRC_NEW;
 829                         how = &res->info.how;
 830                         how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
 831 
 832                         /* Lookup nldap by pid or unixname to get winname */
 833                         if (req->id1.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
 834                                 add = 1;
 835                                 retcode = idmap_nldap_bypid_batch_add(
 836                                     qs, req->id1.idmap_id_u.uid,
 837                                     (req->id1.idtype == IDMAP_UID) ? 1 : 0,
 838                                     &how->idmap_how_u.nldap.dn,
 839                                     &how->idmap_how_u.nldap.attr,
 840                                     &how->idmap_how_u.nldap.value,
 841                                     &req->id2name, &req->id2domain,
 842                                     (req->id1name == NULL) ?
 843                                     &req->id1name : NULL,
 844                                     &res->retcode);
 845                         } else if (req->id1name != NULL) {
 846                                 add = 1;
 847                                 retcode = idmap_nldap_byunixname_batch_add(
 848                                     qs, req->id1name,
 849                                     (req->id1.idtype == IDMAP_UID) ? 1 : 0,
 850                                     &how->idmap_how_u.nldap.dn,
 851                                     &how->idmap_how_u.nldap.attr,
 852                                     &how->idmap_how_u.nldap.value,
 853                                     &req->id2name, &req->id2domain,
 854                                     &req->id1.idmap_id_u.uid, &res->retcode);
 855                         }
 856 
 857                 }
 858 
 859                 /*
 860                  * nldap_batch_add API returns error only on fatal failures
 861                  * otherwise it returns success and the actual status
 862                  * is stored in the individual request (res->retcode).
 863                  * Stop adding requests to this batch on fatal failures
 864                  * (i.e. if retcode != success)
 865                  */
 866                 if (retcode != IDMAP_SUCCESS)
 867                         break;
 868         }
 869 
 870         if (!add)
 871                 idmap_nldap_lookup_batch_release(qs);
 872         else if (retcode != IDMAP_SUCCESS)
 873                 idmap_nldap_lookup_batch_release(qs);
 874         else
 875                 retcode = idmap_nldap_lookup_batch_end(qs);
 876 
 877 out:
 878         for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
 879                 req = &batch->idmap_mapping_batch_val[i];
 880                 res = &result->ids.ids_val[i];
 881                 if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
 882                         continue;
 883 
 884                 /* Reset nldap flag */
 885                 req->direction &= ~(_IDMAP_F_LOOKUP_NLDAP);
 886 
 887                 /*
 888                  * As noted earlier retcode != success if there were fatal
 889                  * errors during batch_start and batch_adds. If so then set
 890                  * the status of each nldap request to that error.
 891                  */
 892                 if (retcode != IDMAP_SUCCESS) {
 893                         res->retcode = retcode;
 894                         continue;
 895                 }
 896                 if (!add)
 897                         continue;
 898 
 899                 /*
 900                  * If we successfully retrieved winname from nldap entry
 901                  * then lookup winname2sid locally. If not found locally
 902                  * then mark this request for AD lookup.
 903                  */
 904                 if (res->retcode == IDMAP_SUCCESS &&
 905                     req->id2name != NULL &&
 906                     res->id.idmap_id_u.sid.prefix == NULL &&
 907                     (IS_ID_UID(req->id1) || IS_ID_GID(req->id1))) {
 908 
 909                         rc1 = lookup_name2sid(state->cache,
 910                             req->id2name, req->id2domain, -1,
 911                             NULL, NULL,
 912                             &res->id.idmap_id_u.sid.prefix,
 913                             &res->id.idmap_id_u.sid.rid,
 914                             &res->id.idtype,
 915                             req, 1);
 916                         if (rc1 == IDMAP_ERR_NOTFOUND) {
 917                                 req->direction |= _IDMAP_F_LOOKUP_AD;
 918                                 state->ad_nqueries++;
 919                         } else
 920                                 res->retcode = rc1;
 921                 }
 922 
 923                 /*
 924                  * Unset non-fatal errors in individual request. This allows
 925                  * the next pass to process other mapping mechanisms for
 926                  * this request.
 927                  */
 928                 if (res->retcode != IDMAP_SUCCESS &&
 929                     res->retcode != IDMAP_ERR_NS_LDAP_BAD_WINNAME &&
 930                     !(IDMAP_FATAL_ERROR(res->retcode))) {
 931                         idmap_how_clear(&res->info.how);
 932                         res->retcode = IDMAP_SUCCESS;
 933                 }
 934         }
 935 
 936         state->nldap_nqueries = 0;
 937         return (retcode);
 938 }