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 } 939