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 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, err2; 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 (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER, 256 U8_UNICODE_LATEST, &err2) != 0 || err2 != 0) 257 goto out; 258 } 259 260 /* Copy the DN and attr and value */ 261 if (q->dn != NULL) 262 *q->dn = strdup(dn); 263 264 if (q->attr != NULL && attr != NULL) 265 *q->attr = strdup(attr); 266 267 if (q->value != NULL && unixname != NULL) 268 *q->value = strdup(unixname); 269 270 /* Set results */ 271 if (q->sid) { 272 *q->sid = sid; 273 sid = NULL; 274 } 275 if (q->rid) 276 *q->rid = rid; 277 if (q->sid_type) 278 *q->sid_type = sid_type; 279 if (q->unixname) { 280 *q->unixname = unixname; 281 unixname = NULL; 282 } 283 if (q->domain != NULL) { 284 *q->domain = domain; 285 domain = NULL; 286 } 287 if (q->canonname != NULL) { 288 /* 289 * The caller may be replacing the given winname by its 290 * canonical name and therefore free any old name before 291 * overwriting the field by the canonical name. 292 */ 293 free(*q->canonname); 294 *q->canonname = san; 295 san = NULL; 296 } 297 298 q->ad_rc = ADUTILS_SUCCESS; 299 300 out: 301 /* Free unused attribute values */ 302 free(san); 303 free(sid); 304 free(domain); 305 free(unixname); 306 } 307 308 #define BVAL_CASEEQ(bv, str) \ 309 (((*(bv))->bv_len == (sizeof (str) - 1)) && \ 310 strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0) 311 312 /* 313 * Extract the class of the result entry. Returns 1 on success, 0 on 314 * failure. 315 */ 316 static 317 int 318 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type) 319 { 320 BerValue **cbval; 321 322 *sid_type = _IDMAP_T_OTHER; 323 if (bvalues == NULL) 324 return (0); 325 326 /* 327 * We iterate over all the values because computer is a 328 * sub-class of user. 329 */ 330 for (cbval = bvalues; *cbval != NULL; cbval++) { 331 if (BVAL_CASEEQ(cbval, "Computer")) { 332 *sid_type = _IDMAP_T_COMPUTER; 333 break; 334 } else if (BVAL_CASEEQ(cbval, "Group")) { 335 *sid_type = _IDMAP_T_GROUP; 336 break; 337 } else if (BVAL_CASEEQ(cbval, "USER")) { 338 *sid_type = _IDMAP_T_USER; 339 /* Continue looping -- this may be a computer yet */ 340 } 341 /* 342 * "else if (*sid_type = _IDMAP_T_USER)" then this is a 343 * new sub-class of user -- what to do with it?? 344 */ 345 } 346 347 return (1); 348 } 349 350 /* 351 * Handle a given search result entry 352 */ 353 static 354 void 355 idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q, 356 LDAPMessage *res, LDAP *ld) 357 { 358 BerElement *ber = NULL; 359 BerValue **bvalues; 360 char *attr; 361 const char *unixuser_attr = NULL; 362 const char *unixgroup_attr = NULL; 363 char *unixuser = NULL; 364 char *unixgroup = NULL; 365 char *dn = NULL; 366 char *san = NULL; 367 char *sid = NULL; 368 rid_t rid = 0; 369 int sid_type = _IDMAP_T_UNDEF; 370 int has_class, has_san, has_sid; 371 int has_unixuser, has_unixgroup; 372 373 assert(q->rc != NULL); 374 375 if ((dn = ldap_get_dn(ld, res)) == NULL) 376 return; 377 378 assert(q->domain == NULL || *q->domain == NULL); 379 380 /* 381 * If the caller has requested unixname then determine the 382 * AD attribute name that will have the unixname. 383 */ 384 if (q->unixname != NULL) { 385 if (q->eunixtype == _IDMAP_T_USER) 386 unixuser_attr = state->ad_unixuser_attr; 387 else if (q->eunixtype == _IDMAP_T_GROUP) 388 unixgroup_attr = state->ad_unixgroup_attr; 389 else if (q->eunixtype == _IDMAP_T_UNDEF) { 390 /* 391 * This is the case where we don't know 392 * before hand whether we need unixuser 393 * or unixgroup. This will be determined 394 * by the "sid_type" (i.e whether the given 395 * winname is user or group). If sid_type 396 * turns out to be user we will return 397 * unixuser (if found) and if it is a group 398 * we will return unixgroup (if found). We 399 * lookup for both ad_unixuser_attr and 400 * ad_unixgroup_attr and discard one of them 401 * after we know the "sidtype". This 402 * supports the following type of lookups. 403 * 404 * Example: 405 * $idmap show -c winname:foo 406 * In the above example, idmap will 407 * return uid if winname is winuser 408 * and gid if winname is wingroup. 409 */ 410 unixuser_attr = state->ad_unixuser_attr; 411 unixgroup_attr = state->ad_unixgroup_attr; 412 } 413 } 414 415 has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0; 416 for (attr = ldap_first_attribute(ld, res, &ber); attr != NULL; 417 attr = ldap_next_attribute(ld, res, ber)) { 418 bvalues = NULL; /* for memory management below */ 419 420 /* 421 * If this is an attribute we are looking for and 422 * haven't seen it yet, parse it 423 */ 424 if (q->sid != NULL && !has_sid && 425 strcasecmp(attr, OBJSID) == 0) { 426 bvalues = ldap_get_values_len(ld, res, attr); 427 if (bvalues != NULL) { 428 sid = adutils_bv_objsid2sidstr( 429 bvalues[0], &rid); 430 has_sid = (sid != NULL); 431 } 432 } else if (!has_san && strcasecmp(attr, SAN) == 0) { 433 bvalues = ldap_get_values_len(ld, res, attr); 434 if (bvalues != NULL) { 435 san = adutils_bv_name2str(bvalues[0]); 436 has_san = (san != NULL); 437 } 438 } else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) { 439 bvalues = ldap_get_values_len(ld, res, attr); 440 has_class = idmap_bv_objclass2sidtype(bvalues, 441 &sid_type); 442 if (has_class && q->unixname != NULL && 443 q->eunixtype == _IDMAP_T_UNDEF) { 444 /* 445 * This is the case where we didn't 446 * know whether we wanted unixuser or 447 * unixgroup as described above. 448 * Now since we know the "sid_type" 449 * we discard the unwanted value 450 * if it was retrieved before we 451 * got here. 452 */ 453 if (sid_type == _IDMAP_T_USER) { 454 free(unixgroup); 455 unixgroup_attr = unixgroup = NULL; 456 } else if (sid_type == _IDMAP_T_GROUP) { 457 free(unixuser); 458 unixuser_attr = unixuser = NULL; 459 } else { 460 free(unixuser); 461 free(unixgroup); 462 unixuser_attr = unixuser = NULL; 463 unixgroup_attr = unixgroup = NULL; 464 } 465 } 466 } else if (!has_unixuser && unixuser_attr != NULL && 467 strcasecmp(attr, unixuser_attr) == 0) { 468 bvalues = ldap_get_values_len(ld, res, attr); 469 if (bvalues != NULL) { 470 unixuser = adutils_bv_name2str(bvalues[0]); 471 has_unixuser = (unixuser != NULL); 472 } 473 474 } else if (!has_unixgroup && unixgroup_attr != NULL && 475 strcasecmp(attr, unixgroup_attr) == 0) { 476 bvalues = ldap_get_values_len(ld, res, attr); 477 if (bvalues != NULL) { 478 unixgroup = adutils_bv_name2str(bvalues[0]); 479 has_unixgroup = (unixgroup != NULL); 480 } 481 } 482 483 if (bvalues != NULL) 484 ldap_value_free_len(bvalues); 485 ldap_memfree(attr); 486 487 if (has_class && has_san && 488 (q->sid == NULL || has_sid) && 489 (unixuser_attr == NULL || has_unixuser) && 490 (unixgroup_attr == NULL || has_unixgroup)) { 491 /* Got what we need */ 492 break; 493 } 494 } 495 496 if (!has_class) { 497 /* 498 * Didn't find objectclass. Something's wrong with our 499 * AD data. 500 */ 501 free(san); 502 free(sid); 503 free(unixuser); 504 free(unixgroup); 505 } else { 506 /* 507 * Either we got what we needed and came out of the loop 508 * early OR we completed the loop in which case we didn't 509 * find some attributes that we were looking for. In either 510 * case set the result with what we got. 511 */ 512 idmap_setqresults(q, san, dn, 513 (unixuser != NULL) ? unixuser_attr : unixgroup_attr, 514 sid, rid, sid_type, 515 (unixuser != NULL) ? unixuser : unixgroup); 516 } 517 518 if (ber != NULL) 519 ber_free(ber, 0); 520 521 ldap_memfree(dn); 522 } 523 524 void 525 idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid, 526 void *argp) 527 { 528 idmap_query_state_t *state = (idmap_query_state_t *)argp; 529 idmap_q_t *q = &(state->queries[qid]); 530 531 switch (rc) { 532 case LDAP_RES_SEARCH_RESULT: 533 if (q->search_res != NULL) { 534 idmap_extract_object(state, q, q->search_res, ld); 535 (void) ldap_msgfree(q->search_res); 536 q->search_res = NULL; 537 } else 538 q->ad_rc = ADUTILS_ERR_NOTFOUND; 539 break; 540 case LDAP_RES_SEARCH_ENTRY: 541 if (q->search_res == NULL) { 542 q->search_res = *res; 543 *res = NULL; 544 } 545 break; 546 default: 547 break; 548 } 549 } 550 551 static 552 void 553 idmap_cleanup_batch(idmap_query_state_t *batch) 554 { 555 int i; 556 557 for (i = 0; i < batch->qcount; i++) { 558 if (batch->queries[i].ecanonname != NULL) 559 free(batch->queries[i].ecanonname); 560 batch->queries[i].ecanonname = NULL; 561 if (batch->queries[i].edomain != NULL) 562 free(batch->queries[i].edomain); 563 batch->queries[i].edomain = NULL; 564 } 565 } 566 567 /* 568 * This routine frees the idmap_query_state_t structure 569 */ 570 void 571 idmap_lookup_release_batch(idmap_query_state_t **state) 572 { 573 if (state == NULL || *state == NULL) 574 return; 575 adutils_lookup_batch_release(&(*state)->qs); 576 idmap_cleanup_batch(*state); 577 free(*state); 578 *state = NULL; 579 } 580 581 idmap_retcode 582 idmap_lookup_batch_end(idmap_query_state_t **state) 583 { 584 adutils_rc ad_rc; 585 int i; 586 idmap_query_state_t *id_qs = *state; 587 588 ad_rc = adutils_lookup_batch_end(&id_qs->qs); 589 590 /* 591 * Map adutils rc to idmap_retcode in each 592 * query because consumers in dbutils.c 593 * expects idmap_retcode. 594 */ 595 for (i = 0; i < id_qs->qcount; i++) { 596 *id_qs->queries[i].rc = 597 map_adrc2idmaprc(id_qs->queries[i].ad_rc); 598 } 599 idmap_lookup_release_batch(state); 600 return (map_adrc2idmaprc(ad_rc)); 601 } 602 603 /* 604 * Send one prepared search, queue up msgid, process what results are 605 * available 606 */ 607 static 608 idmap_retcode 609 idmap_batch_add1(idmap_query_state_t *state, const char *filter, 610 char *ecanonname, char *edomain, int eunixtype, 611 char **dn, char **attr, char **value, 612 char **canonname, char **dname, 613 char **sid, rid_t *rid, int *sid_type, char **unixname, 614 idmap_retcode *rc) 615 { 616 adutils_rc ad_rc; 617 int qid, i; 618 idmap_q_t *q; 619 static char *attrs[] = { 620 SAN, 621 OBJSID, 622 OBJCLASS, 623 NULL, /* placeholder for unixname attr */ 624 NULL, /* placeholder for unixname attr */ 625 NULL 626 }; 627 628 qid = atomic_inc_32_nv(&state->qcount) - 1; 629 q = &(state->queries[qid]); 630 631 assert(qid < state->qsize); 632 633 /* 634 * Remember the expected canonname, domainname and unix type 635 * so we can check the results * against it 636 */ 637 q->ecanonname = ecanonname; 638 q->edomain = edomain; 639 q->eunixtype = eunixtype; 640 641 /* Remember where to put the results */ 642 q->canonname = canonname; 643 q->sid = sid; 644 q->domain = dname; 645 q->rid = rid; 646 q->sid_type = sid_type; 647 q->rc = rc; 648 q->unixname = unixname; 649 q->dn = dn; 650 q->attr = attr; 651 q->value = value; 652 653 /* Add unixuser/unixgroup attribute names to the attrs list */ 654 if (unixname != NULL) { 655 i = 3; 656 if (eunixtype != _IDMAP_T_GROUP && 657 state->ad_unixuser_attr != NULL) 658 attrs[i++] = (char *)state->ad_unixuser_attr; 659 if (eunixtype != _IDMAP_T_USER && 660 state->ad_unixgroup_attr != NULL) 661 attrs[i] = (char *)state->ad_unixgroup_attr; 662 } 663 664 /* 665 * Provide sane defaults for the results in case we never hear 666 * back from the DS before closing the connection. 667 * 668 * In particular we default the result to indicate a retriable 669 * error. The first complete matching result entry will cause 670 * this to be set to IDMAP_SUCCESS, and the end of the results 671 * for this search will cause this to indicate "not found" if no 672 * result entries arrived or no complete ones matched the lookup 673 * we were doing. 674 */ 675 *rc = IDMAP_ERR_RETRIABLE_NET_ERR; 676 if (sid_type != NULL) 677 *sid_type = _IDMAP_T_OTHER; 678 if (sid != NULL) 679 *sid = NULL; 680 if (dname != NULL) 681 *dname = NULL; 682 if (rid != NULL) 683 *rid = 0; 684 if (dn != NULL) 685 *dn = NULL; 686 if (attr != NULL) 687 *attr = NULL; 688 if (value != NULL) 689 *value = NULL; 690 691 /* 692 * Don't set *canonname to NULL because it may be pointing to the 693 * given winname. Later on if we get a canonical name from AD the 694 * old name if any will be freed before assigning the new name. 695 */ 696 697 /* 698 * Invoke the mother of all APIs i.e. the adutils API 699 */ 700 ad_rc = adutils_lookup_batch_add(state->qs, filter, 701 (const char **)attrs, 702 edomain, &q->result, &q->ad_rc); 703 return (map_adrc2idmaprc(ad_rc)); 704 } 705 706 idmap_retcode 707 idmap_name2sid_batch_add1(idmap_query_state_t *state, 708 const char *name, const char *dname, int eunixtype, 709 char **dn, char **attr, char **value, 710 char **canonname, char **sid, rid_t *rid, 711 int *sid_type, char **unixname, idmap_retcode *rc) 712 { 713 idmap_retcode retcode; 714 int len, samAcctNameLen; 715 char *filter = NULL, *s_name; 716 char *ecanonname, *edomain; /* expected canonname */ 717 718 /* 719 * Strategy: search the global catalog for user/group by 720 * sAMAccountName = user/groupname with "" as the base DN and by 721 * userPrincipalName = user/groupname@domain. The result 722 * entries will be checked to conform to the name and domain 723 * name given here. The DN, sAMAccountName, userPrincipalName, 724 * objectSid and objectClass of the result entries are all we 725 * need to figure out which entries match the lookup, the SID of 726 * the user/group and whether it is a user or a group. 727 */ 728 729 /* 730 * We need the name and the domain name separately and as 731 * name@domain. We also allow the domain to be provided 732 * separately. 733 */ 734 samAcctNameLen = strlen(name); 735 736 if ((ecanonname = strdup(name)) == NULL) 737 return (IDMAP_ERR_MEMORY); 738 739 if (dname == NULL || *dname == '\0') { 740 if ((dname = strchr(name, '@')) != NULL) { 741 /* 'name' is qualified with a domain name */ 742 if ((edomain = strdup(dname + 1)) == NULL) { 743 free(ecanonname); 744 return (IDMAP_ERR_MEMORY); 745 } 746 *strchr(ecanonname, '@') = '\0'; 747 } else { 748 /* 'name' not qualified and dname not given */ 749 dname = adutils_lookup_batch_getdefdomain(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 if (!adutils_lookup_check_domain(state->qs, dname)) { 769 free(ecanonname); 770 free(edomain); 771 return (IDMAP_ERR_DOMAIN_NOTFOUND); 772 } 773 774 s_name = sanitize_for_ldap_filter(name); 775 if (s_name == NULL) { 776 free(ecanonname); 777 free(edomain); 778 return (IDMAP_ERR_MEMORY); 779 } 780 781 /* Assemble filter */ 782 len = snprintf(NULL, 0, SANFILTER, samAcctNameLen, s_name) + 1; 783 if ((filter = (char *)malloc(len)) == NULL) { 784 free(ecanonname); 785 free(edomain); 786 if (s_name != name) 787 free(s_name); 788 return (IDMAP_ERR_MEMORY); 789 } 790 (void) snprintf(filter, len, SANFILTER, samAcctNameLen, s_name); 791 if (s_name != name) 792 free(s_name); 793 794 retcode = idmap_batch_add1(state, filter, ecanonname, edomain, 795 eunixtype, dn, attr, value, canonname, NULL, sid, rid, sid_type, 796 unixname, rc); 797 798 free(filter); 799 800 return (retcode); 801 } 802 803 idmap_retcode 804 idmap_sid2name_batch_add1(idmap_query_state_t *state, 805 const char *sid, const rid_t *rid, int eunixtype, 806 char **dn, char **attr, char **value, 807 char **name, char **dname, int *sid_type, 808 char **unixname, idmap_retcode *rc) 809 { 810 idmap_retcode retcode; 811 int flen, ret; 812 char *filter = NULL; 813 char cbinsid[ADUTILS_MAXHEXBINSID + 1]; 814 815 /* 816 * Strategy: search [the global catalog] for user/group by 817 * objectSid = SID with empty base DN. The DN, sAMAccountName 818 * and objectClass of the result are all we need to figure out 819 * the name of the SID and whether it is a user, a group or a 820 * computer. 821 */ 822 823 if (!adutils_lookup_check_sid_prefix(state->qs, sid)) 824 return (IDMAP_ERR_DOMAIN_NOTFOUND); 825 826 ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); 827 if (ret != 0) 828 return (IDMAP_ERR_SID); 829 830 /* Assemble filter */ 831 flen = snprintf(NULL, 0, OBJSIDFILTER, cbinsid) + 1; 832 if ((filter = (char *)malloc(flen)) == NULL) 833 return (IDMAP_ERR_MEMORY); 834 (void) snprintf(filter, flen, OBJSIDFILTER, cbinsid); 835 836 retcode = idmap_batch_add1(state, filter, NULL, NULL, eunixtype, 837 dn, attr, value, name, dname, NULL, NULL, sid_type, unixname, rc); 838 839 free(filter); 840 841 return (retcode); 842 } 843 844 idmap_retcode 845 idmap_unixname2sid_batch_add1(idmap_query_state_t *state, 846 const char *unixname, int is_user, int is_wuser, 847 char **dn, char **attr, char **value, 848 char **sid, rid_t *rid, char **name, 849 char **dname, int *sid_type, idmap_retcode *rc) 850 { 851 idmap_retcode retcode; 852 int len, ulen; 853 char *filter = NULL, *s_unixname; 854 const char *attrname = NULL; 855 856 /* Get unixuser or unixgroup AD attribute name */ 857 attrname = (is_user) ? 858 state->ad_unixuser_attr : state->ad_unixgroup_attr; 859 if (attrname == NULL) 860 return (IDMAP_ERR_NOTFOUND); 861 862 s_unixname = sanitize_for_ldap_filter(unixname); 863 if (s_unixname == NULL) 864 return (IDMAP_ERR_MEMORY); 865 866 /* Assemble filter */ 867 ulen = strlen(unixname); 868 len = snprintf(NULL, 0, "(&(objectclass=%s)(%s=%.*s))", 869 is_wuser ? "user" : "group", attrname, ulen, s_unixname) + 1; 870 if ((filter = (char *)malloc(len)) == NULL) { 871 if (s_unixname != unixname) 872 free(s_unixname); 873 return (IDMAP_ERR_MEMORY); 874 } 875 (void) snprintf(filter, len, "(&(objectclass=%s)(%s=%.*s))", 876 is_wuser ? "user" : "group", attrname, ulen, s_unixname); 877 if (s_unixname != unixname) 878 free(s_unixname); 879 880 retcode = idmap_batch_add1(state, filter, NULL, NULL, 881 _IDMAP_T_UNDEF, dn, NULL, NULL, name, dname, sid, rid, sid_type, 882 NULL, rc); 883 884 if (retcode == IDMAP_SUCCESS && attr != NULL) { 885 if ((*attr = strdup(attrname)) == NULL) 886 retcode = IDMAP_ERR_MEMORY; 887 } 888 889 if (retcode == IDMAP_SUCCESS && value != NULL) { 890 if (ulen > 0) { 891 if ((*value = strdup(unixname)) == NULL) 892 retcode = IDMAP_ERR_MEMORY; 893 } 894 else 895 *value = NULL; 896 } 897 898 free(filter); 899 900 return (retcode); 901 } 902