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