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