1/* 2 Unix SMB/CIFS implementation. 3 ads (active directory) utility library 4 Copyright (C) Andrew Tridgell 2001 5 Copyright (C) Remus Koos 2001 6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21*/ 22 23#include "includes.h" 24 25#ifdef HAVE_LDAP 26 27/** 28 * @file ldap.c 29 * @brief basic ldap client-side routines for ads server communications 30 * 31 * The routines contained here should do the necessary ldap calls for 32 * ads setups. 33 * 34 * Important note: attribute names passed into ads_ routines must 35 * already be in UTF-8 format. We do not convert them because in almost 36 * all cases, they are just ascii (which is represented with the same 37 * codepoints in UTF-8). This may have to change at some point 38 **/ 39 40static SIG_ATOMIC_T gotalarm; 41 42/*************************************************************** 43 Signal function to tell us we timed out. 44****************************************************************/ 45 46static void gotalarm_sig(void) 47{ 48 gotalarm = 1; 49} 50 51 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to) 52{ 53 LDAP *ldp = NULL; 54 55 /* Setup timeout */ 56 gotalarm = 0; 57 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); 58 alarm(to); 59 /* End setup timeout. */ 60 61 ldp = ldap_open(server, port); 62 63 /* Teardown timeout. */ 64 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); 65 alarm(0); 66 67 return ldp; 68} 69 70static int ldap_search_with_timeout(LDAP *ld, 71 LDAP_CONST char *base, 72 int scope, 73 LDAP_CONST char *filter, 74 char **attrs, 75 int attrsonly, 76 LDAPControl **sctrls, 77 LDAPControl **cctrls, 78 int sizelimit, 79 LDAPMessage **res ) 80{ 81 struct timeval timeout; 82 int result; 83 84 /* Setup timeout for the ldap_search_ext_s call - local and remote. */ 85 timeout.tv_sec = lp_ldap_timeout(); 86 timeout.tv_usec = 0; 87 88 /* Setup alarm timeout.... Do we need both of these ? JRA. */ 89 gotalarm = 0; 90 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); 91 alarm(lp_ldap_timeout()); 92 /* End setup timeout. */ 93 94 result = ldap_search_ext_s(ld, base, scope, filter, attrs, 95 attrsonly, sctrls, cctrls, &timeout, 96 sizelimit, res); 97 98 /* Teardown timeout. */ 99 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); 100 alarm(0); 101 102 if (gotalarm != 0) 103 return LDAP_TIMELIMIT_EXCEEDED; 104 105 return result; 106} 107 108/* 109 try a connection to a given ldap server, returning True and setting the servers IP 110 in the ads struct if successful 111 112 TODO : add a negative connection cache in here leveraged off of the one 113 found in the rpc code. --jerry 114 */ 115BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port) 116{ 117 char *srv; 118 119 if (!server || !*server) { 120 return False; 121 } 122 123 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port)); 124 125 /* this copes with inet_ntoa brokenness */ 126 srv = SMB_STRDUP(server); 127 128 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout()); 129 if (!ads->ld) { 130 free(srv); 131 return False; 132 } 133 ads->ldap_port = port; 134 ads->ldap_ip = *interpret_addr2(srv); 135 free(srv); 136 137 return True; 138} 139 140/* 141 try a connection to a given ldap server, based on URL, returning True if successful 142 */ 143static BOOL ads_try_connect_uri(ADS_STRUCT *ads) 144{ 145#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) 146 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n", 147 ads->server.ldap_uri)); 148 149 150 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) { 151 return True; 152 } 153 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno))); 154 155#else 156 157 DEBUG(1, ("no URL support in LDAP libs!\n")); 158#endif 159 160 return False; 161} 162 163/********************************************************************** 164 Try to find an AD dc using our internal name resolution routines 165 Try the realm first and then then workgroup name if netbios is not 166 disabled 167**********************************************************************/ 168 169static BOOL ads_find_dc(ADS_STRUCT *ads) 170{ 171 const char *c_realm; 172 int count, i=0; 173 struct ip_service *ip_list; 174 pstring realm; 175 BOOL got_realm = False; 176 BOOL use_own_domain = False; 177 178 /* if the realm and workgroup are both empty, assume they are ours */ 179 180 /* realm */ 181 c_realm = ads->server.realm; 182 183 if ( !c_realm || !*c_realm ) { 184 /* special case where no realm and no workgroup means our own */ 185 if ( !ads->server.workgroup || !*ads->server.workgroup ) { 186 use_own_domain = True; 187 c_realm = lp_realm(); 188 } 189 } 190 191 if (c_realm && *c_realm) 192 got_realm = True; 193 194again: 195 /* we need to try once with the realm name and fallback to the 196 netbios domain name if we fail (if netbios has not been disabled */ 197 198 if ( !got_realm && !lp_disable_netbios() ) { 199 c_realm = ads->server.workgroup; 200 if (!c_realm || !*c_realm) { 201 if ( use_own_domain ) 202 c_realm = lp_workgroup(); 203 } 204 205 if ( !c_realm || !*c_realm ) { 206 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n")); 207 return False; 208 } 209 } 210 211 pstrcpy( realm, c_realm ); 212 213 DEBUG(6,("ads_find_dc: looking for %s '%s'\n", 214 (got_realm ? "realm" : "domain"), realm)); 215 216 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) { 217 /* fall back to netbios if we can */ 218 if ( got_realm && !lp_disable_netbios() ) { 219 got_realm = False; 220 goto again; 221 } 222 223 return False; 224 } 225 226 /* if we fail this loop, then giveup since all the IP addresses returned were dead */ 227 for ( i=0; i<count; i++ ) { 228 /* since this is an ads conection request, default to LDAP_PORT is not set */ 229 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT; 230 fstring server; 231 232 fstrcpy( server, inet_ntoa(ip_list[i].ip) ); 233 234 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) ) 235 continue; 236 237 if ( ads_try_connect(ads, server, port) ) { 238 SAFE_FREE(ip_list); 239 return True; 240 } 241 242 /* keep track of failures */ 243 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL ); 244 } 245 246 SAFE_FREE(ip_list); 247 248 return False; 249} 250 251 252/** 253 * Connect to the LDAP server 254 * @param ads Pointer to an existing ADS_STRUCT 255 * @return status of connection 256 **/ 257ADS_STATUS ads_connect(ADS_STRUCT *ads) 258{ 259 int version = LDAP_VERSION3; 260 ADS_STATUS status; 261 262 ads->last_attempt = time(NULL); 263 ads->ld = NULL; 264 265 /* try with a URL based server */ 266 267 if (ads->server.ldap_uri && 268 ads_try_connect_uri(ads)) { 269 goto got_connection; 270 } 271 272 /* try with a user specified server */ 273 if (ads->server.ldap_server && 274 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) { 275 goto got_connection; 276 } 277 278 if (ads_find_dc(ads)) { 279 goto got_connection; 280 } 281 282 return ADS_ERROR_SYSTEM(errno?errno:ENOENT); 283 284got_connection: 285 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip))); 286 287 status = ads_server_info(ads); 288 if (!ADS_ERR_OK(status)) { 289 DEBUG(1,("Failed to get ldap server info\n")); 290 return status; 291 } 292 293 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version); 294 295 if (!ads->auth.user_name) { 296 /* have to use the userPrincipalName value here and 297 not servicePrincipalName; found by Guenther Deschner @ Sernet */ 298 299 asprintf(&ads->auth.user_name, "host/%s", global_myname() ); 300 } 301 302 if (!ads->auth.realm) { 303 ads->auth.realm = SMB_STRDUP(ads->config.realm); 304 } 305 306 if (!ads->auth.kdc_server) { 307 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip)); 308 } 309 310#if KRB5_DNS_HACK 311 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch 312 to MIT kerberos to work (tridge) */ 313 { 314 char *env; 315 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm); 316 setenv(env, ads->auth.kdc_server, 1); 317 free(env); 318 } 319#endif 320 321 if (ads->auth.flags & ADS_AUTH_NO_BIND) { 322 return ADS_SUCCESS; 323 } 324 325 if (ads->auth.flags & ADS_AUTH_ANON_BIND) { 326 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL)); 327 } 328 329 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) { 330 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password)); 331 } 332 333 return ads_sasl_bind(ads); 334} 335 336/* 337 Duplicate a struct berval into talloc'ed memory 338 */ 339static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val) 340{ 341 struct berval *value; 342 343 if (!in_val) return NULL; 344 345 value = TALLOC_ZERO_P(ctx, struct berval); 346 if (value == NULL) 347 return NULL; 348 if (in_val->bv_len == 0) return value; 349 350 value->bv_len = in_val->bv_len; 351 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len); 352 return value; 353} 354 355/* 356 Make a values list out of an array of (struct berval *) 357 */ 358static struct berval **ads_dup_values(TALLOC_CTX *ctx, 359 const struct berval **in_vals) 360{ 361 struct berval **values; 362 int i; 363 364 if (!in_vals) return NULL; 365 for (i=0; in_vals[i]; i++) 366 ; /* count values */ 367 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1); 368 if (!values) return NULL; 369 370 for (i=0; in_vals[i]; i++) { 371 values[i] = dup_berval(ctx, in_vals[i]); 372 } 373 return values; 374} 375 376/* 377 UTF8-encode a values list out of an array of (char *) 378 */ 379static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals) 380{ 381 char **values; 382 int i; 383 384 if (!in_vals) return NULL; 385 for (i=0; in_vals[i]; i++) 386 ; /* count values */ 387 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1); 388 if (!values) return NULL; 389 390 for (i=0; in_vals[i]; i++) { 391 push_utf8_talloc(ctx, &values[i], in_vals[i]); 392 } 393 return values; 394} 395 396/* 397 Pull a (char *) array out of a UTF8-encoded values list 398 */ 399static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals) 400{ 401 char **values; 402 int i; 403 404 if (!in_vals) return NULL; 405 for (i=0; in_vals[i]; i++) 406 ; /* count values */ 407 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1); 408 if (!values) return NULL; 409 410 for (i=0; in_vals[i]; i++) { 411 pull_utf8_talloc(ctx, &values[i], in_vals[i]); 412 } 413 return values; 414} 415 416/** 417 * Do a search with paged results. cookie must be null on the first 418 * call, and then returned on each subsequent call. It will be null 419 * again when the entire search is complete 420 * @param ads connection to ads server 421 * @param bind_path Base dn for the search 422 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) 423 * @param expr Search expression - specified in local charset 424 * @param attrs Attributes to retrieve - specified in utf8 or ascii 425 * @param res ** which will contain results - free res* with ads_msgfree() 426 * @param count Number of entries retrieved on this page 427 * @param cookie The paged results cookie to be returned on subsequent calls 428 * @return status of search 429 **/ 430ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path, 431 int scope, const char *expr, 432 const char **attrs, void **res, 433 int *count, void **cookie) 434{ 435 int rc, i, version; 436 char *utf8_expr, *utf8_path, **search_attrs; 437 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols; 438 BerElement *cookie_be = NULL; 439 struct berval *cookie_bv= NULL; 440 TALLOC_CTX *ctx; 441 442 *res = NULL; 443 444 if (!(ctx = talloc_init("ads_do_paged_search"))) 445 return ADS_ERROR(LDAP_NO_MEMORY); 446 447 /* 0 means the conversion worked but the result was empty 448 so we only fail if it's -1. In any case, it always 449 at least nulls out the dest */ 450 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) || 451 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) { 452 rc = LDAP_NO_MEMORY; 453 goto done; 454 } 455 456 if (!attrs || !(*attrs)) 457 search_attrs = NULL; 458 else { 459 /* This would be the utf8-encoded version...*/ 460 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */ 461 if (!(str_list_copy(&search_attrs, attrs))) { 462 rc = LDAP_NO_MEMORY; 463 goto done; 464 } 465 } 466 467 468 /* Paged results only available on ldap v3 or later */ 469 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version); 470 if (version < LDAP_VERSION3) { 471 rc = LDAP_NOT_SUPPORTED; 472 goto done; 473 } 474 475 cookie_be = ber_alloc_t(LBER_USE_DER); 476 if (cookie && *cookie) { 477 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie); 478 ber_bvfree(*cookie); /* don't need it from last time */ 479 *cookie = NULL; 480 } else { 481 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0); 482 } 483 ber_flatten(cookie_be, &cookie_bv); 484 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID; 485 PagedResults.ldctl_iscritical = (char) 1; 486 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len; 487 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val; 488 489 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID; 490 NoReferrals.ldctl_iscritical = (char) 0; 491 NoReferrals.ldctl_value.bv_len = 0; 492 NoReferrals.ldctl_value.bv_val = ""; 493 494 495 controls[0] = &NoReferrals; 496 controls[1] = &PagedResults; 497 controls[2] = NULL; 498 499 /* we need to disable referrals as the openldap libs don't 500 handle them and paged results at the same time. Using them 501 together results in the result record containing the server 502 page control being removed from the result list (tridge/jmcd) 503 504 leaving this in despite the control that says don't generate 505 referrals, in case the server doesn't support it (jmcd) 506 */ 507 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 508 509 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr, 510 search_attrs, 0, controls, 511 NULL, LDAP_NO_LIMIT, 512 (LDAPMessage **)res); 513 514 ber_free(cookie_be, 1); 515 ber_bvfree(cookie_bv); 516 517 if (rc) { 518 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr, 519 ldap_err2string(rc))); 520 goto done; 521 } 522 523 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL, 524 NULL, &rcontrols, 0); 525 526 if (!rcontrols) { 527 goto done; 528 } 529 530 for (i=0; rcontrols[i]; i++) { 531 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) { 532 cookie_be = ber_init(&rcontrols[i]->ldctl_value); 533 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count, 534 &cookie_bv); 535 /* the berval is the cookie, but must be freed when 536 it is all done */ 537 if (cookie_bv->bv_len) /* still more to do */ 538 *cookie=ber_bvdup(cookie_bv); 539 else 540 *cookie=NULL; 541 ber_bvfree(cookie_bv); 542 ber_free(cookie_be, 1); 543 break; 544 } 545 } 546 ldap_controls_free(rcontrols); 547 548done: 549 talloc_destroy(ctx); 550 /* if/when we decide to utf8-encode attrs, take out this next line */ 551 str_list_free(&search_attrs); 552 553 return ADS_ERROR(rc); 554} 555 556 557/** 558 * Get all results for a search. This uses ads_do_paged_search() to return 559 * all entries in a large search. 560 * @param ads connection to ads server 561 * @param bind_path Base dn for the search 562 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) 563 * @param expr Search expression 564 * @param attrs Attributes to retrieve 565 * @param res ** which will contain results - free res* with ads_msgfree() 566 * @return status of search 567 **/ 568ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path, 569 int scope, const char *expr, 570 const char **attrs, void **res) 571{ 572 void *cookie = NULL; 573 int count = 0; 574 ADS_STATUS status; 575 576 *res = NULL; 577 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res, 578 &count, &cookie); 579 580 if (!ADS_ERR_OK(status)) return status; 581 582 while (cookie) { 583 void *res2 = NULL; 584 ADS_STATUS status2; 585 LDAPMessage *msg, *next; 586 587 status2 = ads_do_paged_search(ads, bind_path, scope, expr, 588 attrs, &res2, &count, &cookie); 589 590 if (!ADS_ERR_OK(status2)) break; 591 592 /* this relies on the way that ldap_add_result_entry() works internally. I hope 593 that this works on all ldap libs, but I have only tested with openldap */ 594 for (msg = ads_first_entry(ads, res2); msg; msg = next) { 595 next = ads_next_entry(ads, msg); 596 ldap_add_result_entry((LDAPMessage **)res, msg); 597 } 598 /* note that we do not free res2, as the memory is now 599 part of the main returned list */ 600 } 601 602 return status; 603} 604 605/** 606 * Run a function on all results for a search. Uses ads_do_paged_search() and 607 * runs the function as each page is returned, using ads_process_results() 608 * @param ads connection to ads server 609 * @param bind_path Base dn for the search 610 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) 611 * @param expr Search expression - specified in local charset 612 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii 613 * @param fn Function which takes attr name, values list, and data_area 614 * @param data_area Pointer which is passed to function on each call 615 * @return status of search 616 **/ 617ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path, 618 int scope, const char *expr, const char **attrs, 619 BOOL(*fn)(char *, void **, void *), 620 void *data_area) 621{ 622 void *cookie = NULL; 623 int count = 0; 624 ADS_STATUS status; 625 void *res; 626 627 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res, 628 &count, &cookie); 629 630 if (!ADS_ERR_OK(status)) return status; 631 632 ads_process_results(ads, res, fn, data_area); 633 ads_msgfree(ads, res); 634 635 while (cookie) { 636 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, 637 &res, &count, &cookie); 638 639 if (!ADS_ERR_OK(status)) break; 640 641 ads_process_results(ads, res, fn, data_area); 642 ads_msgfree(ads, res); 643 } 644 645 return status; 646} 647 648/** 649 * Do a search with a timeout. 650 * @param ads connection to ads server 651 * @param bind_path Base dn for the search 652 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) 653 * @param expr Search expression 654 * @param attrs Attributes to retrieve 655 * @param res ** which will contain results - free res* with ads_msgfree() 656 * @return status of search 657 **/ 658ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 659 const char *expr, 660 const char **attrs, void **res) 661{ 662 int rc; 663 char *utf8_expr, *utf8_path, **search_attrs = NULL; 664 TALLOC_CTX *ctx; 665 666 *res = NULL; 667 if (!(ctx = talloc_init("ads_do_search"))) { 668 DEBUG(1,("ads_do_search: talloc_init() failed!")); 669 return ADS_ERROR(LDAP_NO_MEMORY); 670 } 671 672 /* 0 means the conversion worked but the result was empty 673 so we only fail if it's negative. In any case, it always 674 at least nulls out the dest */ 675 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) || 676 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) { 677 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!")); 678 rc = LDAP_NO_MEMORY; 679 goto done; 680 } 681 682 if (!attrs || !(*attrs)) 683 search_attrs = NULL; 684 else { 685 /* This would be the utf8-encoded version...*/ 686 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */ 687 if (!(str_list_copy(&search_attrs, attrs))) 688 { 689 DEBUG(1,("ads_do_search: str_list_copy() failed!")); 690 rc = LDAP_NO_MEMORY; 691 goto done; 692 } 693 } 694 695 /* see the note in ads_do_paged_search - we *must* disable referrals */ 696 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 697 698 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr, 699 search_attrs, 0, NULL, NULL, 700 LDAP_NO_LIMIT, 701 (LDAPMessage **)res); 702 703 if (rc == LDAP_SIZELIMIT_EXCEEDED) { 704 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n")); 705 rc = 0; 706 } 707 708 done: 709 talloc_destroy(ctx); 710 /* if/when we decide to utf8-encode attrs, take out this next line */ 711 str_list_free(&search_attrs); 712 return ADS_ERROR(rc); 713} 714/** 715 * Do a general ADS search 716 * @param ads connection to ads server 717 * @param res ** which will contain results - free res* with ads_msgfree() 718 * @param expr Search expression 719 * @param attrs Attributes to retrieve 720 * @return status of search 721 **/ 722ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, 723 const char *expr, 724 const char **attrs) 725{ 726 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 727 expr, attrs, res); 728} 729 730/** 731 * Do a search on a specific DistinguishedName 732 * @param ads connection to ads server 733 * @param res ** which will contain results - free res* with ads_msgfree() 734 * @param dn DistinguishName to search 735 * @param attrs Attributes to retrieve 736 * @return status of search 737 **/ 738ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res, 739 const char *dn, 740 const char **attrs) 741{ 742 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res); 743} 744 745/** 746 * Free up memory from a ads_search 747 * @param ads connection to ads server 748 * @param msg Search results to free 749 **/ 750void ads_msgfree(ADS_STRUCT *ads, void *msg) 751{ 752 if (!msg) return; 753 ldap_msgfree(msg); 754} 755 756/** 757 * Free up memory from various ads requests 758 * @param ads connection to ads server 759 * @param mem Area to free 760 **/ 761void ads_memfree(ADS_STRUCT *ads, void *mem) 762{ 763 SAFE_FREE(mem); 764} 765 766/** 767 * Get a dn from search results 768 * @param ads connection to ads server 769 * @param msg Search result 770 * @return dn string 771 **/ 772char *ads_get_dn(ADS_STRUCT *ads, void *msg) 773{ 774 char *utf8_dn, *unix_dn; 775 776 utf8_dn = ldap_get_dn(ads->ld, msg); 777 778 if (!utf8_dn) { 779 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n")); 780 return NULL; 781 } 782 783 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) { 784 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n", 785 utf8_dn )); 786 return NULL; 787 } 788 ldap_memfree(utf8_dn); 789 return unix_dn; 790} 791 792/** 793 * Find a machine account given a hostname 794 * @param ads connection to ads server 795 * @param res ** which will contain results - free res* with ads_msgfree() 796 * @param host Hostname to search for 797 * @return status of search 798 **/ 799ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine) 800{ 801 ADS_STATUS status; 802 char *expr; 803 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL}; 804 805 *res = NULL; 806 807 /* the easiest way to find a machine account anywhere in the tree 808 is to look for hostname$ */ 809 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) { 810 DEBUG(1, ("asprintf failed!\n")); 811 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 812 } 813 814 status = ads_search(ads, res, expr, attrs); 815 SAFE_FREE(expr); 816 return status; 817} 818 819/** 820 * Initialize a list of mods to be used in a modify request 821 * @param ctx An initialized TALLOC_CTX 822 * @return allocated ADS_MODLIST 823 **/ 824ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx) 825{ 826#define ADS_MODLIST_ALLOC_SIZE 10 827 LDAPMod **mods; 828 829 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1))) 830 /* -1 is safety to make sure we don't go over the end. 831 need to reset it to NULL before doing ldap modify */ 832 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1; 833 834 return mods; 835} 836 837 838/* 839 add an attribute to the list, with values list already constructed 840*/ 841static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 842 int mod_op, const char *name, 843 const void **invals) 844{ 845 int curmod; 846 LDAPMod **modlist = (LDAPMod **) *mods; 847 struct berval **ber_values = NULL; 848 char **char_values = NULL; 849 850 if (!invals) { 851 mod_op = LDAP_MOD_DELETE; 852 } else { 853 if (mod_op & LDAP_MOD_BVALUES) 854 ber_values = ads_dup_values(ctx, 855 (const struct berval **)invals); 856 else 857 char_values = ads_push_strvals(ctx, 858 (const char **) invals); 859 } 860 861 /* find the first empty slot */ 862 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1; 863 curmod++); 864 if (modlist[curmod] == (LDAPMod *) -1) { 865 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *, 866 curmod+ADS_MODLIST_ALLOC_SIZE+1))) 867 return ADS_ERROR(LDAP_NO_MEMORY); 868 memset(&modlist[curmod], 0, 869 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *)); 870 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1; 871 *mods = modlist; 872 } 873 874 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod))) 875 return ADS_ERROR(LDAP_NO_MEMORY); 876 modlist[curmod]->mod_type = talloc_strdup(ctx, name); 877 if (mod_op & LDAP_MOD_BVALUES) { 878 modlist[curmod]->mod_bvalues = ber_values; 879 } else if (mod_op & LDAP_MOD_DELETE) { 880 modlist[curmod]->mod_values = NULL; 881 } else { 882 modlist[curmod]->mod_values = char_values; 883 } 884 885 modlist[curmod]->mod_op = mod_op; 886 return ADS_ERROR(LDAP_SUCCESS); 887} 888 889/** 890 * Add a single string value to a mod list 891 * @param ctx An initialized TALLOC_CTX 892 * @param mods An initialized ADS_MODLIST 893 * @param name The attribute name to add 894 * @param val The value to add - NULL means DELETE 895 * @return ADS STATUS indicating success of add 896 **/ 897ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 898 const char *name, const char *val) 899{ 900 const char *values[2]; 901 902 values[0] = val; 903 values[1] = NULL; 904 905 if (!val) 906 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL); 907 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, 908 (const void **) values); 909} 910 911/** 912 * Add an array of string values to a mod list 913 * @param ctx An initialized TALLOC_CTX 914 * @param mods An initialized ADS_MODLIST 915 * @param name The attribute name to add 916 * @param vals The array of string values to add - NULL means DELETE 917 * @return ADS STATUS indicating success of add 918 **/ 919ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods, 920 const char *name, const char **vals) 921{ 922 if (!vals) 923 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL); 924 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 925 name, (const void **) vals); 926} 927 928/** 929 * Add a single ber-encoded value to a mod list 930 * @param ctx An initialized TALLOC_CTX 931 * @param mods An initialized ADS_MODLIST 932 * @param name The attribute name to add 933 * @param val The value to add - NULL means DELETE 934 * @return ADS STATUS indicating success of add 935 **/ 936static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 937 const char *name, const struct berval *val) 938{ 939 const struct berval *values[2]; 940 941 values[0] = val; 942 values[1] = NULL; 943 if (!val) 944 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL); 945 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES, 946 name, (const void **) values); 947} 948 949/** 950 * Perform an ldap modify 951 * @param ads connection to ads server 952 * @param mod_dn DistinguishedName to modify 953 * @param mods list of modifications to perform 954 * @return status of modify 955 **/ 956ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods) 957{ 958 int ret,i; 959 char *utf8_dn = NULL; 960 /* 961 this control is needed to modify that contains a currently 962 non-existent attribute (but allowable for the object) to run 963 */ 964 LDAPControl PermitModify = { 965 ADS_PERMIT_MODIFY_OID, 966 {0, NULL}, 967 (char) 1}; 968 LDAPControl *controls[2]; 969 970 controls[0] = &PermitModify; 971 controls[1] = NULL; 972 973 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) { 974 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 975 } 976 977 /* find the end of the list, marked by NULL or -1 */ 978 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++); 979 /* make sure the end of the list is NULL */ 980 mods[i] = NULL; 981 ret = ldap_modify_ext_s(ads->ld, utf8_dn, 982 (LDAPMod **) mods, controls, NULL); 983 SAFE_FREE(utf8_dn); 984 return ADS_ERROR(ret); 985} 986 987/** 988 * Perform an ldap add 989 * @param ads connection to ads server 990 * @param new_dn DistinguishedName to add 991 * @param mods list of attributes and values for DN 992 * @return status of add 993 **/ 994ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods) 995{ 996 int ret, i; 997 char *utf8_dn = NULL; 998 999 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) { 1000 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!")); 1001 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 1002 } 1003 1004 /* find the end of the list, marked by NULL or -1 */ 1005 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++); 1006 /* make sure the end of the list is NULL */ 1007 mods[i] = NULL; 1008 1009 ret = ldap_add_s(ads->ld, utf8_dn, mods); 1010 SAFE_FREE(utf8_dn); 1011 return ADS_ERROR(ret); 1012} 1013 1014/** 1015 * Delete a DistinguishedName 1016 * @param ads connection to ads server 1017 * @param new_dn DistinguishedName to delete 1018 * @return status of delete 1019 **/ 1020ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn) 1021{ 1022 int ret; 1023 char *utf8_dn = NULL; 1024 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) { 1025 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!")); 1026 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 1027 } 1028 1029 ret = ldap_delete_s(ads->ld, utf8_dn); 1030 return ADS_ERROR(ret); 1031} 1032 1033/** 1034 * Build an org unit string 1035 * if org unit is Computers or blank then assume a container, otherwise 1036 * assume a \ separated list of organisational units 1037 * @param ads connection to ads server 1038 * @param org_unit Organizational unit 1039 * @return org unit string - caller must free 1040 **/ 1041char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit) 1042{ 1043 char *ret = NULL; 1044 1045 if (!org_unit || !*org_unit) { 1046 1047 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS); 1048 1049 /* samba4 might not yet respond to a wellknownobject-query */ 1050 return ret ? ret : SMB_STRDUP("cn=Computers"); 1051 } 1052 1053 if (strequal(org_unit, "Computers")) { 1054 return SMB_STRDUP("cn=Computers"); 1055 } 1056 1057 return ads_build_path(org_unit, "\\/", "ou=", 1); 1058} 1059 1060/** 1061 * Get a org unit string for a well-known GUID 1062 * @param ads connection to ads server 1063 * @param wknguid Well known GUID 1064 * @return org unit string - caller must free 1065 **/ 1066char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid) 1067{ 1068 ADS_STATUS status; 1069 void *res; 1070 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp; 1071 const char *attrs[] = {"distinguishedName", NULL}; 1072 int new_ln, wkn_ln, bind_ln, i; 1073 1074 if (wknguid == NULL) { 1075 return NULL; 1076 } 1077 1078 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) { 1079 DEBUG(1, ("asprintf failed!\n")); 1080 return NULL; 1081 } 1082 1083 status = ads_search_dn(ads, &res, base, attrs); 1084 if (!ADS_ERR_OK(status)) { 1085 DEBUG(1,("Failed while searching for: %s\n", base)); 1086 return NULL; 1087 } 1088 free(base); 1089 1090 if (ads_count_replies(ads, res) != 1) { 1091 return NULL; 1092 } 1093 1094 /* substitute the bind-path from the well-known-guid-search result */ 1095 wkn_dn = ads_get_dn(ads, res); 1096 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0); 1097 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0); 1098 1099 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++) 1100 ; 1101 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++) 1102 ; 1103 1104 new_ln = wkn_ln - bind_ln; 1105 1106 ret = wkn_dn_exp[0]; 1107 1108 for (i=1; i < new_ln; i++) { 1109 char *s; 1110 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]); 1111 ret = SMB_STRDUP(s); 1112 free(s); 1113 } 1114 1115 return ret; 1116} 1117 1118/** 1119 * Adds (appends) an item to an attribute array, rather then 1120 * replacing the whole list 1121 * @param ctx An initialized TALLOC_CTX 1122 * @param mods An initialized ADS_MODLIST 1123 * @param name name of the ldap attribute to append to 1124 * @param vals an array of values to add 1125 * @return status of addition 1126 **/ 1127 1128ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods, 1129 const char *name, const char **vals) 1130{ 1131 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals); 1132} 1133 1134/** 1135 * Determines the computer account's current KVNO via an LDAP lookup 1136 * @param ads An initialized ADS_STRUCT 1137 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account. 1138 * @return the kvno for the computer account, or -1 in case of a failure. 1139 **/ 1140 1141uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name) 1142{ 1143 LDAPMessage *res = NULL; 1144 uint32 kvno = (uint32)-1; /* -1 indicates a failure */ 1145 char *filter; 1146 const char *attrs[] = {"msDS-KeyVersionNumber", NULL}; 1147 char *dn_string = NULL; 1148 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS); 1149 1150 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name)); 1151 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) { 1152 return kvno; 1153 } 1154 ret = ads_search(ads, (void**) &res, filter, attrs); 1155 SAFE_FREE(filter); 1156 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) { 1157 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name)); 1158 ads_msgfree(ads, res); 1159 return kvno; 1160 } 1161 1162 dn_string = ads_get_dn(ads, res); 1163 if (!dn_string) { 1164 DEBUG(0,("ads_get_kvno: out of memory.\n")); 1165 ads_msgfree(ads, res); 1166 return kvno; 1167 } 1168 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string)); 1169 ads_memfree(ads, dn_string); 1170 1171 /* --------------------------------------------------------- 1172 * 0 is returned as a default KVNO from this point on... 1173 * This is done because Windows 2000 does not support key 1174 * version numbers. Chances are that a failure in the next 1175 * step is simply due to Windows 2000 being used for a 1176 * domain controller. */ 1177 kvno = 0; 1178 1179 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) { 1180 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n")); 1181 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n")); 1182 ads_msgfree(ads, res); 1183 return kvno; 1184 } 1185 1186 /* Success */ 1187 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno)); 1188 ads_msgfree(ads, res); 1189 return kvno; 1190} 1191 1192/** 1193 * This clears out all registered spn's for a given hostname 1194 * @param ads An initilaized ADS_STRUCT 1195 * @param machine_name the NetBIOS name of the computer. 1196 * @return 0 upon success, non-zero otherwise. 1197 **/ 1198 1199ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name) 1200{ 1201 TALLOC_CTX *ctx; 1202 LDAPMessage *res = NULL; 1203 ADS_MODLIST mods; 1204 const char *servicePrincipalName[1] = {NULL}; 1205 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS); 1206 char *dn_string = NULL; 1207 1208 ret = ads_find_machine_acct(ads, (void **)&res, machine_name); 1209 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) { 1210 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name)); 1211 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name)); 1212 ads_msgfree(ads, res); 1213 return ADS_ERROR(LDAP_NO_SUCH_OBJECT); 1214 } 1215 1216 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name)); 1217 ctx = talloc_init("ads_clear_service_principal_names"); 1218 if (!ctx) { 1219 ads_msgfree(ads, res); 1220 return ADS_ERROR(LDAP_NO_MEMORY); 1221 } 1222 1223 if (!(mods = ads_init_mods(ctx))) { 1224 talloc_destroy(ctx); 1225 ads_msgfree(ads, res); 1226 return ADS_ERROR(LDAP_NO_MEMORY); 1227 } 1228 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName); 1229 if (!ADS_ERR_OK(ret)) { 1230 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n")); 1231 ads_msgfree(ads, res); 1232 talloc_destroy(ctx); 1233 return ret; 1234 } 1235 dn_string = ads_get_dn(ads, res); 1236 if (!dn_string) { 1237 talloc_destroy(ctx); 1238 ads_msgfree(ads, res); 1239 return ADS_ERROR(LDAP_NO_MEMORY); 1240 } 1241 ret = ads_gen_mod(ads, dn_string, mods); 1242 ads_memfree(ads,dn_string); 1243 if (!ADS_ERR_OK(ret)) { 1244 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n", 1245 machine_name)); 1246 ads_msgfree(ads, res); 1247 talloc_destroy(ctx); 1248 return ret; 1249 } 1250 1251 ads_msgfree(ads, res); 1252 talloc_destroy(ctx); 1253 return ret; 1254} 1255 1256/** 1257 * This adds a service principal name to an existing computer account 1258 * (found by hostname) in AD. 1259 * @param ads An initialized ADS_STRUCT 1260 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account. 1261 * @param spn A string of the service principal to add, i.e. 'host' 1262 * @return 0 upon sucess, or non-zero if a failure occurs 1263 **/ 1264 1265ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn) 1266{ 1267 ADS_STATUS ret; 1268 TALLOC_CTX *ctx; 1269 LDAPMessage *res = NULL; 1270 char *host_spn, *host_upn, *psp1, *psp2, *psp3; 1271 ADS_MODLIST mods; 1272 fstring my_fqdn; 1273 char *dn_string = NULL; 1274 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL}; 1275 1276 ret = ads_find_machine_acct(ads, (void **)&res, machine_name); 1277 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) { 1278 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n", 1279 machine_name)); 1280 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n", 1281 spn, machine_name, ads->config.realm)); 1282 ads_msgfree(ads, res); 1283 return ADS_ERROR(LDAP_NO_SUCH_OBJECT); 1284 } 1285 1286 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name)); 1287 if (!(ctx = talloc_init("ads_add_service_principal_name"))) { 1288 ads_msgfree(ads, res); 1289 return ADS_ERROR(LDAP_NO_MEMORY); 1290 } 1291 1292 name_to_fqdn(my_fqdn, machine_name); 1293 strlower_m(my_fqdn); 1294 1295 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) { 1296 talloc_destroy(ctx); 1297 ads_msgfree(ads, res); 1298 return ADS_ERROR(LDAP_NO_SUCH_OBJECT); 1299 } 1300 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) { 1301 talloc_destroy(ctx); 1302 ads_msgfree(ads, res); 1303 return ADS_ERROR(LDAP_NO_SUCH_OBJECT); 1304 } 1305 1306 /* Add the extra principal */ 1307 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name); 1308 strupper_m(psp1); 1309 strlower_m(&psp1[strlen(spn)]); 1310 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name)); 1311 servicePrincipalName[0] = psp1; 1312 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm); 1313 strupper_m(psp2); 1314 strlower_m(&psp2[strlen(spn)]); 1315 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name)); 1316 servicePrincipalName[1] = psp2; 1317 1318 /* Add another principal in case the realm != the DNS domain, so that 1319 * the KDC doesn't send "server principal unknown" errors to clients 1320 * which use the DNS name in determining service principal names. */ 1321 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn); 1322 strupper_m(psp3); 1323 strlower_m(&psp3[strlen(spn)]); 1324 if (strcmp(psp2, psp3) != 0) { 1325 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name)); 1326 servicePrincipalName[2] = psp3; 1327 } 1328 1329 if (!(mods = ads_init_mods(ctx))) { 1330 talloc_destroy(ctx); 1331 ads_msgfree(ads, res); 1332 return ADS_ERROR(LDAP_NO_MEMORY); 1333 } 1334 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName); 1335 if (!ADS_ERR_OK(ret)) { 1336 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n")); 1337 talloc_destroy(ctx); 1338 ads_msgfree(ads, res); 1339 return ret; 1340 } 1341 dn_string = ads_get_dn(ads, res); 1342 if (!dn_string) { 1343 talloc_destroy(ctx); 1344 ads_msgfree(ads, res); 1345 return ADS_ERROR(LDAP_NO_MEMORY); 1346 } 1347 ret = ads_gen_mod(ads, dn_string, mods); 1348 ads_memfree(ads,dn_string); 1349 if (!ADS_ERR_OK(ret)) { 1350 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n")); 1351 talloc_destroy(ctx); 1352 ads_msgfree(ads, res); 1353 return ret; 1354 } 1355 1356 talloc_destroy(ctx); 1357 ads_msgfree(ads, res); 1358 return ret; 1359} 1360 1361/** 1362 * adds a machine account to the ADS server 1363 * @param ads An intialized ADS_STRUCT 1364 * @param machine_name - the NetBIOS machine name of this account. 1365 * @param account_type A number indicating the type of account to create 1366 * @param org_unit The LDAP path in which to place this account 1367 * @return 0 upon success, or non-zero otherwise 1368**/ 1369 1370static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name, 1371 uint32 account_type, 1372 const char *org_unit) 1373{ 1374 ADS_STATUS ret, status; 1375 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr; 1376 TALLOC_CTX *ctx; 1377 ADS_MODLIST mods; 1378 const char *objectClass[] = {"top", "person", "organizationalPerson", 1379 "user", "computer", NULL}; 1380 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; 1381 char *psp, *psp2, *psp3, *psp4; 1382 unsigned acct_control; 1383 unsigned exists=0; 1384 fstring my_fqdn; 1385 LDAPMessage *res = NULL; 1386 int i, next_spn; 1387 1388 if (!(ctx = talloc_init("ads_add_machine_acct"))) 1389 return ADS_ERROR(LDAP_NO_MEMORY); 1390 1391 ret = ADS_ERROR(LDAP_NO_MEMORY); 1392 1393 name_to_fqdn(my_fqdn, machine_name); 1394 1395 status = ads_find_machine_acct(ads, (void **)&res, machine_name); 1396 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) { 1397 char *dn_string = ads_get_dn(ads, res); 1398 if (!dn_string) { 1399 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n")); 1400 goto done; 1401 } 1402 new_dn = talloc_strdup(ctx, dn_string); 1403 ads_memfree(ads,dn_string); 1404 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n", 1405 machine_name)); 1406 exists=1; 1407 } else { 1408 char *ou_str = ads_ou_string(ads,org_unit); 1409 if (!ou_str) { 1410 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n")); 1411 goto done; 1412 } 1413 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str, 1414 ads->config.bind_path); 1415 1416 SAFE_FREE(ou_str); 1417 } 1418 1419 if (!new_dn) { 1420 goto done; 1421 } 1422 1423 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name))) 1424 goto done; 1425 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) 1426 goto done; 1427 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name); 1428 psp = talloc_asprintf(ctx, "HOST/%s.%s", 1429 machine_name, 1430 ads->config.realm); 1431 strlower_m(&psp[5]); 1432 servicePrincipalName[1] = psp; 1433 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name); 1434 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 1435 machine_name, 1436 ads->config.realm); 1437 strlower_m(&psp2[5]); 1438 servicePrincipalName[3] = psp2; 1439 1440 /* Ensure servicePrincipalName[4] and [5] are unique. */ 1441 strlower_m(my_fqdn); 1442 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn); 1443 strlower_m(&psp3[5]); 1444 1445 next_spn = 4; 1446 for (i = 0; i < next_spn; i++) { 1447 if (strequal(servicePrincipalName[i], psp3)) 1448 break; 1449 } 1450 if (i == next_spn) { 1451 servicePrincipalName[next_spn++] = psp3; 1452 } 1453 1454 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn); 1455 strlower_m(&psp4[5]); 1456 for (i = 0; i < next_spn; i++) { 1457 if (strequal(servicePrincipalName[i], psp4)) 1458 break; 1459 } 1460 if (i == next_spn) { 1461 servicePrincipalName[next_spn++] = psp4; 1462 } 1463 1464 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) { 1465 goto done; 1466 } 1467 1468 acct_control = account_type | UF_DONT_EXPIRE_PASSWD; 1469#ifndef ENCTYPE_ARCFOUR_HMAC 1470 acct_control |= UF_USE_DES_KEY_ONLY; 1471#endif 1472 1473 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) { 1474 goto done; 1475 } 1476 1477 if (!(mods = ads_init_mods(ctx))) { 1478 goto done; 1479 } 1480 1481 if (!exists) { 1482 ads_mod_str(ctx, &mods, "cn", machine_name); 1483 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName); 1484 ads_mod_str(ctx, &mods, "userAccountControl", controlstr); 1485 ads_mod_strlist(ctx, &mods, "objectClass", objectClass); 1486 } 1487 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn); 1488 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn); 1489 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName); 1490 ads_mod_str(ctx, &mods, "operatingSystem", "Samba"); 1491 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING); 1492 1493 if (!exists) { 1494 ret = ads_gen_add(ads, new_dn, mods); 1495 } else { 1496 ret = ads_gen_mod(ads, new_dn, mods); 1497 } 1498 1499 if (!ADS_ERR_OK(ret)) { 1500 goto done; 1501 } 1502 1503 /* Do not fail if we can't set security descriptor 1504 * it shouldn't be mandatory and probably we just 1505 * don't have enough rights to do it. 1506 */ 1507 if (!exists) { 1508 status = ads_set_machine_sd(ads, machine_name, new_dn); 1509 1510 if (!ADS_ERR_OK(status)) { 1511 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n", 1512 ads_errstr(status))); 1513 } 1514 } 1515done: 1516 ads_msgfree(ads, res); 1517 talloc_destroy(ctx); 1518 return ret; 1519} 1520 1521/* 1522 dump a binary result from ldap 1523*/ 1524static void dump_binary(const char *field, struct berval **values) 1525{ 1526 int i, j; 1527 for (i=0; values[i]; i++) { 1528 printf("%s: ", field); 1529 for (j=0; j<values[i]->bv_len; j++) { 1530 printf("%02X", (unsigned char)values[i]->bv_val[j]); 1531 } 1532 printf("\n"); 1533 } 1534} 1535 1536static void dump_guid(const char *field, struct berval **values) 1537{ 1538 int i; 1539 UUID_FLAT guid; 1540 for (i=0; values[i]; i++) { 1541 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info)); 1542 printf("%s: %s\n", field, 1543 smb_uuid_string_static(smb_uuid_unpack_static(guid))); 1544 } 1545} 1546 1547/* 1548 dump a sid result from ldap 1549*/ 1550static void dump_sid(const char *field, struct berval **values) 1551{ 1552 int i; 1553 for (i=0; values[i]; i++) { 1554 DOM_SID sid; 1555 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid); 1556 printf("%s: %s\n", field, sid_string_static(&sid)); 1557 } 1558} 1559 1560/* 1561 dump ntSecurityDescriptor 1562*/ 1563static void dump_sd(const char *filed, struct berval **values) 1564{ 1565 prs_struct ps; 1566 1567 SEC_DESC *psd = 0; 1568 TALLOC_CTX *ctx = 0; 1569 1570 if (!(ctx = talloc_init("sec_io_desc"))) 1571 return; 1572 1573 /* prepare data */ 1574 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL); 1575 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len); 1576 prs_set_offset(&ps,0); 1577 1578 /* parse secdesc */ 1579 if (!sec_io_desc("sd", &psd, &ps, 1)) { 1580 prs_mem_free(&ps); 1581 talloc_destroy(ctx); 1582 return; 1583 } 1584 if (psd) ads_disp_sd(psd); 1585 1586 prs_mem_free(&ps); 1587 talloc_destroy(ctx); 1588} 1589 1590/* 1591 dump a string result from ldap 1592*/ 1593static void dump_string(const char *field, char **values) 1594{ 1595 int i; 1596 for (i=0; values[i]; i++) { 1597 printf("%s: %s\n", field, values[i]); 1598 } 1599} 1600 1601/* 1602 dump a field from LDAP on stdout 1603 used for debugging 1604*/ 1605 1606static BOOL ads_dump_field(char *field, void **values, void *data_area) 1607{ 1608 const struct { 1609 const char *name; 1610 BOOL string; 1611 void (*handler)(const char *, struct berval **); 1612 } handlers[] = { 1613 {"objectGUID", False, dump_guid}, 1614 {"nTSecurityDescriptor", False, dump_sd}, 1615 {"dnsRecord", False, dump_binary}, 1616 {"objectSid", False, dump_sid}, 1617 {"tokenGroups", False, dump_sid}, 1618 {NULL, True, NULL} 1619 }; 1620 int i; 1621 1622 if (!field) { /* must be end of an entry */ 1623 printf("\n"); 1624 return False; 1625 } 1626 1627 for (i=0; handlers[i].name; i++) { 1628 if (StrCaseCmp(handlers[i].name, field) == 0) { 1629 if (!values) /* first time, indicate string or not */ 1630 return handlers[i].string; 1631 handlers[i].handler(field, (struct berval **) values); 1632 break; 1633 } 1634 } 1635 if (!handlers[i].name) { 1636 if (!values) /* first time, indicate string conversion */ 1637 return True; 1638 dump_string(field, (char **)values); 1639 } 1640 return False; 1641} 1642 1643/** 1644 * Dump a result from LDAP on stdout 1645 * used for debugging 1646 * @param ads connection to ads server 1647 * @param res Results to dump 1648 **/ 1649 1650void ads_dump(ADS_STRUCT *ads, void *res) 1651{ 1652 ads_process_results(ads, res, ads_dump_field, NULL); 1653} 1654 1655/** 1656 * Walk through results, calling a function for each entry found. 1657 * The function receives a field name, a berval * array of values, 1658 * and a data area passed through from the start. The function is 1659 * called once with null for field and values at the end of each 1660 * entry. 1661 * @param ads connection to ads server 1662 * @param res Results to process 1663 * @param fn Function for processing each result 1664 * @param data_area user-defined area to pass to function 1665 **/ 1666void ads_process_results(ADS_STRUCT *ads, void *res, 1667 BOOL(*fn)(char *, void **, void *), 1668 void *data_area) 1669{ 1670 void *msg; 1671 TALLOC_CTX *ctx; 1672 1673 if (!(ctx = talloc_init("ads_process_results"))) 1674 return; 1675 1676 for (msg = ads_first_entry(ads, res); msg; 1677 msg = ads_next_entry(ads, msg)) { 1678 char *utf8_field; 1679 BerElement *b; 1680 1681 for (utf8_field=ldap_first_attribute(ads->ld, 1682 (LDAPMessage *)msg,&b); 1683 utf8_field; 1684 utf8_field=ldap_next_attribute(ads->ld, 1685 (LDAPMessage *)msg,b)) { 1686 struct berval **ber_vals; 1687 char **str_vals, **utf8_vals; 1688 char *field; 1689 BOOL string; 1690 1691 pull_utf8_talloc(ctx, &field, utf8_field); 1692 string = fn(field, NULL, data_area); 1693 1694 if (string) { 1695 utf8_vals = ldap_get_values(ads->ld, 1696 (LDAPMessage *)msg, field); 1697 str_vals = ads_pull_strvals(ctx, 1698 (const char **) utf8_vals); 1699 fn(field, (void **) str_vals, data_area); 1700 ldap_value_free(utf8_vals); 1701 } else { 1702 ber_vals = ldap_get_values_len(ads->ld, 1703 (LDAPMessage *)msg, field); 1704 fn(field, (void **) ber_vals, data_area); 1705 1706 ldap_value_free_len(ber_vals); 1707 } 1708 ldap_memfree(utf8_field); 1709 } 1710 ber_free(b, 0); 1711 talloc_destroy_pool(ctx); 1712 fn(NULL, NULL, data_area); /* completed an entry */ 1713 1714 } 1715 talloc_destroy(ctx); 1716} 1717 1718/** 1719 * count how many replies are in a LDAPMessage 1720 * @param ads connection to ads server 1721 * @param res Results to count 1722 * @return number of replies 1723 **/ 1724int ads_count_replies(ADS_STRUCT *ads, void *res) 1725{ 1726 return ldap_count_entries(ads->ld, (LDAPMessage *)res); 1727} 1728 1729/** 1730 * Join a machine to a realm 1731 * Creates the machine account and sets the machine password 1732 * @param ads connection to ads server 1733 * @param machine name of host to add 1734 * @param org_unit Organizational unit to place machine in 1735 * @return status of join 1736 **/ 1737ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name, 1738 uint32 account_type, const char *org_unit) 1739{ 1740 ADS_STATUS status; 1741 LDAPMessage *res = NULL; 1742 char *machine; 1743 1744 /* machine name must be lowercase */ 1745 machine = SMB_STRDUP(machine_name); 1746 strlower_m(machine); 1747 1748 /* 1749 status = ads_find_machine_acct(ads, (void **)&res, machine); 1750 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) { 1751 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine)); 1752 status = ads_leave_realm(ads, machine); 1753 if (!ADS_ERR_OK(status)) { 1754 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 1755 machine, ads->config.realm)); 1756 return status; 1757 } 1758 } 1759 */ 1760 1761 status = ads_add_machine_acct(ads, machine, account_type, org_unit); 1762 if (!ADS_ERR_OK(status)) { 1763 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status))); 1764 SAFE_FREE(machine); 1765 return status; 1766 } 1767 1768 status = ads_find_machine_acct(ads, (void **)&res, machine); 1769 if (!ADS_ERR_OK(status)) { 1770 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine)); 1771 SAFE_FREE(machine); 1772 return status; 1773 } 1774 1775 SAFE_FREE(machine); 1776 ads_msgfree(ads, res); 1777 1778 return status; 1779} 1780 1781/** 1782 * Delete a machine from the realm 1783 * @param ads connection to ads server 1784 * @param hostname Machine to remove 1785 * @return status of delete 1786 **/ 1787ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname) 1788{ 1789 ADS_STATUS status; 1790 void *res, *msg; 1791 char *hostnameDN, *host; 1792 int rc; 1793 1794 /* hostname must be lowercase */ 1795 host = SMB_STRDUP(hostname); 1796 strlower_m(host); 1797 1798 status = ads_find_machine_acct(ads, &res, host); 1799 if (!ADS_ERR_OK(status)) { 1800 DEBUG(0, ("Host account for %s does not exist.\n", host)); 1801 return status; 1802 } 1803 1804 msg = ads_first_entry(ads, res); 1805 if (!msg) { 1806 return ADS_ERROR_SYSTEM(ENOENT); 1807 } 1808 1809 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg); 1810 rc = ldap_delete_s(ads->ld, hostnameDN); 1811 ads_memfree(ads, hostnameDN); 1812 if (rc != LDAP_SUCCESS) { 1813 return ADS_ERROR(rc); 1814 } 1815 1816 status = ads_find_machine_acct(ads, &res, host); 1817 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) { 1818 DEBUG(0, ("Failed to remove host account.\n")); 1819 return status; 1820 } 1821 1822 free(host); 1823 1824 return status; 1825} 1826 1827/** 1828 * add machine account to existing security descriptor 1829 * @param ads connection to ads server 1830 * @param hostname machine to add 1831 * @param dn DN of security descriptor 1832 * @return status 1833 **/ 1834ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn) 1835{ 1836 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0}; 1837 char *expr = 0; 1838 size_t sd_size = 0; 1839 struct berval bval = {0, NULL}; 1840 prs_struct ps_wire; 1841 char *escaped_hostname = escape_ldap_string_alloc(hostname); 1842 1843 LDAPMessage *res = 0; 1844 LDAPMessage *msg = 0; 1845 ADS_MODLIST mods = 0; 1846 1847 NTSTATUS status; 1848 ADS_STATUS ret; 1849 DOM_SID sid; 1850 SEC_DESC *psd = NULL; 1851 TALLOC_CTX *ctx = NULL; 1852 1853 /* Avoid segmentation fault in prs_mem_free if 1854 * we have to bail out before prs_init */ 1855 ps_wire.is_dynamic = False; 1856 1857 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN); 1858 1859 ret = ADS_ERROR(LDAP_SUCCESS); 1860 1861 if (!escaped_hostname) { 1862 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 1863 } 1864 1865 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) { 1866 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n")); 1867 SAFE_FREE(escaped_hostname); 1868 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 1869 } 1870 1871 SAFE_FREE(escaped_hostname); 1872 1873 ret = ads_search(ads, (void *) &res, expr, attrs); 1874 1875 if (!ADS_ERR_OK(ret)) return ret; 1876 1877 if ( !(msg = ads_first_entry(ads, res) )) { 1878 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED); 1879 goto ads_set_sd_error; 1880 } 1881 1882 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) { 1883 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); 1884 goto ads_set_sd_error; 1885 } 1886 1887 if (!(ctx = talloc_init("sec_io_desc"))) { 1888 ret = ADS_ERROR(LDAP_NO_MEMORY); 1889 goto ads_set_sd_error; 1890 } 1891 1892 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) { 1893 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); 1894 goto ads_set_sd_error; 1895 } 1896 1897 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size); 1898 1899 if (!NT_STATUS_IS_OK(status)) { 1900 ret = ADS_ERROR_NT(status); 1901 goto ads_set_sd_error; 1902 } 1903 1904 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) { 1905 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 1906 } 1907 1908 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) { 1909 ret = ADS_ERROR(LDAP_NO_MEMORY); 1910 goto ads_set_sd_error; 1911 } 1912 1913#if 0 1914 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size); 1915#endif 1916 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY); 1917 1918 bval.bv_len = prs_offset(&ps_wire); 1919 bval.bv_val = TALLOC(ctx, bval.bv_len); 1920 if (!bval.bv_val) { 1921 ret = ADS_ERROR(LDAP_NO_MEMORY); 1922 goto ads_set_sd_error; 1923 } 1924 1925 prs_set_offset(&ps_wire, 0); 1926 1927 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) { 1928 ret = ADS_ERROR(LDAP_NO_MEMORY); 1929 goto ads_set_sd_error; 1930 } 1931 1932 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval); 1933 if (ADS_ERR_OK(ret)) { 1934 ret = ads_gen_mod(ads, dn, mods); 1935 } 1936 1937ads_set_sd_error: 1938 ads_msgfree(ads, res); 1939 prs_mem_free(&ps_wire); 1940 talloc_destroy(ctx); 1941 return ret; 1942} 1943 1944/** 1945 * pull the first entry from a ADS result 1946 * @param ads connection to ads server 1947 * @param res Results of search 1948 * @return first entry from result 1949 **/ 1950void *ads_first_entry(ADS_STRUCT *ads, void *res) 1951{ 1952 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res); 1953} 1954 1955/** 1956 * pull the next entry from a ADS result 1957 * @param ads connection to ads server 1958 * @param res Results of search 1959 * @return next entry from result 1960 **/ 1961void *ads_next_entry(ADS_STRUCT *ads, void *res) 1962{ 1963 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res); 1964} 1965 1966/** 1967 * pull a single string from a ADS result 1968 * @param ads connection to ads server 1969 * @param mem_ctx TALLOC_CTX to use for allocating result string 1970 * @param msg Results of search 1971 * @param field Attribute to retrieve 1972 * @return Result string in talloc context 1973 **/ 1974char *ads_pull_string(ADS_STRUCT *ads, 1975 TALLOC_CTX *mem_ctx, void *msg, const char *field) 1976{ 1977 char **values; 1978 char *ret = NULL; 1979 char *ux_string; 1980 size_t rc; 1981 1982 values = ldap_get_values(ads->ld, msg, field); 1983 if (!values) 1984 return NULL; 1985 1986 if (values[0]) { 1987 rc = pull_utf8_talloc(mem_ctx, &ux_string, 1988 values[0]); 1989 if (rc != (size_t)-1) 1990 ret = ux_string; 1991 1992 } 1993 ldap_value_free(values); 1994 return ret; 1995} 1996 1997/** 1998 * pull an array of strings from a ADS result 1999 * @param ads connection to ads server 2000 * @param mem_ctx TALLOC_CTX to use for allocating result string 2001 * @param msg Results of search 2002 * @param field Attribute to retrieve 2003 * @return Result strings in talloc context 2004 **/ 2005char **ads_pull_strings(ADS_STRUCT *ads, 2006 TALLOC_CTX *mem_ctx, void *msg, const char *field, 2007 size_t *num_values) 2008{ 2009 char **values; 2010 char **ret = NULL; 2011 int i; 2012 2013 values = ldap_get_values(ads->ld, msg, field); 2014 if (!values) 2015 return NULL; 2016 2017 *num_values = ldap_count_values(values); 2018 2019 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1); 2020 if (!ret) { 2021 ldap_value_free(values); 2022 return NULL; 2023 } 2024 2025 for (i=0;i<*num_values;i++) { 2026 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) { 2027 ldap_value_free(values); 2028 return NULL; 2029 } 2030 } 2031 ret[i] = NULL; 2032 2033 ldap_value_free(values); 2034 return ret; 2035} 2036 2037/** 2038 * pull an array of strings from a ADS result 2039 * (handle large multivalue attributes with range retrieval) 2040 * @param ads connection to ads server 2041 * @param mem_ctx TALLOC_CTX to use for allocating result string 2042 * @param msg Results of search 2043 * @param field Attribute to retrieve 2044 * @param current_strings strings returned by a previous call to this function 2045 * @param next_attribute The next query should ask for this attribute 2046 * @param num_values How many values did we get this time? 2047 * @param more_values Are there more values to get? 2048 * @return Result strings in talloc context 2049 **/ 2050char **ads_pull_strings_range(ADS_STRUCT *ads, 2051 TALLOC_CTX *mem_ctx, 2052 void *msg, const char *field, 2053 char **current_strings, 2054 const char **next_attribute, 2055 size_t *num_strings, 2056 BOOL *more_strings) 2057{ 2058 char *attr; 2059 char *expected_range_attrib, *range_attr; 2060 BerElement *ptr = NULL; 2061 char **strings; 2062 char **new_strings; 2063 size_t num_new_strings; 2064 unsigned long int range_start; 2065 unsigned long int range_end; 2066 2067 /* we might have been given the whole lot anyway */ 2068 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) { 2069 *more_strings = False; 2070 return strings; 2071 } 2072 2073 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field); 2074 2075 /* look for Range result */ 2076 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr); 2077 attr; 2078 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) { 2079 /* we ignore the fact that this is utf8, as all attributes are ascii... */ 2080 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) { 2081 range_attr = attr; 2082 break; 2083 } 2084 ldap_memfree(attr); 2085 } 2086 if (!attr) { 2087 ber_free(ptr, 0); 2088 /* nothing here - this field is just empty */ 2089 *more_strings = False; 2090 return NULL; 2091 } 2092 2093 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 2094 &range_start, &range_end) == 2) { 2095 *more_strings = True; 2096 } else { 2097 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 2098 &range_start) == 1) { 2099 *more_strings = False; 2100 } else { 2101 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n", 2102 range_attr)); 2103 ldap_memfree(range_attr); 2104 *more_strings = False; 2105 return NULL; 2106 } 2107 } 2108 2109 if ((*num_strings) != range_start) { 2110 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu" 2111 " - aborting range retreival\n", 2112 range_attr, *num_strings + 1, range_start)); 2113 ldap_memfree(range_attr); 2114 *more_strings = False; 2115 return NULL; 2116 } 2117 2118 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings); 2119 2120 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) { 2121 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu " 2122 "strings in this bunch, but we only got %lu - aborting range retreival\n", 2123 range_attr, (unsigned long int)range_end - range_start + 1, 2124 (unsigned long int)num_new_strings)); 2125 ldap_memfree(range_attr); 2126 *more_strings = False; 2127 return NULL; 2128 } 2129 2130 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *, 2131 *num_strings + num_new_strings); 2132 2133 if (strings == NULL) { 2134 ldap_memfree(range_attr); 2135 *more_strings = False; 2136 return NULL; 2137 } 2138 2139 memcpy(&strings[*num_strings], new_strings, 2140 sizeof(*new_strings) * num_new_strings); 2141 2142 (*num_strings) += num_new_strings; 2143 2144 if (*more_strings) { 2145 *next_attribute = talloc_asprintf(mem_ctx, 2146 "%s;range=%d-*", 2147 field, 2148 *num_strings); 2149 2150 if (!*next_attribute) { 2151 DEBUG(1, ("talloc_asprintf for next attribute failed!\n")); 2152 ldap_memfree(range_attr); 2153 *more_strings = False; 2154 return NULL; 2155 } 2156 } 2157 2158 ldap_memfree(range_attr); 2159 2160 return strings; 2161} 2162 2163/** 2164 * pull a single uint32 from a ADS result 2165 * @param ads connection to ads server 2166 * @param msg Results of search 2167 * @param field Attribute to retrieve 2168 * @param v Pointer to int to store result 2169 * @return boolean inidicating success 2170*/ 2171BOOL ads_pull_uint32(ADS_STRUCT *ads, 2172 void *msg, const char *field, uint32 *v) 2173{ 2174 char **values; 2175 2176 values = ldap_get_values(ads->ld, msg, field); 2177 if (!values) 2178 return False; 2179 if (!values[0]) { 2180 ldap_value_free(values); 2181 return False; 2182 } 2183 2184 *v = atoi(values[0]); 2185 ldap_value_free(values); 2186 return True; 2187} 2188 2189/** 2190 * pull a single objectGUID from an ADS result 2191 * @param ads connection to ADS server 2192 * @param msg results of search 2193 * @param guid 37-byte area to receive text guid 2194 * @return boolean indicating success 2195 **/ 2196BOOL ads_pull_guid(ADS_STRUCT *ads, 2197 void *msg, struct uuid *guid) 2198{ 2199 char **values; 2200 UUID_FLAT flat_guid; 2201 2202 values = ldap_get_values(ads->ld, msg, "objectGUID"); 2203 if (!values) 2204 return False; 2205 2206 if (values[0]) { 2207 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT)); 2208 smb_uuid_unpack(flat_guid, guid); 2209 ldap_value_free(values); 2210 return True; 2211 } 2212 ldap_value_free(values); 2213 return False; 2214 2215} 2216 2217 2218/** 2219 * pull a single DOM_SID from a ADS result 2220 * @param ads connection to ads server 2221 * @param msg Results of search 2222 * @param field Attribute to retrieve 2223 * @param sid Pointer to sid to store result 2224 * @return boolean inidicating success 2225*/ 2226BOOL ads_pull_sid(ADS_STRUCT *ads, 2227 void *msg, const char *field, DOM_SID *sid) 2228{ 2229 struct berval **values; 2230 BOOL ret = False; 2231 2232 values = ldap_get_values_len(ads->ld, msg, field); 2233 2234 if (!values) 2235 return False; 2236 2237 if (values[0]) 2238 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid); 2239 2240 ldap_value_free_len(values); 2241 return ret; 2242} 2243 2244/** 2245 * pull an array of DOM_SIDs from a ADS result 2246 * @param ads connection to ads server 2247 * @param mem_ctx TALLOC_CTX for allocating sid array 2248 * @param msg Results of search 2249 * @param field Attribute to retrieve 2250 * @param sids pointer to sid array to allocate 2251 * @return the count of SIDs pulled 2252 **/ 2253int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 2254 void *msg, const char *field, DOM_SID **sids) 2255{ 2256 struct berval **values; 2257 BOOL ret; 2258 int count, i; 2259 2260 values = ldap_get_values_len(ads->ld, msg, field); 2261 2262 if (!values) 2263 return 0; 2264 2265 for (i=0; values[i]; i++) 2266 /* nop */ ; 2267 2268 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i); 2269 if (!(*sids)) { 2270 ldap_value_free_len(values); 2271 return 0; 2272 } 2273 2274 count = 0; 2275 for (i=0; values[i]; i++) { 2276 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]); 2277 if (ret) { 2278 fstring sid; 2279 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count]))); 2280 count++; 2281 } 2282 } 2283 2284 ldap_value_free_len(values); 2285 return count; 2286} 2287 2288/** 2289 * pull a SEC_DESC from a ADS result 2290 * @param ads connection to ads server 2291 * @param mem_ctx TALLOC_CTX for allocating sid array 2292 * @param msg Results of search 2293 * @param field Attribute to retrieve 2294 * @param sd Pointer to *SEC_DESC to store result (talloc()ed) 2295 * @return boolean inidicating success 2296*/ 2297BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 2298 void *msg, const char *field, SEC_DESC **sd) 2299{ 2300 struct berval **values; 2301 prs_struct ps; 2302 BOOL ret = False; 2303 2304 values = ldap_get_values_len(ads->ld, msg, field); 2305 2306 if (!values) return False; 2307 2308 if (values[0]) { 2309 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL); 2310 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len); 2311 prs_set_offset(&ps,0); 2312 2313 ret = sec_io_desc("sd", sd, &ps, 1); 2314 } 2315 2316 ldap_value_free_len(values); 2317 return ret; 2318} 2319 2320/* 2321 * in order to support usernames longer than 21 characters we need to 2322 * use both the sAMAccountName and the userPrincipalName attributes 2323 * It seems that not all users have the userPrincipalName attribute set 2324 * 2325 * @param ads connection to ads server 2326 * @param mem_ctx TALLOC_CTX for allocating sid array 2327 * @param msg Results of search 2328 * @return the username 2329 */ 2330char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg) 2331{ 2332#if 0 /* JERRY */ 2333 char *ret, *p; 2334 2335 /* lookup_name() only works on the sAMAccountName to 2336 returning the username portion of userPrincipalName 2337 breaks winbindd_getpwnam() */ 2338 2339 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName"); 2340 if (ret && (p = strchr_m(ret, '@'))) { 2341 *p = 0; 2342 return ret; 2343 } 2344#endif 2345 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName"); 2346} 2347 2348 2349/** 2350 * find the update serial number - this is the core of the ldap cache 2351 * @param ads connection to ads server 2352 * @param ads connection to ADS server 2353 * @param usn Pointer to retrieved update serial number 2354 * @return status of search 2355 **/ 2356ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn) 2357{ 2358 const char *attrs[] = {"highestCommittedUSN", NULL}; 2359 ADS_STATUS status; 2360 void *res; 2361 2362 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); 2363 if (!ADS_ERR_OK(status)) 2364 return status; 2365 2366 if (ads_count_replies(ads, res) != 1) { 2367 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); 2368 } 2369 2370 ads_pull_uint32(ads, res, "highestCommittedUSN", usn); 2371 ads_msgfree(ads, res); 2372 return ADS_SUCCESS; 2373} 2374 2375/* parse a ADS timestring - typical string is 2376 '20020917091222.0Z0' which means 09:12.22 17th September 2377 2002, timezone 0 */ 2378static time_t ads_parse_time(const char *str) 2379{ 2380 struct tm tm; 2381 2382 ZERO_STRUCT(tm); 2383 2384 if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 2385 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 2386 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 2387 return 0; 2388 } 2389 tm.tm_year -= 1900; 2390 tm.tm_mon -= 1; 2391 2392 return timegm(&tm); 2393} 2394 2395 2396/** 2397 * Find the servers name and realm - this can be done before authentication 2398 * The ldapServiceName field on w2k looks like this: 2399 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG 2400 * @param ads connection to ads server 2401 * @return status of search 2402 **/ 2403ADS_STATUS ads_server_info(ADS_STRUCT *ads) 2404{ 2405 const char *attrs[] = {"ldapServiceName", "currentTime", NULL}; 2406 ADS_STATUS status; 2407 void *res; 2408 char *value; 2409 char *p; 2410 char *timestr; 2411 TALLOC_CTX *ctx; 2412 2413 if (!(ctx = talloc_init("ads_server_info"))) { 2414 return ADS_ERROR(LDAP_NO_MEMORY); 2415 } 2416 2417 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); 2418 if (!ADS_ERR_OK(status)) { 2419 talloc_destroy(ctx); 2420 return status; 2421 } 2422 2423 value = ads_pull_string(ads, ctx, res, "ldapServiceName"); 2424 if (!value) { 2425 ads_msgfree(ads, res); 2426 talloc_destroy(ctx); 2427 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); 2428 } 2429 2430 timestr = ads_pull_string(ads, ctx, res, "currentTime"); 2431 if (!timestr) { 2432 ads_msgfree(ads, res); 2433 talloc_destroy(ctx); 2434 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); 2435 } 2436 2437 ads_msgfree(ads, res); 2438 2439 p = strchr(value, ':'); 2440 if (!p) { 2441 talloc_destroy(ctx); 2442 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' " 2443 "so was deemed invalid\n")); 2444 return ADS_ERROR(LDAP_DECODING_ERROR); 2445 } 2446 2447 SAFE_FREE(ads->config.ldap_server_name); 2448 2449 ads->config.ldap_server_name = SMB_STRDUP(p+1); 2450 p = strchr(ads->config.ldap_server_name, '$'); 2451 if (!p || p[1] != '@') { 2452 talloc_destroy(ctx); 2453 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'" 2454 " so was deemed invalid\n", ads->config.ldap_server_name)); 2455 SAFE_FREE(ads->config.ldap_server_name); 2456 return ADS_ERROR(LDAP_DECODING_ERROR); 2457 } 2458 2459 *p = 0; 2460 2461 SAFE_FREE(ads->config.realm); 2462 SAFE_FREE(ads->config.bind_path); 2463 2464 ads->config.realm = SMB_STRDUP(p+2); 2465 ads->config.bind_path = ads_build_dn(ads->config.realm); 2466 2467 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 2468 ads->config.ldap_server_name, ads->config.realm, 2469 ads->config.bind_path)); 2470 2471 ads->config.current_time = ads_parse_time(timestr); 2472 2473 if (ads->config.current_time != 0) { 2474 ads->auth.time_offset = ads->config.current_time - time(NULL); 2475 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset)); 2476 } 2477 2478 talloc_destroy(ctx); 2479 2480 return ADS_SUCCESS; 2481} 2482 2483/** 2484 * find the domain sid for our domain 2485 * @param ads connection to ads server 2486 * @param sid Pointer to domain sid 2487 * @return status of search 2488 **/ 2489ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid) 2490{ 2491 const char *attrs[] = {"objectSid", NULL}; 2492 void *res; 2493 ADS_STATUS rc; 2494 2495 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 2496 attrs, &res); 2497 if (!ADS_ERR_OK(rc)) return rc; 2498 if (!ads_pull_sid(ads, res, "objectSid", sid)) { 2499 ads_msgfree(ads, res); 2500 return ADS_ERROR_SYSTEM(ENOENT); 2501 } 2502 ads_msgfree(ads, res); 2503 2504 return ADS_SUCCESS; 2505} 2506 2507/* this is rather complex - we need to find the allternate (netbios) name 2508 for the domain, but there isn't a simple query to do this. Instead 2509 we look for the principle names on the DCs account and find one that has 2510 the right form, then extract the netbios name of the domain from that 2511 2512 NOTE! better method is this: 2513 2514bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName 2515 2516but you need to force the bind path to match the configurationNamingContext from the rootDSE 2517 2518*/ 2519ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup) 2520{ 2521 char *expr; 2522 ADS_STATUS rc; 2523 char **principles; 2524 char *prefix; 2525 int prefix_length; 2526 int i; 2527 void *res; 2528 const char *attrs[] = {"servicePrincipalName", NULL}; 2529 size_t num_principals; 2530 2531 (*workgroup) = NULL; 2532 2533 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))", 2534 ads->config.ldap_server_name, ads->config.realm); 2535 if (expr == NULL) { 2536 ADS_ERROR_NT(NT_STATUS_NO_MEMORY); 2537 } 2538 2539 rc = ads_search(ads, &res, expr, attrs); 2540 free(expr); 2541 2542 if (!ADS_ERR_OK(rc)) { 2543 return rc; 2544 } 2545 2546 principles = ads_pull_strings(ads, mem_ctx, res, 2547 "servicePrincipalName", &num_principals); 2548 2549 ads_msgfree(ads, res); 2550 2551 if (!principles) { 2552 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); 2553 } 2554 2555 asprintf(&prefix, "HOST/%s.%s/", 2556 ads->config.ldap_server_name, 2557 ads->config.realm); 2558 2559 prefix_length = strlen(prefix); 2560 2561 for (i=0;principles[i]; i++) { 2562 if (strnequal(principles[i], prefix, prefix_length) && 2563 !strequal(ads->config.realm, principles[i]+prefix_length) && 2564 !strchr(principles[i]+prefix_length, '.')) { 2565 /* found an alternate (short) name for the domain. */ 2566 DEBUG(3,("Found alternate name '%s' for realm '%s'\n", 2567 principles[i]+prefix_length, 2568 ads->config.realm)); 2569 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length); 2570 break; 2571 } 2572 } 2573 free(prefix); 2574 2575 if (!*workgroup) { 2576 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); 2577 } 2578 2579 return ADS_SUCCESS; 2580} 2581 2582#endif 2583