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 /* 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 qcount; /* how many queries */ 103 uint32_t qlastsent; 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 if (ad == NULL) 196 return (IDMAP_ERR_INTERNAL); 197 198 new_state = calloc(1, sizeof (idmap_query_state_t) + 199 (nqueries - 1) * sizeof (idmap_q_t)); 200 if (new_state == NULL) 201 return (IDMAP_ERR_MEMORY); 202 203 if ((rc = adutils_lookup_batch_start(ad, nqueries, 204 idmap_ldap_res_search_cb, new_state, &new_state->qs)) 205 != ADUTILS_SUCCESS) { 206 free(new_state); 207 return (map_adrc2idmaprc(rc)); 208 } 209 210 new_state->qcount = nqueries; 211 *state = new_state; 212 return (IDMAP_SUCCESS); 213 } 214 215 /* 216 * Set unixuser_attr and unixgroup_attr for AD-based name mapping 217 */ 218 void 219 idmap_lookup_batch_set_unixattr(idmap_query_state_t *state, 220 const char *unixuser_attr, const char *unixgroup_attr) 221 { 222 state->ad_unixuser_attr = unixuser_attr; 223 state->ad_unixgroup_attr = unixgroup_attr; 224 } 225 226 /* 227 * Take parsed attribute values from a search result entry and check if 228 * it is the result that was desired and, if so, set the result fields 229 * of the given idmap_q_t. 230 * 231 * Frees the unused char * values. 232 */ 233 static 234 void 235 idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr, 236 char *sid, rid_t rid, int sid_type, char *unixname) 237 { 238 char *domain; 239 int err1, err2; 240 241 assert(dn != NULL); 242 243 if ((domain = adutils_dn2dns(dn)) == NULL) 244 goto out; 245 246 if (q->ecanonname != NULL && san != NULL) { 247 /* Check that this is the canonname that we were looking for */ 248 if (u8_strcmp(q->ecanonname, san, 0, 249 U8_STRCMP_CI_LOWER, /* no normalization, for now */ 250 U8_UNICODE_LATEST, &err1) != 0 || err1 != 0) 251 goto out; 252 } 253 254 if (q->edomain != NULL) { 255 /* Check that this is the domain that we were looking for */ 256 if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER, 257 U8_UNICODE_LATEST, &err2) != 0 || err2 != 0) 258 goto out; 259 } 260 261 /* Copy the DN and attr and value */ 262 if (q->dn != NULL) 263 *q->dn = strdup(dn); 264 265 if (q->attr != NULL && attr != NULL) 266 *q->attr = strdup(attr); 267 268 if (q->value != NULL && unixname != NULL) 269 *q->value = strdup(unixname); 270 271 /* Set results */ 272 if (q->sid) { 273 *q->sid = sid; 274 sid = NULL; 275 } 276 if (q->rid) 277 *q->rid = rid; 278 if (q->sid_type) 279 *q->sid_type = sid_type; 280 if (q->unixname) { 281 *q->unixname = unixname; 282 unixname = NULL; 283 } 284 if (q->domain != NULL) { 285 *q->domain = domain; 286 domain = NULL; 287 } 288 if (q->canonname != NULL) { 289 /* 290 * The caller may be replacing the given winname by its 291 * canonical name and therefore free any old name before 292 * overwriting the field by the canonical name. 293 */ 294 free(*q->canonname); 295 *q->canonname = san; 296 san = NULL; 297 } 298 299 q->ad_rc = ADUTILS_SUCCESS; 300 301 out: 302 /* Free unused attribute values */ 303 free(san); 304 free(sid); 305 free(domain); 306 free(unixname); 307 } 308 309 #define BVAL_CASEEQ(bv, str) \ 310 (((*(bv))->bv_len == (sizeof (str) - 1)) && \ 311 strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0) 312 313 /* 314 * Extract the class of the result entry. Returns 1 on success, 0 on 315 * failure. 316 */ 317 static 318 int 319 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type) 320 { 321 BerValue **cbval; 322 323 *sid_type = _IDMAP_T_OTHER; 324 if (bvalues == NULL) 325 return (0); 326 327 /* 328 * We iterate over all the values because computer is a 329 * sub-class of user. 330 */ 331 for (cbval = bvalues; *cbval != NULL; cbval++) { 332 if (BVAL_CASEEQ(cbval, "Computer")) { 333 *sid_type = _IDMAP_T_COMPUTER; 334 break; 335 } else if (BVAL_CASEEQ(cbval, "Group")) { 336 *sid_type = _IDMAP_T_GROUP; 337 break; 338 } else if (BVAL_CASEEQ(cbval, "USER")) { 339 *sid_type = _IDMAP_T_USER; 340 /* Continue looping -- this may be a computer yet */ 341 } 342 /* 343 * "else if (*sid_type = _IDMAP_T_USER)" then this is a 344 * new sub-class of user -- what to do with it?? 345 */ 346 } 347 348 return (1); 349 } 350 351 /* 352 * Handle a given search result entry 353 */ 354 static 355 void 356 idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q, 357 LDAPMessage *res, LDAP *ld) 358 { 359 BerElement *ber = NULL; 360 BerValue **bvalues; 361 char *attr; 362 const char *unixuser_attr = NULL; 363 const char *unixgroup_attr = NULL; 364 char *unixuser = NULL; 365 char *unixgroup = NULL; 366 char *dn = NULL; 367 char *san = NULL; 368 char *sid = NULL; 369 rid_t rid = 0; 370 int sid_type = _IDMAP_T_UNDEF; 371 int has_class, has_san, has_sid; 372 int has_unixuser, has_unixgroup; 373 374 assert(q->rc != NULL); 375 376 if ((dn = ldap_get_dn(ld, res)) == NULL) 377 return; 378 379 assert(q->domain == NULL || *q->domain == NULL); 380 381 /* 382 * If the caller has requested unixname then determine the 383 * AD attribute name that will have the unixname. 384 */ 385 if (q->unixname != NULL) { 386 if (q->eunixtype == _IDMAP_T_USER) 387 unixuser_attr = state->ad_unixuser_attr; 388 else if (q->eunixtype == _IDMAP_T_GROUP) 389 unixgroup_attr = state->ad_unixgroup_attr; 390 else if (q->eunixtype == _IDMAP_T_UNDEF) { 391 /* 392 * This is the case where we don't know 393 * before hand whether we need unixuser 394 * or unixgroup. This will be determined 395 * by the "sid_type" (i.e whether the given 396 * winname is user or group). If sid_type 397 * turns out to be user we will return 398 * unixuser (if found) and if it is a group 399 * we will return unixgroup (if found). We 400 * lookup for both ad_unixuser_attr and 401 * ad_unixgroup_attr and discard one of them 402 * after we know the "sidtype". This 403 * supports the following type of lookups. 404 * 405 * Example: 406 * $idmap show -c winname:foo 407 * In the above example, idmap will 408 * return uid if winname is winuser 409 * and gid if winname is wingroup. 410 */ 411 unixuser_attr = state->ad_unixuser_attr; 412 unixgroup_attr = state->ad_unixgroup_attr; 413 } 414 } 415 416 has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0; 417 for (attr = ldap_first_attribute(ld, res, &ber); attr != NULL; 418 attr = ldap_next_attribute(ld, res, ber)) { 419 bvalues = NULL; /* for memory management below */ 420 421 /* 422 * If this is an attribute we are looking for and 423 * haven't seen it yet, parse it 424 */ 425 if (q->sid != NULL && !has_sid && 426 strcasecmp(attr, OBJSID) == 0) { 427 bvalues = ldap_get_values_len(ld, res, attr); 428 if (bvalues != NULL) { 429 sid = adutils_bv_objsid2sidstr( 430 bvalues[0], &rid); 431 has_sid = (sid != NULL); 432 } 433 } else if (!has_san && strcasecmp(attr, SAN) == 0) { 434 bvalues = ldap_get_values_len(ld, res, attr); 435 if (bvalues != NULL) { 436 san = adutils_bv_name2str(bvalues[0]); 437 has_san = (san != NULL); 438 } 439 } else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) { 440 bvalues = ldap_get_values_len(ld, res, attr); 441 has_class = idmap_bv_objclass2sidtype(bvalues, 442 &sid_type); 443 if (has_class && q->unixname != NULL && 444 q->eunixtype == _IDMAP_T_UNDEF) { 445 /* 446 * This is the case where we didn't 447 * know whether we wanted unixuser or 448 * unixgroup as described above. 449 * Now since we know the "sid_type" 450 * we discard the unwanted value 451 * if it was retrieved before we 452 * got here. 453 */ 454 if (sid_type == _IDMAP_T_USER) { 455 free(unixgroup); 456 unixgroup_attr = unixgroup = NULL; 457 } else if (sid_type == _IDMAP_T_GROUP) { 458 free(unixuser); 459 unixuser_attr = unixuser = NULL; 460 } else { 461 free(unixuser); 462 free(unixgroup); 463 unixuser_attr = unixuser = NULL; 464 unixgroup_attr = unixgroup = NULL; 465 } 466 } 467 } else if (!has_unixuser && unixuser_attr != NULL && 468 strcasecmp(attr, unixuser_attr) == 0) { 469 bvalues = ldap_get_values_len(ld, res, attr); 470 if (bvalues != NULL) { 471 unixuser = adutils_bv_name2str(bvalues[0]); 472 has_unixuser = (unixuser != NULL); 473 } 474 475 } else if (!has_unixgroup && unixgroup_attr != NULL && 476 strcasecmp(attr, unixgroup_attr) == 0) { 477 bvalues = ldap_get_values_len(ld, res, attr); 478 if (bvalues != NULL) { 479 unixgroup = adutils_bv_name2str(bvalues[0]); 480 has_unixgroup = (unixgroup != NULL); 481 } 482 } 483 484 if (bvalues != NULL) 485 ldap_value_free_len(bvalues); 486 ldap_memfree(attr); 487 488 if (has_class && has_san && 489 (q->sid == NULL || has_sid) && 490 (unixuser_attr == NULL || has_unixuser) && 491 (unixgroup_attr == NULL || has_unixgroup)) { 492 /* Got what we need */ 493 break; 494 } 495 } 496 497 if (!has_class) { 498 /* 499 * Didn't find objectclass. Something's wrong with our 500 * AD data. 501 */ 502 free(san); 503 free(sid); 504 free(unixuser); 505 free(unixgroup); 506 } else { 507 /* 508 * Either we got what we needed and came out of the loop 509 * early OR we completed the loop in which case we didn't 510 * find some attributes that we were looking for. In either 511 * case set the result with what we got. 512 */ 513 idmap_setqresults(q, san, dn, 514 (unixuser != NULL) ? unixuser_attr : unixgroup_attr, 515 sid, rid, sid_type, 516 (unixuser != NULL) ? unixuser : unixgroup); 517 } 518 519 if (ber != NULL) 520 ber_free(ber, 0); 521 522 ldap_memfree(dn); 523 } 524 525 void 526 idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid, 527 void *argp) 528 { 529 idmap_query_state_t *state = (idmap_query_state_t *)argp; 530 idmap_q_t *q = &(state->queries[qid]); 531 532 switch (rc) { 533 case LDAP_RES_SEARCH_RESULT: 534 if (q->search_res != NULL) { 535 idmap_extract_object(state, q, q->search_res, ld); 536 (void) ldap_msgfree(q->search_res); 537 q->search_res = NULL; 538 } else 539 q->ad_rc = ADUTILS_ERR_NOTFOUND; 540 break; 541 case LDAP_RES_SEARCH_ENTRY: 542 if (q->search_res == NULL) { 543 q->search_res = *res; 544 *res = NULL; 545 } 546 break; 547 default: 548 break; 549 } 550 } 551 552 static 553 void 554 idmap_cleanup_batch(idmap_query_state_t *batch) 555 { 556 int i; 557 558 for (i = 0; i < batch->qcount; i++) { 559 if (batch->queries[i].ecanonname != NULL) 560 free(batch->queries[i].ecanonname); 561 batch->queries[i].ecanonname = NULL; 562 if (batch->queries[i].edomain != NULL) 563 free(batch->queries[i].edomain); 564 batch->queries[i].edomain = NULL; 565 } 566 } 567 568 /* 569 * This routine frees the idmap_query_state_t structure 570 */ 571 void 572 idmap_lookup_release_batch(idmap_query_state_t **state) 573 { 574 if (state == NULL || *state == NULL) 575 return; 576 adutils_lookup_batch_release(&(*state)->qs); 577 idmap_cleanup_batch(*state); 578 free(*state); 579 *state = NULL; 580 } 581 582 idmap_retcode 583 idmap_lookup_batch_end(idmap_query_state_t **state) 584 { 585 adutils_rc ad_rc; 586 int i; 587 idmap_query_state_t *id_qs = *state; 588 589 ad_rc = adutils_lookup_batch_end(&id_qs->qs); 590 591 /* 592 * Map adutils rc to idmap_retcode in each 593 * query because consumers in dbutils.c 594 * expects idmap_retcode. 595 */ 596 for (i = 0; i < id_qs->qcount; i++) { 597 *id_qs->queries[i].rc = 598 map_adrc2idmaprc(id_qs->queries[i].ad_rc); 599 } 600 idmap_lookup_release_batch(state); 601 return (map_adrc2idmaprc(ad_rc)); 602 } 603 604 /* 605 * Send one prepared search, queue up msgid, process what results are 606 * available 607 */ 608 static 609 idmap_retcode 610 idmap_batch_add1(idmap_query_state_t *state, const char *filter, 611 char *ecanonname, char *edomain, int eunixtype, 612 char **dn, char **attr, char **value, 613 char **canonname, char **dname, 614 char **sid, rid_t *rid, int *sid_type, char **unixname, 615 idmap_retcode *rc) 616 { 617 adutils_rc ad_rc; 618 int qid, i; 619 idmap_q_t *q; 620 static char *attrs[] = { 621 SAN, 622 OBJSID, 623 OBJCLASS, 624 NULL, /* placeholder for unixname attr */ 625 NULL, /* placeholder for unixname attr */ 626 NULL 627 }; 628 629 qid = atomic_inc_32_nv(&state->qlastsent) - 1; 630 q = &(state->queries[qid]); 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( 749 state->qs); 750 assert(dname != NULL); 751 if (*dname == '\0') { 752 free(ecanonname); 753 return (IDMAP_ERR_DOMAIN); 754 } 755 edomain = strdup(dname); 756 if (edomain == NULL) { 757 free(ecanonname); 758 return (IDMAP_ERR_MEMORY); 759 } 760 } 761 } else { 762 if ((edomain = strdup(dname)) == NULL) { 763 free(ecanonname); 764 return (IDMAP_ERR_MEMORY); 765 } 766 } 767 768 s_name = sanitize_for_ldap_filter(name); 769 if (s_name == NULL) { 770 free(ecanonname); 771 free(edomain); 772 return (IDMAP_ERR_MEMORY); 773 } 774 775 /* Assemble filter */ 776 len = snprintf(NULL, 0, SANFILTER, samAcctNameLen, s_name) + 1; 777 if ((filter = (char *)malloc(len)) == NULL) { 778 free(ecanonname); 779 free(edomain); 780 if (s_name != name) 781 free(s_name); 782 return (IDMAP_ERR_MEMORY); 783 } 784 (void) snprintf(filter, len, SANFILTER, samAcctNameLen, s_name); 785 if (s_name != name) 786 free(s_name); 787 788 retcode = idmap_batch_add1(state, filter, ecanonname, edomain, 789 eunixtype, dn, attr, value, canonname, NULL, sid, rid, sid_type, 790 unixname, rc); 791 792 free(filter); 793 794 return (retcode); 795 } 796 797 idmap_retcode 798 idmap_sid2name_batch_add1(idmap_query_state_t *state, 799 const char *sid, const rid_t *rid, int eunixtype, 800 char **dn, char **attr, char **value, 801 char **name, char **dname, int *sid_type, 802 char **unixname, idmap_retcode *rc) 803 { 804 idmap_retcode retcode; 805 int flen, ret; 806 char *filter = NULL; 807 char cbinsid[ADUTILS_MAXHEXBINSID + 1]; 808 809 /* 810 * Strategy: search [the global catalog] for user/group by 811 * objectSid = SID with empty base DN. The DN, sAMAccountName 812 * and objectClass of the result are all we need to figure out 813 * the name of the SID and whether it is a user, a group or a 814 * computer. 815 */ 816 817 ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); 818 if (ret != 0) 819 return (IDMAP_ERR_SID); 820 821 /* Assemble filter */ 822 flen = snprintf(NULL, 0, OBJSIDFILTER, cbinsid) + 1; 823 if ((filter = (char *)malloc(flen)) == NULL) 824 return (IDMAP_ERR_MEMORY); 825 (void) snprintf(filter, flen, OBJSIDFILTER, cbinsid); 826 827 retcode = idmap_batch_add1(state, filter, NULL, NULL, eunixtype, 828 dn, attr, value, name, dname, NULL, NULL, sid_type, unixname, rc); 829 830 free(filter); 831 832 return (retcode); 833 } 834 835 idmap_retcode 836 idmap_unixname2sid_batch_add1(idmap_query_state_t *state, 837 const char *unixname, int is_user, int is_wuser, 838 char **dn, char **attr, char **value, 839 char **sid, rid_t *rid, char **name, 840 char **dname, int *sid_type, idmap_retcode *rc) 841 { 842 idmap_retcode retcode; 843 int len, ulen; 844 char *filter = NULL, *s_unixname; 845 const char *attrname = NULL; 846 847 /* Get unixuser or unixgroup AD attribute name */ 848 attrname = (is_user) ? 849 state->ad_unixuser_attr : state->ad_unixgroup_attr; 850 if (attrname == NULL) 851 return (IDMAP_ERR_NOTFOUND); 852 853 s_unixname = sanitize_for_ldap_filter(unixname); 854 if (s_unixname == NULL) 855 return (IDMAP_ERR_MEMORY); 856 857 /* Assemble filter */ 858 ulen = strlen(unixname); 859 len = snprintf(NULL, 0, "(&(objectclass=%s)(%s=%.*s))", 860 is_wuser ? "user" : "group", attrname, ulen, s_unixname) + 1; 861 if ((filter = (char *)malloc(len)) == NULL) { 862 if (s_unixname != unixname) 863 free(s_unixname); 864 return (IDMAP_ERR_MEMORY); 865 } 866 (void) snprintf(filter, len, "(&(objectclass=%s)(%s=%.*s))", 867 is_wuser ? "user" : "group", attrname, ulen, s_unixname); 868 if (s_unixname != unixname) 869 free(s_unixname); 870 871 retcode = idmap_batch_add1(state, filter, NULL, NULL, 872 _IDMAP_T_UNDEF, dn, NULL, NULL, name, dname, sid, rid, sid_type, 873 NULL, rc); 874 875 if (retcode == IDMAP_SUCCESS && attr != NULL) { 876 if ((*attr = strdup(attrname)) == NULL) 877 retcode = IDMAP_ERR_MEMORY; 878 } 879 880 if (retcode == IDMAP_SUCCESS && value != NULL) { 881 if (ulen > 0) { 882 if ((*value = strdup(unixname)) == NULL) 883 retcode = IDMAP_ERR_MEMORY; 884 } 885 else 886 *value = NULL; 887 } 888 889 free(filter); 890 891 return (retcode); 892 } 893