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