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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Processes name2sid & sid2name batched lookups for a given user or 29 * computer from an AD Directory server using GSSAPI authentication 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <alloca.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <lber.h> 38 #include <ldap.h> 39 #include <sasl/sasl.h> 40 #include <string.h> 41 #include <ctype.h> 42 #include <pthread.h> 43 #include <synch.h> 44 #include <atomic.h> 45 #include <errno.h> 46 #include <assert.h> 47 #include <limits.h> 48 #include <time.h> 49 #include <sys/u8_textprep.h> 50 #include "libadutils.h" 51 #include "nldaputils.h" 52 #include "idmapd.h" 53 54 /* Attribute names and filter format strings */ 55 #define SAN "sAMAccountName" 56 #define OBJSID "objectSid" 57 #define OBJCLASS "objectClass" 58 #define SANFILTER "(sAMAccountName=%.*s)" 59 #define OBJSIDFILTER "(objectSid=%s)" 60 61 void idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, 62 int qid, void *argp); 63 64 /* 65 * A place to put the results of a batched (async) query 66 * 67 * There is one of these for every query added to a batch object 68 * (idmap_query_state, see below). 69 */ 70 typedef struct idmap_q { 71 /* 72 * data used for validating search result entries for name->SID 73 * lookups 74 */ 75 char *ecanonname; /* expected canon name */ 76 char *edomain; /* expected domain name */ 77 int eunixtype; /* expected unix type */ 78 /* results */ 79 char **canonname; /* actual canon name */ 80 char **domain; /* name of domain of object */ 81 char **sid; /* stringified SID */ 82 rid_t *rid; /* RID */ 83 int *sid_type; /* user or group SID? */ 84 char **unixname; /* unixname for name mapping */ 85 char **dn; /* DN of entry */ 86 char **attr; /* Attr for name mapping */ 87 char **value; /* value for name mapping */ 88 idmap_retcode *rc; 89 adutils_rc ad_rc; 90 adutils_result_t *result; 91 92 /* 93 * The LDAP search entry result is placed here to be processed 94 * when the search done result is received. 95 */ 96 LDAPMessage *search_res; /* The LDAP search result */ 97 } idmap_q_t; 98 99 /* Batch context structure; typedef is in header file */ 100 struct idmap_query_state { 101 adutils_query_state_t *qs; 102 int qsize; /* Queue size */ 103 uint32_t qcount; /* Number of queued requests */ 104 const char *ad_unixuser_attr; 105 const char *ad_unixgroup_attr; 106 idmap_q_t queries[1]; /* array of query results */ 107 }; 108 109 static pthread_t reaperid = 0; 110 111 /* 112 * Keep connection management simple for now, extend or replace later 113 * with updated libsldap code. 114 */ 115 #define ADREAPERSLEEP 60 116 117 /* 118 * Idle connection reaping side of connection management 119 * 120 * Every minute wake up and look for connections that have been idle for 121 * five minutes or more and close them. 122 */ 123 /*ARGSUSED*/ 124 static 125 void 126 adreaper(void *arg) 127 { 128 timespec_t ts; 129 130 ts.tv_sec = ADREAPERSLEEP; 131 ts.tv_nsec = 0; 132 133 for (;;) { 134 /* 135 * nanosleep(3RT) is thead-safe (no SIGALRM) and more 136 * portable than usleep(3C) 137 */ 138 (void) nanosleep(&ts, NULL); 139 adutils_reap_idle_connections(); 140 } 141 } 142 143 /* 144 * Take ad_host_config_t information, create a ad_host_t, 145 * populate it and add it to the list of hosts. 146 */ 147 148 int 149 idmap_add_ds(adutils_ad_t *ad, const char *host, int port) 150 { 151 int ret = -1; 152 153 if (adutils_add_ds(ad, host, port) == ADUTILS_SUCCESS) 154 ret = 0; 155 156 /* Start reaper if it doesn't exist */ 157 if (ret == 0 && reaperid == 0) 158 (void) pthread_create(&reaperid, NULL, 159 (void *(*)(void *))adreaper, (void *)NULL); 160 return (ret); 161 } 162 163 static 164 idmap_retcode 165 map_adrc2idmaprc(adutils_rc adrc) 166 { 167 switch (adrc) { 168 case ADUTILS_SUCCESS: 169 return (IDMAP_SUCCESS); 170 case ADUTILS_ERR_NOTFOUND: 171 return (IDMAP_ERR_NOTFOUND); 172 case ADUTILS_ERR_MEMORY: 173 return (IDMAP_ERR_MEMORY); 174 case ADUTILS_ERR_DOMAIN: 175 return (IDMAP_ERR_DOMAIN); 176 case ADUTILS_ERR_OTHER: 177 return (IDMAP_ERR_OTHER); 178 case ADUTILS_ERR_RETRIABLE_NET_ERR: 179 return (IDMAP_ERR_RETRIABLE_NET_ERR); 180 default: 181 return (IDMAP_ERR_INTERNAL); 182 } 183 /* NOTREACHED */ 184 } 185 186 idmap_retcode 187 idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries, 188 idmap_query_state_t **state) 189 { 190 idmap_query_state_t *new_state; 191 adutils_rc rc; 192 193 *state = NULL; 194 195 assert(ad != NULL); 196 197 new_state = calloc(1, sizeof (idmap_query_state_t) + 198 (nqueries - 1) * sizeof (idmap_q_t)); 199 if (new_state == NULL) 200 return (IDMAP_ERR_MEMORY); 201 202 if ((rc = adutils_lookup_batch_start(ad, nqueries, 203 idmap_ldap_res_search_cb, new_state, &new_state->qs)) 204 != ADUTILS_SUCCESS) { 205 free(new_state); 206 return (map_adrc2idmaprc(rc)); 207 } 208 209 new_state->qsize = nqueries; 210 *state = new_state; 211 return (IDMAP_SUCCESS); 212 } 213 214 /* 215 * Set unixuser_attr and unixgroup_attr for AD-based name mapping 216 */ 217 void 218 idmap_lookup_batch_set_unixattr(idmap_query_state_t *state, 219 const char *unixuser_attr, const char *unixgroup_attr) 220 { 221 state->ad_unixuser_attr = unixuser_attr; 222 state->ad_unixgroup_attr = unixgroup_attr; 223 } 224 225 /* 226 * Take parsed attribute values from a search result entry and check if 227 * it is the result that was desired and, if so, set the result fields 228 * of the given idmap_q_t. 229 * 230 * Frees the unused char * values. 231 */ 232 static 233 void 234 idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr, 235 char *sid, rid_t rid, int sid_type, char *unixname) 236 { 237 char *domain; 238 int err1; 239 240 assert(dn != NULL); 241 242 if ((domain = adutils_dn2dns(dn)) == NULL) 243 goto out; 244 245 if (q->ecanonname != NULL && san != NULL) { 246 /* Check that this is the canonname that we were looking for */ 247 if (u8_strcmp(q->ecanonname, san, 0, 248 U8_STRCMP_CI_LOWER, /* no normalization, for now */ 249 U8_UNICODE_LATEST, &err1) != 0 || err1 != 0) 250 goto out; 251 } 252 253 if (q->edomain != NULL) { 254 /* Check that this is the domain that we were looking for */ 255 if (!domain_eq(q->edomain, domain)) 256 goto out; 257 } 258 259 /* Copy the DN and attr and value */ 260 if (q->dn != NULL) 261 *q->dn = strdup(dn); 262 263 if (q->attr != NULL && attr != NULL) 264 *q->attr = strdup(attr); 265 266 if (q->value != NULL && unixname != NULL) 267 *q->value = strdup(unixname); 268 269 /* Set results */ 270 if (q->sid) { 271 *q->sid = sid; 272 sid = NULL; 273 } 274 if (q->rid) 275 *q->rid = rid; 276 if (q->sid_type) 277 *q->sid_type = sid_type; 278 if (q->unixname) { 279 *q->unixname = unixname; 280 unixname = NULL; 281 } 282 if (q->domain != NULL) { 283 *q->domain = domain; 284 domain = NULL; 285 } 286 if (q->canonname != NULL) { 287 /* 288 * The caller may be replacing the given winname by its 289 * canonical name and therefore free any old name before 290 * overwriting the field by the canonical name. 291 */ 292 free(*q->canonname); 293 *q->canonname = san; 294 san = NULL; 295 } 296 297 q->ad_rc = ADUTILS_SUCCESS; 298 299 out: 300 /* Free unused attribute values */ 301 free(san); 302 free(sid); 303 free(domain); 304 free(unixname); 305 } 306 307 #define BVAL_CASEEQ(bv, str) \ 308 (((*(bv))->bv_len == (sizeof (str) - 1)) && \ 309 strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0) 310 311 /* 312 * Extract the class of the result entry. Returns 1 on success, 0 on 313 * failure. 314 */ 315 static 316 int 317 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type) 318 { 319 BerValue **cbval; 320 321 *sid_type = _IDMAP_T_OTHER; 322 if (bvalues == NULL) 323 return (0); 324 325 /* 326 * We iterate over all the values because computer is a 327 * sub-class of user. 328 */ 329 for (cbval = bvalues; *cbval != NULL; cbval++) { 330 if (BVAL_CASEEQ(cbval, "Computer")) { 331 *sid_type = _IDMAP_T_COMPUTER; 332 break; 333 } else if (BVAL_CASEEQ(cbval, "Group")) { 334 *sid_type = _IDMAP_T_GROUP; 335 break; 336 } else if (BVAL_CASEEQ(cbval, "USER")) { 337 *sid_type = _IDMAP_T_USER; 338 /* Continue looping -- this may be a computer yet */ 339 } 340 /* 341 * "else if (*sid_type = _IDMAP_T_USER)" then this is a 342 * new sub-class of user -- what to do with it?? 343 */ 344 } 345 346 return (1); 347 } 348 349 /* 350 * Handle a given search result entry 351 */ 352 static 353 void 354 idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q, 355 LDAPMessage *res, LDAP *ld) 356 { 357 BerElement *ber = NULL; 358 BerValue **bvalues; 359 char *attr; 360 const char *unixuser_attr = NULL; 361 const char *unixgroup_attr = NULL; 362 char *unixuser = NULL; 363 char *unixgroup = NULL; 364 char *dn = NULL; 365 char *san = NULL; 366 char *sid = NULL; 367 rid_t rid = 0; 368 int sid_type = _IDMAP_T_UNDEF; 369 int has_class, has_san, has_sid; 370 int has_unixuser, has_unixgroup; 371 372 assert(q->rc != NULL); 373 374 if ((dn = ldap_get_dn(ld, res)) == NULL) 375 return; 376 377 assert(q->domain == NULL || *q->domain == NULL); 378 379 /* 380 * If the caller has requested unixname then determine the 381 * AD attribute name that will have the unixname. 382 */ 383 if (q->unixname != NULL) { 384 if (q->eunixtype == _IDMAP_T_USER) 385 unixuser_attr = state->ad_unixuser_attr; 386 else if (q->eunixtype == _IDMAP_T_GROUP) 387 unixgroup_attr = state->ad_unixgroup_attr; 388 else if (q->eunixtype == _IDMAP_T_UNDEF) { 389 /* 390 * This is the case where we don't know 391 * before hand whether we need unixuser 392 * or unixgroup. This will be determined 393 * by the "sid_type" (i.e whether the given 394 * winname is user or group). If sid_type 395 * turns out to be user we will return 396 * unixuser (if found) and if it is a group 397 * we will return unixgroup (if found). We 398 * lookup for both ad_unixuser_attr and 399 * ad_unixgroup_attr and discard one of them 400 * after we know the "sidtype". This 401 * supports the following type of lookups. 402 * 403 * Example: 404 * $idmap show -c winname:foo 405 * In the above example, idmap will 406 * return uid if winname is winuser 407 * and gid if winname is wingroup. 408 */ 409 unixuser_attr = state->ad_unixuser_attr; 410 unixgroup_attr = state->ad_unixgroup_attr; 411 } 412 } 413 414 has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0; 415 for (attr = ldap_first_attribute(ld, res, &ber); attr != NULL; 416 attr = ldap_next_attribute(ld, res, ber)) { 417 bvalues = NULL; /* for memory management below */ 418 419 /* 420 * If this is an attribute we are looking for and 421 * haven't seen it yet, parse it 422 */ 423 if (q->sid != NULL && !has_sid && 424 strcasecmp(attr, OBJSID) == 0) { 425 bvalues = ldap_get_values_len(ld, res, attr); 426 if (bvalues != NULL) { 427 sid = adutils_bv_objsid2sidstr( 428 bvalues[0], &rid); 429 has_sid = (sid != NULL); 430 } 431 } else if (!has_san && strcasecmp(attr, SAN) == 0) { 432 bvalues = ldap_get_values_len(ld, res, attr); 433 if (bvalues != NULL) { 434 san = adutils_bv_name2str(bvalues[0]); 435 has_san = (san != NULL); 436 } 437 } else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) { 438 bvalues = ldap_get_values_len(ld, res, attr); 439 has_class = idmap_bv_objclass2sidtype(bvalues, 440 &sid_type); 441 if (has_class && q->unixname != NULL && 442 q->eunixtype == _IDMAP_T_UNDEF) { 443 /* 444 * This is the case where we didn't 445 * know whether we wanted unixuser or 446 * unixgroup as described above. 447 * Now since we know the "sid_type" 448 * we discard the unwanted value 449 * if it was retrieved before we 450 * got here. 451 */ 452 if (sid_type == _IDMAP_T_USER) { 453 free(unixgroup); 454 unixgroup_attr = unixgroup = NULL; 455 } else if (sid_type == _IDMAP_T_GROUP) { 456 free(unixuser); 457 unixuser_attr = unixuser = NULL; 458 } else { 459 free(unixuser); 460 free(unixgroup); 461 unixuser_attr = unixuser = NULL; 462 unixgroup_attr = unixgroup = NULL; 463 } 464 } 465 } else if (!has_unixuser && unixuser_attr != NULL && 466 strcasecmp(attr, unixuser_attr) == 0) { 467 bvalues = ldap_get_values_len(ld, res, attr); 468 if (bvalues != NULL) { 469 unixuser = adutils_bv_name2str(bvalues[0]); 470 has_unixuser = (unixuser != NULL); 471 } 472 473 } else if (!has_unixgroup && unixgroup_attr != NULL && 474 strcasecmp(attr, unixgroup_attr) == 0) { 475 bvalues = ldap_get_values_len(ld, res, attr); 476 if (bvalues != NULL) { 477 unixgroup = adutils_bv_name2str(bvalues[0]); 478 has_unixgroup = (unixgroup != NULL); 479 } 480 } 481 482 if (bvalues != NULL) 483 ldap_value_free_len(bvalues); 484 ldap_memfree(attr); 485 486 if (has_class && has_san && 487 (q->sid == NULL || has_sid) && 488 (unixuser_attr == NULL || has_unixuser) && 489 (unixgroup_attr == NULL || has_unixgroup)) { 490 /* Got what we need */ 491 break; 492 } 493 } 494 495 if (!has_class) { 496 /* 497 * Didn't find objectclass. Something's wrong with our 498 * AD data. 499 */ 500 free(san); 501 free(sid); 502 free(unixuser); 503 free(unixgroup); 504 } else { 505 /* 506 * Either we got what we needed and came out of the loop 507 * early OR we completed the loop in which case we didn't 508 * find some attributes that we were looking for. In either 509 * case set the result with what we got. 510 */ 511 idmap_setqresults(q, san, dn, 512 (unixuser != NULL) ? unixuser_attr : unixgroup_attr, 513 sid, rid, sid_type, 514 (unixuser != NULL) ? unixuser : unixgroup); 515 } 516 517 if (ber != NULL) 518 ber_free(ber, 0); 519 520 ldap_memfree(dn); 521 } 522 523 void 524 idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid, 525 void *argp) 526 { 527 idmap_query_state_t *state = (idmap_query_state_t *)argp; 528 idmap_q_t *q = &(state->queries[qid]); 529 530 switch (rc) { 531 case LDAP_RES_SEARCH_RESULT: 532 if (q->search_res != NULL) { 533 idmap_extract_object(state, q, q->search_res, ld); 534 (void) ldap_msgfree(q->search_res); 535 q->search_res = NULL; 536 } else 537 q->ad_rc = ADUTILS_ERR_NOTFOUND; 538 break; 539 case LDAP_RES_SEARCH_ENTRY: 540 if (q->search_res == NULL) { 541 q->search_res = *res; 542 *res = NULL; 543 } 544 break; 545 default: 546 break; 547 } 548 } 549 550 static 551 void 552 idmap_cleanup_batch(idmap_query_state_t *batch) 553 { 554 int i; 555 556 for (i = 0; i < batch->qcount; i++) { 557 if (batch->queries[i].ecanonname != NULL) 558 free(batch->queries[i].ecanonname); 559 batch->queries[i].ecanonname = NULL; 560 if (batch->queries[i].edomain != NULL) 561 free(batch->queries[i].edomain); 562 batch->queries[i].edomain = NULL; 563 } 564 } 565 566 /* 567 * This routine frees the idmap_query_state_t structure 568 */ 569 void 570 idmap_lookup_release_batch(idmap_query_state_t **state) 571 { 572 if (state == NULL || *state == NULL) 573 return; 574 adutils_lookup_batch_release(&(*state)->qs); 575 idmap_cleanup_batch(*state); 576 free(*state); 577 *state = NULL; 578 } 579 580 idmap_retcode 581 idmap_lookup_batch_end(idmap_query_state_t **state) 582 { 583 adutils_rc ad_rc; 584 int i; 585 idmap_query_state_t *id_qs = *state; 586 587 ad_rc = adutils_lookup_batch_end(&id_qs->qs); 588 589 /* 590 * Map adutils rc to idmap_retcode in each 591 * query because consumers in dbutils.c 592 * expects idmap_retcode. 593 */ 594 for (i = 0; i < id_qs->qcount; i++) { 595 *id_qs->queries[i].rc = 596 map_adrc2idmaprc(id_qs->queries[i].ad_rc); 597 } 598 idmap_lookup_release_batch(state); 599 return (map_adrc2idmaprc(ad_rc)); 600 } 601 602 /* 603 * Send one prepared search, queue up msgid, process what results are 604 * available 605 */ 606 static 607 idmap_retcode 608 idmap_batch_add1(idmap_query_state_t *state, const char *filter, 609 char *ecanonname, char *edomain, int eunixtype, 610 char **dn, char **attr, char **value, 611 char **canonname, char **dname, 612 char **sid, rid_t *rid, int *sid_type, char **unixname, 613 idmap_retcode *rc) 614 { 615 adutils_rc ad_rc; 616 int qid, i; 617 idmap_q_t *q; 618 static char *attrs[] = { 619 SAN, 620 OBJSID, 621 OBJCLASS, 622 NULL, /* placeholder for unixname attr */ 623 NULL, /* placeholder for unixname attr */ 624 NULL 625 }; 626 627 qid = atomic_inc_32_nv(&state->qcount) - 1; 628 q = &(state->queries[qid]); 629 630 assert(qid < state->qsize); 631 632 /* 633 * Remember the expected canonname, domainname and unix type 634 * so we can check the results * against it 635 */ 636 q->ecanonname = ecanonname; 637 q->edomain = edomain; 638 q->eunixtype = eunixtype; 639 640 /* Remember where to put the results */ 641 q->canonname = canonname; 642 q->sid = sid; 643 q->domain = dname; 644 q->rid = rid; 645 q->sid_type = sid_type; 646 q->rc = rc; 647 q->unixname = unixname; 648 q->dn = dn; 649 q->attr = attr; 650 q->value = value; 651 652 /* Add unixuser/unixgroup attribute names to the attrs list */ 653 if (unixname != NULL) { 654 i = 3; 655 if (eunixtype != _IDMAP_T_GROUP && 656 state->ad_unixuser_attr != NULL) 657 attrs[i++] = (char *)state->ad_unixuser_attr; 658 if (eunixtype != _IDMAP_T_USER && 659 state->ad_unixgroup_attr != NULL) 660 attrs[i] = (char *)state->ad_unixgroup_attr; 661 } 662 663 /* 664 * Provide sane defaults for the results in case we never hear 665 * back from the DS before closing the connection. 666 * 667 * In particular we default the result to indicate a retriable 668 * error. The first complete matching result entry will cause 669 * this to be set to IDMAP_SUCCESS, and the end of the results 670 * for this search will cause this to indicate "not found" if no 671 * result entries arrived or no complete ones matched the lookup 672 * we were doing. 673 */ 674 *rc = IDMAP_ERR_RETRIABLE_NET_ERR; 675 if (sid_type != NULL) 676 *sid_type = _IDMAP_T_OTHER; 677 if (sid != NULL) 678 *sid = NULL; 679 if (dname != NULL) 680 *dname = NULL; 681 if (rid != NULL) 682 *rid = 0; 683 if (dn != NULL) 684 *dn = NULL; 685 if (attr != NULL) 686 *attr = NULL; 687 if (value != NULL) 688 *value = NULL; 689 690 /* 691 * Don't set *canonname to NULL because it may be pointing to the 692 * given winname. Later on if we get a canonical name from AD the 693 * old name if any will be freed before assigning the new name. 694 */ 695 696 /* 697 * Invoke the mother of all APIs i.e. the adutils API 698 */ 699 ad_rc = adutils_lookup_batch_add(state->qs, filter, 700 (const char **)attrs, 701 edomain, &q->result, &q->ad_rc); 702 return (map_adrc2idmaprc(ad_rc)); 703 } 704 705 idmap_retcode 706 idmap_name2sid_batch_add1(idmap_query_state_t *state, 707 const char *name, const char *dname, int eunixtype, 708 char **dn, char **attr, char **value, 709 char **canonname, char **sid, rid_t *rid, 710 int *sid_type, char **unixname, idmap_retcode *rc) 711 { 712 idmap_retcode retcode; 713 int len, samAcctNameLen; 714 char *filter = NULL, *s_name; 715 char *ecanonname, *edomain; /* expected canonname */ 716 717 /* 718 * Strategy: search the global catalog for user/group by 719 * sAMAccountName = user/groupname with "" as the base DN and by 720 * userPrincipalName = user/groupname@domain. The result 721 * entries will be checked to conform to the name and domain 722 * name given here. The DN, sAMAccountName, userPrincipalName, 723 * objectSid and objectClass of the result entries are all we 724 * need to figure out which entries match the lookup, the SID of 725 * the user/group and whether it is a user or a group. 726 */ 727 728 /* 729 * We need the name and the domain name separately and as 730 * name@domain. We also allow the domain to be provided 731 * separately. 732 */ 733 samAcctNameLen = strlen(name); 734 735 if ((ecanonname = strdup(name)) == NULL) 736 return (IDMAP_ERR_MEMORY); 737 738 if (dname == NULL || *dname == '\0') { 739 if ((dname = strchr(name, '@')) != NULL) { 740 /* 'name' is qualified with a domain name */ 741 if ((edomain = strdup(dname + 1)) == NULL) { 742 free(ecanonname); 743 return (IDMAP_ERR_MEMORY); 744 } 745 *strchr(ecanonname, '@') = '\0'; 746 } else { 747 /* 'name' not qualified and dname not given */ 748 dname = adutils_lookup_batch_getdefdomain(state->qs); 749 assert(dname != NULL); 750 if (*dname == '\0') { 751 free(ecanonname); 752 return (IDMAP_ERR_DOMAIN); 753 } 754 edomain = strdup(dname); 755 if (edomain == NULL) { 756 free(ecanonname); 757 return (IDMAP_ERR_MEMORY); 758 } 759 } 760 } else { 761 if ((edomain = strdup(dname)) == NULL) { 762 free(ecanonname); 763 return (IDMAP_ERR_MEMORY); 764 } 765 } 766 767 if (!adutils_lookup_check_domain(state->qs, dname)) { 768 free(ecanonname); 769 free(edomain); 770 return (IDMAP_ERR_DOMAIN_NOTFOUND); 771 } 772 773 s_name = sanitize_for_ldap_filter(name); 774 if (s_name == NULL) { 775 free(ecanonname); 776 free(edomain); 777 return (IDMAP_ERR_MEMORY); 778 } 779 780 /* Assemble filter */ 781 len = snprintf(NULL, 0, SANFILTER, samAcctNameLen, s_name) + 1; 782 if ((filter = (char *)malloc(len)) == NULL) { 783 free(ecanonname); 784 free(edomain); 785 if (s_name != name) 786 free(s_name); 787 return (IDMAP_ERR_MEMORY); 788 } 789 (void) snprintf(filter, len, SANFILTER, samAcctNameLen, s_name); 790 if (s_name != name) 791 free(s_name); 792 793 retcode = idmap_batch_add1(state, filter, ecanonname, edomain, 794 eunixtype, dn, attr, value, canonname, NULL, sid, rid, sid_type, 795 unixname, rc); 796 797 free(filter); 798 799 return (retcode); 800 } 801 802 idmap_retcode 803 idmap_sid2name_batch_add1(idmap_query_state_t *state, 804 const char *sid, const rid_t *rid, int eunixtype, 805 char **dn, char **attr, char **value, 806 char **name, char **dname, int *sid_type, 807 char **unixname, idmap_retcode *rc) 808 { 809 idmap_retcode retcode; 810 int flen, ret; 811 char *filter = NULL; 812 char cbinsid[ADUTILS_MAXHEXBINSID + 1]; 813 814 /* 815 * Strategy: search [the global catalog] for user/group by 816 * objectSid = SID with empty base DN. The DN, sAMAccountName 817 * and objectClass of the result are all we need to figure out 818 * the name of the SID and whether it is a user, a group or a 819 * computer. 820 */ 821 822 if (!adutils_lookup_check_sid_prefix(state->qs, sid)) 823 return (IDMAP_ERR_DOMAIN_NOTFOUND); 824 825 ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); 826 if (ret != 0) 827 return (IDMAP_ERR_SID); 828 829 /* Assemble filter */ 830 flen = snprintf(NULL, 0, OBJSIDFILTER, cbinsid) + 1; 831 if ((filter = (char *)malloc(flen)) == NULL) 832 return (IDMAP_ERR_MEMORY); 833 (void) snprintf(filter, flen, OBJSIDFILTER, cbinsid); 834 835 retcode = idmap_batch_add1(state, filter, NULL, NULL, eunixtype, 836 dn, attr, value, name, dname, NULL, NULL, sid_type, unixname, rc); 837 838 free(filter); 839 840 return (retcode); 841 } 842 843 idmap_retcode 844 idmap_unixname2sid_batch_add1(idmap_query_state_t *state, 845 const char *unixname, int is_user, int is_wuser, 846 char **dn, char **attr, char **value, 847 char **sid, rid_t *rid, char **name, 848 char **dname, int *sid_type, idmap_retcode *rc) 849 { 850 idmap_retcode retcode; 851 int len, ulen; 852 char *filter = NULL, *s_unixname; 853 const char *attrname = NULL; 854 855 /* Get unixuser or unixgroup AD attribute name */ 856 attrname = (is_user) ? 857 state->ad_unixuser_attr : state->ad_unixgroup_attr; 858 if (attrname == NULL) 859 return (IDMAP_ERR_NOTFOUND); 860 861 s_unixname = sanitize_for_ldap_filter(unixname); 862 if (s_unixname == NULL) 863 return (IDMAP_ERR_MEMORY); 864 865 /* Assemble filter */ 866 ulen = strlen(unixname); 867 len = snprintf(NULL, 0, "(&(objectclass=%s)(%s=%.*s))", 868 is_wuser ? "user" : "group", attrname, ulen, s_unixname) + 1; 869 if ((filter = (char *)malloc(len)) == NULL) { 870 if (s_unixname != unixname) 871 free(s_unixname); 872 return (IDMAP_ERR_MEMORY); 873 } 874 (void) snprintf(filter, len, "(&(objectclass=%s)(%s=%.*s))", 875 is_wuser ? "user" : "group", attrname, ulen, s_unixname); 876 if (s_unixname != unixname) 877 free(s_unixname); 878 879 retcode = idmap_batch_add1(state, filter, NULL, NULL, 880 _IDMAP_T_UNDEF, dn, NULL, NULL, name, dname, sid, rid, sid_type, 881 NULL, rc); 882 883 if (retcode == IDMAP_SUCCESS && attr != NULL) { 884 if ((*attr = strdup(attrname)) == NULL) 885 retcode = IDMAP_ERR_MEMORY; 886 } 887 888 if (retcode == IDMAP_SUCCESS && value != NULL) { 889 if (ulen > 0) { 890 if ((*value = strdup(unixname)) == NULL) 891 retcode = IDMAP_ERR_MEMORY; 892 } 893 else 894 *value = NULL; 895 } 896 897 free(filter); 898 899 return (retcode); 900 } 901