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