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