root/source3/libads/ldap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gotalarm_sig
  2. ldap_open_with_timeout
  3. ldap_search_with_timeout
  4. ads_sitename_match
  5. ads_closest_dc
  6. ads_try_connect
  7. ads_find_dc
  8. ads_lookup_site
  9. host_dns_domain
  10. ads_connect_gc
  11. ads_connect
  12. ads_connect_user_creds
  13. ads_disconnect
  14. dup_berval
  15. ads_dup_values
  16. ads_push_strvals
  17. ads_pull_strvals
  18. ads_do_paged_search_args
  19. ads_do_paged_search
  20. ads_do_search_all_args
  21. ads_do_search_all
  22. ads_do_search_all_sd_flags
  23. ads_do_search_all_fn
  24. ads_do_search
  25. ads_search
  26. ads_search_dn
  27. ads_msgfree
  28. ads_get_dn
  29. ads_parent_dn
  30. ads_find_machine_acct
  31. ads_init_mods
  32. ads_modlist_add
  33. ads_mod_str
  34. ads_mod_strlist
  35. ads_mod_ber
  36. ads_gen_mod
  37. ads_gen_add
  38. ads_del_dn
  39. ads_ou_string
  40. ads_default_ou_string
  41. ads_add_strlist
  42. ads_get_kvno
  43. ads_get_machine_kvno
  44. ads_clear_service_principal_names
  45. ads_add_service_principal_name
  46. ads_create_machine_acct
  47. ads_move_machine_acct
  48. dump_binary
  49. dump_guid
  50. dump_sid
  51. dump_sd
  52. dump_string
  53. ads_dump_field
  54. ads_dump
  55. ads_process_results
  56. ads_count_replies
  57. ads_first_entry
  58. ads_next_entry
  59. ads_first_message
  60. ads_next_message
  61. ads_pull_string
  62. ads_pull_strings
  63. ads_pull_strings_range
  64. ads_pull_uint32
  65. ads_pull_guid
  66. ads_pull_sid
  67. ads_pull_sids
  68. ads_pull_sd
  69. ads_pull_username
  70. ads_USN
  71. ads_parse_time
  72. ads_current_time
  73. ads_domain_func_level
  74. ads_domain_sid
  75. ads_site_dn
  76. ads_site_dn_for_machine
  77. ads_upn_suffixes
  78. ads_get_joinable_ous
  79. ads_get_sid_from_extended_dn
  80. ads_pull_sids_from_extendeddn
  81. ads_get_dnshostname
  82. ads_get_upn
  83. ads_get_samaccountname
  84. ads_join_realm
  85. ads_leave_realm
  86. ads_get_tokensids
  87. ads_find_samaccount
  88. ads_config_path
  89. ads_get_extended_right_name_by_guid
  90. ads_check_ou_dn

   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    Copyright (C) Guenther Deschner 2005
   8    Copyright (C) Gerald Carter 2006
   9    
  10    This program is free software; you can redistribute it and/or modify
  11    it under the terms of the GNU General Public License as published by
  12    the Free Software Foundation; either version 3 of the License, or
  13    (at your option) any later version.
  14    
  15    This program is distributed in the hope that it will be useful,
  16    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18    GNU General Public License for more details.
  19    
  20    You should have received a copy of the GNU General Public License
  21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  22 */
  23 
  24 #include "includes.h"
  25 #include "lib/ldb/include/includes.h"
  26 
  27 #ifdef HAVE_LDAP
  28 
  29 /**
  30  * @file ldap.c
  31  * @brief basic ldap client-side routines for ads server communications
  32  *
  33  * The routines contained here should do the necessary ldap calls for
  34  * ads setups.
  35  * 
  36  * Important note: attribute names passed into ads_ routines must
  37  * already be in UTF-8 format.  We do not convert them because in almost
  38  * all cases, they are just ascii (which is represented with the same
  39  * codepoints in UTF-8).  This may have to change at some point
  40  **/
  41 
  42 
  43 #define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
  44 
  45 static SIG_ATOMIC_T gotalarm;
  46 
  47 /***************************************************************
  48  Signal function to tell us we timed out.
  49 ****************************************************************/
  50 
  51 static void gotalarm_sig(void)
     /* [<][>][^][v][top][bottom][index][help] */
  52 {
  53         gotalarm = 1;
  54 }
  55 
  56  LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
     /* [<][>][^][v][top][bottom][index][help] */
  57 {
  58         LDAP *ldp = NULL;
  59 
  60 
  61         DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
  62                    "%u seconds\n", server, port, to));
  63 
  64         /* Setup timeout */
  65         gotalarm = 0;
  66         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
  67         alarm(to);
  68         /* End setup timeout. */
  69 
  70         ldp = ldap_open(server, port);
  71 
  72         if (ldp == NULL) {
  73                 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
  74                          server, port, strerror(errno)));
  75         } else {
  76                 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
  77         }
  78 
  79         /* Teardown timeout. */
  80         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
  81         alarm(0);
  82 
  83         return ldp;
  84 }
  85 
  86 static int ldap_search_with_timeout(LDAP *ld,
     /* [<][>][^][v][top][bottom][index][help] */
  87                                     LDAP_CONST char *base,
  88                                     int scope,
  89                                     LDAP_CONST char *filter,
  90                                     char **attrs,
  91                                     int attrsonly,
  92                                     LDAPControl **sctrls,
  93                                     LDAPControl **cctrls,
  94                                     int sizelimit,
  95                                     LDAPMessage **res )
  96 {
  97         struct timeval timeout;
  98         int result;
  99 
 100         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
 101         timeout.tv_sec = lp_ldap_timeout();
 102         timeout.tv_usec = 0;
 103 
 104         /* Setup alarm timeout.... Do we need both of these ? JRA. */
 105         gotalarm = 0;
 106         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
 107         alarm(lp_ldap_timeout());
 108         /* End setup timeout. */
 109 
 110         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
 111                                    attrsonly, sctrls, cctrls, &timeout,
 112                                    sizelimit, res);
 113 
 114         /* Teardown timeout. */
 115         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
 116         alarm(0);
 117 
 118         if (gotalarm != 0)
 119                 return LDAP_TIMELIMIT_EXCEEDED;
 120 
 121         /*
 122          * A bug in OpenLDAP means ldap_search_ext_s can return
 123          * LDAP_SUCCESS but with a NULL res pointer. Cope with
 124          * this. See bug #6279 for details. JRA.
 125          */
 126 
 127         if (*res == NULL) {
 128                 return LDAP_TIMELIMIT_EXCEEDED;
 129         }
 130 
 131         return result;
 132 }
 133 
 134 /**********************************************
 135  Do client and server sitename match ?
 136 **********************************************/
 137 
 138 bool ads_sitename_match(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
 139 {
 140         if (ads->config.server_site_name == NULL &&
 141             ads->config.client_site_name == NULL ) {
 142                 DEBUG(10,("ads_sitename_match: both null\n"));
 143                 return True;
 144         }
 145         if (ads->config.server_site_name &&
 146             ads->config.client_site_name &&
 147             strequal(ads->config.server_site_name,
 148                      ads->config.client_site_name)) {
 149                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
 150                 return True;
 151         }
 152         DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
 153                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
 154                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
 155         return False;
 156 }
 157 
 158 /**********************************************
 159  Is this the closest DC ?
 160 **********************************************/
 161 
 162 bool ads_closest_dc(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
 163 {
 164         if (ads->config.flags & NBT_SERVER_CLOSEST) {
 165                 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
 166                 return True;
 167         }
 168 
 169         /* not sure if this can ever happen */
 170         if (ads_sitename_match(ads)) {
 171                 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
 172                 return True;
 173         }
 174 
 175         if (ads->config.client_site_name == NULL) {
 176                 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
 177                 return True;
 178         }
 179 
 180         DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", 
 181                 ads->config.ldap_server_name));
 182 
 183         return False;
 184 }
 185 
 186 
 187 /*
 188   try a connection to a given ldap server, returning True and setting the servers IP
 189   in the ads struct if successful
 190  */
 191 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
     /* [<][>][^][v][top][bottom][index][help] */
 192 {
 193         char *srv;
 194         struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
 195         TALLOC_CTX *mem_ctx = NULL;
 196         bool ret = false;
 197 
 198         if (!server || !*server) {
 199                 return False;
 200         }
 201         
 202         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
 203                 server, ads->server.realm));
 204 
 205         mem_ctx = talloc_init("ads_try_connect");
 206         if (!mem_ctx) {
 207                 DEBUG(0,("out of memory\n"));
 208                 return false;
 209         }
 210 
 211         /* this copes with inet_ntoa brokenness */
 212         
 213         srv = SMB_STRDUP(server);
 214 
 215         ZERO_STRUCT( cldap_reply );
 216 
 217         if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
 218                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
 219                 ret = false;
 220                 goto out;
 221         }
 222 
 223         /* Check the CLDAP reply flags */
 224 
 225         if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
 226                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
 227                         srv));
 228                 ret = false;
 229                 goto out;
 230         }
 231 
 232         /* Fill in the ads->config values */
 233 
 234         SAFE_FREE(ads->config.realm);
 235         SAFE_FREE(ads->config.bind_path);
 236         SAFE_FREE(ads->config.ldap_server_name);
 237         SAFE_FREE(ads->config.server_site_name);
 238         SAFE_FREE(ads->config.client_site_name);
 239         SAFE_FREE(ads->server.workgroup);
 240 
 241         ads->config.flags              = cldap_reply.server_type;
 242         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.pdc_dns_name);
 243         ads->config.realm              = SMB_STRDUP(cldap_reply.dns_domain);
 244         strupper_m(ads->config.realm);
 245         ads->config.bind_path          = ads_build_dn(ads->config.realm);
 246         if (*cldap_reply.server_site) {
 247                 ads->config.server_site_name =
 248                         SMB_STRDUP(cldap_reply.server_site);
 249         }
 250         if (*cldap_reply.client_site) {
 251                 ads->config.client_site_name =
 252                         SMB_STRDUP(cldap_reply.client_site);
 253         }
 254         ads->server.workgroup          = SMB_STRDUP(cldap_reply.domain);
 255 
 256         ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
 257         if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
 258                 DEBUG(1,("ads_try_connect: unable to convert %s "
 259                         "to an address\n",
 260                         srv));
 261                 ret = false;
 262                 goto out;
 263         }
 264 
 265         /* Store our site name. */
 266         sitename_store( cldap_reply.domain, cldap_reply.client_site);
 267         sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
 268 
 269         ret = true;
 270  out:
 271         SAFE_FREE(srv);
 272         TALLOC_FREE(mem_ctx);
 273 
 274         return ret;
 275 }
 276 
 277 /**********************************************************************
 278  Try to find an AD dc using our internal name resolution routines
 279  Try the realm first and then then workgroup name if netbios is not 
 280  disabled
 281 **********************************************************************/
 282 
 283 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
 284 {
 285         const char *c_domain;
 286         const char *c_realm;
 287         int count, i=0;
 288         struct ip_service *ip_list;
 289         const char *realm;
 290         const char *domain;
 291         bool got_realm = False;
 292         bool use_own_domain = False;
 293         char *sitename;
 294         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
 295 
 296         /* if the realm and workgroup are both empty, assume they are ours */
 297 
 298         /* realm */
 299         c_realm = ads->server.realm;
 300 
 301         if ( !c_realm || !*c_realm ) {
 302                 /* special case where no realm and no workgroup means our own */
 303                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
 304                         use_own_domain = True;
 305                         c_realm = lp_realm();
 306                 }
 307         }
 308 
 309         if (c_realm && *c_realm)
 310                 got_realm = True;
 311 
 312         /* we need to try once with the realm name and fallback to the
 313            netbios domain name if we fail (if netbios has not been disabled */
 314 
 315         if ( !got_realm && !lp_disable_netbios() ) {
 316                 c_realm = ads->server.workgroup;
 317                 if (!c_realm || !*c_realm) {
 318                         if ( use_own_domain )
 319                                 c_realm = lp_workgroup();
 320                 }
 321         }
 322 
 323         if ( !c_realm || !*c_realm ) {
 324                 DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
 325                 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
 326         }
 327 
 328         if ( use_own_domain ) {
 329                 c_domain = lp_workgroup();
 330         } else {
 331                 c_domain = ads->server.workgroup;
 332         }
 333 
 334         realm = c_realm;
 335         domain = c_domain;
 336 
 337         /*
 338          * In case of LDAP we use get_dc_name() as that
 339          * creates the custom krb5.conf file
 340          */
 341         if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
 342                 fstring srv_name;
 343                 struct sockaddr_storage ip_out;
 344 
 345                 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
 346                         (got_realm ? "realm" : "domain"), realm));
 347 
 348                 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
 349                         /*
 350                          * we call ads_try_connect() to fill in the
 351                          * ads->config details
 352                          */
 353                         if (ads_try_connect(ads, srv_name, false)) {
 354                                 return NT_STATUS_OK;
 355                         }
 356                 }
 357 
 358                 return NT_STATUS_NO_LOGON_SERVERS;
 359         }
 360 
 361         sitename = sitename_fetch(realm);
 362 
 363  again:
 364 
 365         DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
 366                 (got_realm ? "realm" : "domain"), realm));
 367 
 368         status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
 369         if (!NT_STATUS_IS_OK(status)) {
 370                 /* fall back to netbios if we can */
 371                 if ( got_realm && !lp_disable_netbios() ) {
 372                         got_realm = False;
 373                         goto again;
 374                 }
 375 
 376                 SAFE_FREE(sitename);
 377                 return status;
 378         }
 379 
 380         /* if we fail this loop, then giveup since all the IP addresses returned were dead */
 381         for ( i=0; i<count; i++ ) {
 382                 char server[INET6_ADDRSTRLEN];
 383 
 384                 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
 385 
 386                 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
 387                         continue;
 388 
 389                 if (!got_realm) {
 390                         /* realm in this case is a workgroup name. We need
 391                            to ignore any IP addresses in the negative connection
 392                            cache that match ip addresses returned in the ad realm
 393                            case. It sucks that I have to reproduce the logic above... */
 394                         c_realm = ads->server.realm;
 395                         if ( !c_realm || !*c_realm ) {
 396                                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
 397                                         c_realm = lp_realm();
 398                                 }
 399                         }
 400                         if (c_realm && *c_realm &&
 401                                         !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
 402                                 /* Ensure we add the workgroup name for this
 403                                    IP address as negative too. */
 404                                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
 405                                 continue;
 406                         }
 407                 }
 408 
 409                 if ( ads_try_connect(ads, server, false) ) {
 410                         SAFE_FREE(ip_list);
 411                         SAFE_FREE(sitename);
 412                         return NT_STATUS_OK;
 413                 }
 414                 
 415                 /* keep track of failures */
 416                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
 417         }
 418 
 419         SAFE_FREE(ip_list);
 420 
 421         /* In case we failed to contact one of our closest DC on our site we
 422          * need to try to find another DC, retry with a site-less SRV DNS query
 423          * - Guenther */
 424 
 425         if (sitename) {
 426                 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
 427                                 "trying to find another DC\n", sitename));
 428                 SAFE_FREE(sitename);
 429                 namecache_delete(realm, 0x1C);
 430                 goto again;
 431         }
 432 
 433         return NT_STATUS_NO_LOGON_SERVERS;
 434 }
 435 
 436 /*********************************************************************
 437  *********************************************************************/
 438 
 439 static NTSTATUS ads_lookup_site(void)
     /* [<][>][^][v][top][bottom][index][help] */
 440 {
 441         ADS_STRUCT *ads = NULL;
 442         ADS_STATUS ads_status;
 443         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
 444 
 445         ads = ads_init(lp_realm(), NULL, NULL);
 446         if (!ads) {
 447                 return NT_STATUS_NO_MEMORY;
 448         }
 449 
 450         /* The NO_BIND here will find a DC and set the client site
 451            but not establish the TCP connection */
 452 
 453         ads->auth.flags = ADS_AUTH_NO_BIND;
 454         ads_status = ads_connect(ads);
 455         if (!ADS_ERR_OK(ads_status)) {
 456                 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
 457                           ads_errstr(ads_status)));
 458         }
 459         nt_status = ads_ntstatus(ads_status);
 460 
 461         if (ads) {
 462                 ads_destroy(&ads);
 463         }
 464 
 465         return nt_status;
 466 }
 467 
 468 /*********************************************************************
 469  *********************************************************************/
 470 
 471 static const char* host_dns_domain(const char *fqdn)
     /* [<][>][^][v][top][bottom][index][help] */
 472 {
 473         const char *p = fqdn;
 474 
 475         /* go to next char following '.' */
 476 
 477         if ((p = strchr_m(fqdn, '.')) != NULL) {
 478                 p++;
 479         }
 480 
 481         return p;
 482 }
 483 
 484 
 485 /**
 486  * Connect to the Global Catalog server
 487  * @param ads Pointer to an existing ADS_STRUCT
 488  * @return status of connection
 489  *
 490  * Simple wrapper around ads_connect() that fills in the
 491  * GC ldap server information
 492  **/
 493 
 494 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
 495 {
 496         TALLOC_CTX *frame = talloc_stackframe();
 497         struct dns_rr_srv *gcs_list;
 498         int num_gcs;
 499         char *realm = ads->server.realm;
 500         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
 501         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
 502         int i;
 503         bool done = false;
 504         char *sitename = NULL;
 505 
 506         if (!realm)
 507                 realm = lp_realm();
 508 
 509         if ((sitename = sitename_fetch(realm)) == NULL) {
 510                 ads_lookup_site();
 511                 sitename = sitename_fetch(realm);
 512         }
 513 
 514         do {
 515                 /* We try once with a sitename and once without
 516                    (unless we don't have a sitename and then we're
 517                    done */
 518 
 519                 if (sitename == NULL)
 520                         done = true;
 521 
 522                 nt_status = ads_dns_query_gcs(frame, realm, sitename,
 523                                               &gcs_list, &num_gcs);
 524 
 525                 SAFE_FREE(sitename);
 526 
 527                 if (!NT_STATUS_IS_OK(nt_status)) {
 528                         ads_status = ADS_ERROR_NT(nt_status);
 529                         goto done;
 530                 }
 531 
 532                 /* Loop until we get a successful connection or have gone
 533                    through them all.  When connecting a GC server, make sure that
 534                    the realm is the server's DNS name and not the forest root */
 535 
 536                 for (i=0; i<num_gcs; i++) {
 537                         ads->server.gc = true;
 538                         ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
 539                         ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
 540                         ads_status = ads_connect(ads);
 541                         if (ADS_ERR_OK(ads_status)) {
 542                                 /* Reset the bind_dn to "".  A Global Catalog server
 543                                    may host  multiple domain trees in a forest.
 544                                    Windows 2003 GC server will accept "" as the search
 545                                    path to imply search all domain trees in the forest */
 546 
 547                                 SAFE_FREE(ads->config.bind_path);
 548                                 ads->config.bind_path = SMB_STRDUP("");
 549 
 550 
 551                                 goto done;
 552                         }
 553                         SAFE_FREE(ads->server.ldap_server);
 554                         SAFE_FREE(ads->server.realm);
 555                 }
 556 
 557                 TALLOC_FREE(gcs_list);
 558                 num_gcs = 0;
 559         } while (!done);
 560 
 561 done:
 562         SAFE_FREE(sitename);
 563         talloc_destroy(frame);
 564 
 565         return ads_status;
 566 }
 567 
 568 
 569 /**
 570  * Connect to the LDAP server
 571  * @param ads Pointer to an existing ADS_STRUCT
 572  * @return status of connection
 573  **/
 574 ADS_STATUS ads_connect(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
 575 {
 576         int version = LDAP_VERSION3;
 577         ADS_STATUS status;
 578         NTSTATUS ntstatus;
 579         char addr[INET6_ADDRSTRLEN];
 580 
 581         ZERO_STRUCT(ads->ldap);
 582         ads->ldap.last_attempt  = time(NULL);
 583         ads->ldap.wrap_type     = ADS_SASLWRAP_TYPE_PLAIN;
 584 
 585         /* try with a user specified server */
 586 
 587         if (DEBUGLEVEL >= 11) {
 588                 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
 589                 DEBUG(11,("ads_connect: entering\n"));
 590                 DEBUGADD(11,("%s\n", s));
 591                 TALLOC_FREE(s);
 592         }
 593 
 594         if (ads->server.ldap_server)
 595         {
 596                 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
 597                         goto got_connection;
 598                 }
 599 
 600                 /* The choice of which GC use is handled one level up in
 601                    ads_connect_gc().  If we continue on from here with
 602                    ads_find_dc() we will get GC searches on port 389 which
 603                    doesn't work.   --jerry */
 604 
 605                 if (ads->server.gc == true) {
 606                         return ADS_ERROR(LDAP_OPERATIONS_ERROR);
 607                 }
 608         }
 609 
 610         ntstatus = ads_find_dc(ads);
 611         if (NT_STATUS_IS_OK(ntstatus)) {
 612                 goto got_connection;
 613         }
 614 
 615         status = ADS_ERROR_NT(ntstatus);
 616         goto out;
 617 
 618 got_connection:
 619 
 620         print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
 621         DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
 622 
 623         if (!ads->auth.user_name) {
 624                 /* Must use the userPrincipalName value here or sAMAccountName
 625                    and not servicePrincipalName; found by Guenther Deschner */
 626 
 627                 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
 628                         DEBUG(0,("ads_connect: asprintf fail.\n"));
 629                         ads->auth.user_name = NULL;
 630                 }
 631         }
 632 
 633         if (!ads->auth.realm) {
 634                 ads->auth.realm = SMB_STRDUP(ads->config.realm);
 635         }
 636 
 637         if (!ads->auth.kdc_server) {
 638                 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
 639                 ads->auth.kdc_server = SMB_STRDUP(addr);
 640         }
 641 
 642 #if KRB5_DNS_HACK
 643         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
 644            to MIT kerberos to work (tridge) */
 645         {
 646                 char *env = NULL;
 647                 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
 648                         setenv(env, ads->auth.kdc_server, 1);
 649                         free(env);
 650                 }
 651         }
 652 #endif
 653 
 654         /* If the caller() requested no LDAP bind, then we are done */
 655         
 656         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
 657                 status = ADS_SUCCESS;
 658                 goto out;
 659         }
 660 
 661         ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
 662         if (!ads->ldap.mem_ctx) {
 663                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
 664                 goto out;
 665         }
 666         
 667         /* Otherwise setup the TCP LDAP session */
 668 
 669         ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
 670                                               ads->ldap.port, lp_ldap_timeout());
 671         if (ads->ldap.ld == NULL) {
 672                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
 673                 goto out;
 674         }
 675         DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
 676 
 677         /* cache the successful connection for workgroup and realm */
 678         if (ads_closest_dc(ads)) {
 679                 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
 680                 saf_store( ads->server.realm, ads->config.ldap_server_name);
 681         }
 682 
 683         ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
 684 
 685         if ( lp_ldap_ssl_ads() ) {
 686                 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
 687                 if (!ADS_ERR_OK(status)) {
 688                         goto out;
 689                 }
 690         }
 691 
 692         /* fill in the current time and offsets */
 693         
 694         status = ads_current_time( ads );
 695         if ( !ADS_ERR_OK(status) ) {
 696                 goto out;
 697         }
 698 
 699         /* Now do the bind */
 700         
 701         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
 702                 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
 703                 goto out;
 704         }
 705 
 706         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
 707                 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
 708                 goto out;
 709         }
 710 
 711         status = ads_sasl_bind(ads);
 712 
 713  out:
 714         if (DEBUGLEVEL >= 11) {
 715                 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
 716                 DEBUG(11,("ads_connect: leaving with: %s\n",
 717                         ads_errstr(status)));
 718                 DEBUGADD(11,("%s\n", s));
 719                 TALLOC_FREE(s);
 720         }
 721 
 722         return status;
 723 }
 724 
 725 /**
 726  * Connect to the LDAP server using given credentials
 727  * @param ads Pointer to an existing ADS_STRUCT
 728  * @return status of connection
 729  **/
 730 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
 731 {
 732         ads->auth.flags |= ADS_AUTH_USER_CREDS;
 733 
 734         return ads_connect(ads);
 735 }
 736 
 737 /**
 738  * Disconnect the LDAP server
 739  * @param ads Pointer to an existing ADS_STRUCT
 740  **/
 741 void ads_disconnect(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
 742 {
 743         if (ads->ldap.ld) {
 744                 ldap_unbind(ads->ldap.ld);
 745                 ads->ldap.ld = NULL;
 746         }
 747         if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
 748                 ads->ldap.wrap_ops->disconnect(ads);
 749         }
 750         if (ads->ldap.mem_ctx) {
 751                 talloc_free(ads->ldap.mem_ctx);
 752         }
 753         ZERO_STRUCT(ads->ldap);
 754 }
 755 
 756 /*
 757   Duplicate a struct berval into talloc'ed memory
 758  */
 759 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
     /* [<][>][^][v][top][bottom][index][help] */
 760 {
 761         struct berval *value;
 762 
 763         if (!in_val) return NULL;
 764 
 765         value = TALLOC_ZERO_P(ctx, struct berval);
 766         if (value == NULL)
 767                 return NULL;
 768         if (in_val->bv_len == 0) return value;
 769 
 770         value->bv_len = in_val->bv_len;
 771         value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
 772                                               in_val->bv_len);
 773         return value;
 774 }
 775 
 776 /*
 777   Make a values list out of an array of (struct berval *)
 778  */
 779 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
     /* [<][>][^][v][top][bottom][index][help] */
 780                                       const struct berval **in_vals)
 781 {
 782         struct berval **values;
 783         int i;
 784        
 785         if (!in_vals) return NULL;
 786         for (i=0; in_vals[i]; i++)
 787                 ; /* count values */
 788         values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
 789         if (!values) return NULL;
 790 
 791         for (i=0; in_vals[i]; i++) {
 792                 values[i] = dup_berval(ctx, in_vals[i]);
 793         }
 794         return values;
 795 }
 796 
 797 /*
 798   UTF8-encode a values list out of an array of (char *)
 799  */
 800 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
     /* [<][>][^][v][top][bottom][index][help] */
 801 {
 802         char **values;
 803         int i;
 804         size_t size;
 805 
 806         if (!in_vals) return NULL;
 807         for (i=0; in_vals[i]; i++)
 808                 ; /* count values */
 809         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
 810         if (!values) return NULL;
 811 
 812         for (i=0; in_vals[i]; i++) {
 813                 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
 814                         TALLOC_FREE(values);
 815                         return NULL;
 816                 }
 817         }
 818         return values;
 819 }
 820 
 821 /*
 822   Pull a (char *) array out of a UTF8-encoded values list
 823  */
 824 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
     /* [<][>][^][v][top][bottom][index][help] */
 825 {
 826         char **values;
 827         int i;
 828         size_t converted_size;
 829        
 830         if (!in_vals) return NULL;
 831         for (i=0; in_vals[i]; i++)
 832                 ; /* count values */
 833         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
 834         if (!values) return NULL;
 835 
 836         for (i=0; in_vals[i]; i++) {
 837                 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
 838                                       &converted_size)) {
 839                         DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
 840                                  "%s", strerror(errno)));
 841                 }
 842         }
 843         return values;
 844 }
 845 
 846 /**
 847  * Do a search with paged results.  cookie must be null on the first
 848  *  call, and then returned on each subsequent call.  It will be null
 849  *  again when the entire search is complete 
 850  * @param ads connection to ads server 
 851  * @param bind_path Base dn for the search
 852  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
 853  * @param expr Search expression - specified in local charset
 854  * @param attrs Attributes to retrieve - specified in utf8 or ascii
 855  * @param res ** which will contain results - free res* with ads_msgfree()
 856  * @param count Number of entries retrieved on this page
 857  * @param cookie The paged results cookie to be returned on subsequent calls
 858  * @return status of search
 859  **/
 860 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
     /* [<][>][^][v][top][bottom][index][help] */
 861                                            const char *bind_path,
 862                                            int scope, const char *expr,
 863                                            const char **attrs, void *args,
 864                                            LDAPMessage **res, 
 865                                            int *count, struct berval **cookie)
 866 {
 867         int rc, i, version;
 868         char *utf8_expr, *utf8_path, **search_attrs = NULL;
 869         size_t converted_size;
 870         LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
 871         BerElement *cookie_be = NULL;
 872         struct berval *cookie_bv= NULL;
 873         BerElement *ext_be = NULL;
 874         struct berval *ext_bv= NULL;
 875 
 876         TALLOC_CTX *ctx;
 877         ads_control *external_control = (ads_control *) args;
 878 
 879         *res = NULL;
 880 
 881         if (!(ctx = talloc_init("ads_do_paged_search_args")))
 882                 return ADS_ERROR(LDAP_NO_MEMORY);
 883 
 884         /* 0 means the conversion worked but the result was empty 
 885            so we only fail if it's -1.  In any case, it always 
 886            at least nulls out the dest */
 887         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
 888             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
 889         {
 890                 rc = LDAP_NO_MEMORY;
 891                 goto done;
 892         }
 893 
 894         if (!attrs || !(*attrs))
 895                 search_attrs = NULL;
 896         else {
 897                 /* This would be the utf8-encoded version...*/
 898                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
 899                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
 900                         rc = LDAP_NO_MEMORY;
 901                         goto done;
 902                 }
 903         }
 904                 
 905         /* Paged results only available on ldap v3 or later */
 906         ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
 907         if (version < LDAP_VERSION3) {
 908                 rc =  LDAP_NOT_SUPPORTED;
 909                 goto done;
 910         }
 911 
 912         cookie_be = ber_alloc_t(LBER_USE_DER);
 913         if (*cookie) {
 914                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
 915                 ber_bvfree(*cookie); /* don't need it from last time */
 916                 *cookie = NULL;
 917         } else {
 918                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
 919         }
 920         ber_flatten(cookie_be, &cookie_bv);
 921         PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
 922         PagedResults.ldctl_iscritical = (char) 1;
 923         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
 924         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
 925 
 926         NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
 927         NoReferrals.ldctl_iscritical = (char) 0;
 928         NoReferrals.ldctl_value.bv_len = 0;
 929         NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
 930 
 931         if (external_control && 
 932             (strequal(external_control->control, ADS_EXTENDED_DN_OID) || 
 933              strequal(external_control->control, ADS_SD_FLAGS_OID))) {
 934 
 935                 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
 936                 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
 937 
 938                 /* win2k does not accept a ldctl_value beeing passed in */
 939 
 940                 if (external_control->val != 0) {
 941 
 942                         if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
 943                                 rc = LDAP_NO_MEMORY;
 944                                 goto done;
 945                         }
 946 
 947                         if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
 948                                 rc = LDAP_NO_MEMORY;
 949                                 goto done;
 950                         }
 951                         if ((ber_flatten(ext_be, &ext_bv)) == -1) {
 952                                 rc = LDAP_NO_MEMORY;
 953                                 goto done;
 954                         }
 955 
 956                         ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
 957                         ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
 958 
 959                 } else {
 960                         ExternalCtrl.ldctl_value.bv_len = 0;
 961                         ExternalCtrl.ldctl_value.bv_val = NULL;
 962                 }
 963 
 964                 controls[0] = &NoReferrals;
 965                 controls[1] = &PagedResults;
 966                 controls[2] = &ExternalCtrl;
 967                 controls[3] = NULL;
 968 
 969         } else {
 970                 controls[0] = &NoReferrals;
 971                 controls[1] = &PagedResults;
 972                 controls[2] = NULL;
 973         }
 974 
 975         /* we need to disable referrals as the openldap libs don't
 976            handle them and paged results at the same time.  Using them
 977            together results in the result record containing the server 
 978            page control being removed from the result list (tridge/jmcd) 
 979         
 980            leaving this in despite the control that says don't generate
 981            referrals, in case the server doesn't support it (jmcd)
 982         */
 983         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 984 
 985         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, 
 986                                       search_attrs, 0, controls,
 987                                       NULL, LDAP_NO_LIMIT,
 988                                       (LDAPMessage **)res);
 989 
 990         ber_free(cookie_be, 1);
 991         ber_bvfree(cookie_bv);
 992 
 993         if (rc) {
 994                 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
 995                          ldap_err2string(rc)));
 996                 goto done;
 997         }
 998 
 999         rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1000                                         NULL, &rcontrols,  0);
1001 
1002         if (!rcontrols) {
1003                 goto done;
1004         }
1005 
1006         for (i=0; rcontrols[i]; i++) {
1007                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1008                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1009                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1010                                   &cookie_bv);
1011                         /* the berval is the cookie, but must be freed when
1012                            it is all done */
1013                         if (cookie_bv->bv_len) /* still more to do */
1014                                 *cookie=ber_bvdup(cookie_bv);
1015                         else
1016                                 *cookie=NULL;
1017                         ber_bvfree(cookie_bv);
1018                         ber_free(cookie_be, 1);
1019                         break;
1020                 }
1021         }
1022         ldap_controls_free(rcontrols);
1023 
1024 done:
1025         talloc_destroy(ctx);
1026 
1027         if (ext_be) {
1028                 ber_free(ext_be, 1);
1029         }
1030 
1031         if (ext_bv) {
1032                 ber_bvfree(ext_bv);
1033         }
1034  
1035         /* if/when we decide to utf8-encode attrs, take out this next line */
1036         TALLOC_FREE(search_attrs);
1037 
1038         return ADS_ERROR(rc);
1039 }
1040 
1041 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
     /* [<][>][^][v][top][bottom][index][help] */
1042                                       int scope, const char *expr,
1043                                       const char **attrs, LDAPMessage **res, 
1044                                       int *count, struct berval **cookie)
1045 {
1046         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1047 }
1048 
1049 
1050 /**
1051  * Get all results for a search.  This uses ads_do_paged_search() to return 
1052  * all entries in a large search.
1053  * @param ads connection to ads server 
1054  * @param bind_path Base dn for the search
1055  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1056  * @param expr Search expression
1057  * @param attrs Attributes to retrieve
1058  * @param res ** which will contain results - free res* with ads_msgfree()
1059  * @return status of search
1060  **/
1061  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
     /* [<][>][^][v][top][bottom][index][help] */
1062                                    int scope, const char *expr,
1063                                    const char **attrs, void *args,
1064                                    LDAPMessage **res)
1065 {
1066         struct berval *cookie = NULL;
1067         int count = 0;
1068         ADS_STATUS status;
1069 
1070         *res = NULL;
1071         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1072                                      &count, &cookie);
1073 
1074         if (!ADS_ERR_OK(status)) 
1075                 return status;
1076 
1077 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1078         while (cookie) {
1079                 LDAPMessage *res2 = NULL;
1080                 ADS_STATUS status2;
1081                 LDAPMessage *msg, *next;
1082 
1083                 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
1084                                               attrs, args, &res2, &count, &cookie);
1085 
1086                 if (!ADS_ERR_OK(status2)) break;
1087 
1088                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1089                    that this works on all ldap libs, but I have only tested with openldap */
1090                 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1091                         next = ads_next_message(ads, msg);
1092                         ldap_add_result_entry((LDAPMessage **)res, msg);
1093                 }
1094                 /* note that we do not free res2, as the memory is now
1095                    part of the main returned list */
1096         }
1097 #else
1098         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1099         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1100 #endif
1101 
1102         return status;
1103 }
1104 
1105  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
     /* [<][>][^][v][top][bottom][index][help] */
1106                               int scope, const char *expr,
1107                               const char **attrs, LDAPMessage **res)
1108 {
1109         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1110 }
1111 
1112  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
     /* [<][>][^][v][top][bottom][index][help] */
1113                                        int scope, const char *expr,
1114                                        const char **attrs, uint32 sd_flags, 
1115                                        LDAPMessage **res)
1116 {
1117         ads_control args;
1118 
1119         args.control = ADS_SD_FLAGS_OID;
1120         args.val = sd_flags;
1121         args.critical = True;
1122 
1123         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1124 }
1125 
1126 
1127 /**
1128  * Run a function on all results for a search.  Uses ads_do_paged_search() and
1129  *  runs the function as each page is returned, using ads_process_results()
1130  * @param ads connection to ads server
1131  * @param bind_path Base dn for the search
1132  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1133  * @param expr Search expression - specified in local charset
1134  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1135  * @param fn Function which takes attr name, values list, and data_area
1136  * @param data_area Pointer which is passed to function on each call
1137  * @return status of search
1138  **/
1139 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
     /* [<][>][^][v][top][bottom][index][help] */
1140                                 int scope, const char *expr, const char **attrs,
1141                                 bool (*fn)(ADS_STRUCT *, char *, void **, void *), 
1142                                 void *data_area)
1143 {
1144         struct berval *cookie = NULL;
1145         int count = 0;
1146         ADS_STATUS status;
1147         LDAPMessage *res;
1148 
1149         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1150                                      &count, &cookie);
1151 
1152         if (!ADS_ERR_OK(status)) return status;
1153 
1154         ads_process_results(ads, res, fn, data_area);
1155         ads_msgfree(ads, res);
1156 
1157         while (cookie) {
1158                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1159                                              &res, &count, &cookie);
1160 
1161                 if (!ADS_ERR_OK(status)) break;
1162                 
1163                 ads_process_results(ads, res, fn, data_area);
1164                 ads_msgfree(ads, res);
1165         }
1166 
1167         return status;
1168 }
1169 
1170 /**
1171  * Do a search with a timeout.
1172  * @param ads connection to ads server
1173  * @param bind_path Base dn for the search
1174  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1175  * @param expr Search expression
1176  * @param attrs Attributes to retrieve
1177  * @param res ** which will contain results - free res* with ads_msgfree()
1178  * @return status of search
1179  **/
1180  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
     /* [<][>][^][v][top][bottom][index][help] */
1181                           const char *expr,
1182                           const char **attrs, LDAPMessage **res)
1183 {
1184         int rc;
1185         char *utf8_expr, *utf8_path, **search_attrs = NULL;
1186         size_t converted_size;
1187         TALLOC_CTX *ctx;
1188 
1189         *res = NULL;
1190         if (!(ctx = talloc_init("ads_do_search"))) {
1191                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1192                 return ADS_ERROR(LDAP_NO_MEMORY);
1193         }
1194 
1195         /* 0 means the conversion worked but the result was empty 
1196            so we only fail if it's negative.  In any case, it always 
1197            at least nulls out the dest */
1198         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1199             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1200         {
1201                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1202                 rc = LDAP_NO_MEMORY;
1203                 goto done;
1204         }
1205 
1206         if (!attrs || !(*attrs))
1207                 search_attrs = NULL;
1208         else {
1209                 /* This would be the utf8-encoded version...*/
1210                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
1211                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1212                 {
1213                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1214                         rc = LDAP_NO_MEMORY;
1215                         goto done;
1216                 }
1217         }
1218 
1219         /* see the note in ads_do_paged_search - we *must* disable referrals */
1220         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1221 
1222         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1223                                       search_attrs, 0, NULL, NULL, 
1224                                       LDAP_NO_LIMIT,
1225                                       (LDAPMessage **)res);
1226 
1227         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1228                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1229                 rc = 0;
1230         }
1231 
1232  done:
1233         talloc_destroy(ctx);
1234         /* if/when we decide to utf8-encode attrs, take out this next line */
1235         TALLOC_FREE(search_attrs);
1236         return ADS_ERROR(rc);
1237 }
1238 /**
1239  * Do a general ADS search
1240  * @param ads connection to ads server
1241  * @param res ** which will contain results - free res* with ads_msgfree()
1242  * @param expr Search expression
1243  * @param attrs Attributes to retrieve
1244  * @return status of search
1245  **/
1246  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
     /* [<][>][^][v][top][bottom][index][help] */
1247                        const char *expr, const char **attrs)
1248 {
1249         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
1250                              expr, attrs, res);
1251 }
1252 
1253 /**
1254  * Do a search on a specific DistinguishedName
1255  * @param ads connection to ads server
1256  * @param res ** which will contain results - free res* with ads_msgfree()
1257  * @param dn DistinguishName to search
1258  * @param attrs Attributes to retrieve
1259  * @return status of search
1260  **/
1261  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
     /* [<][>][^][v][top][bottom][index][help] */
1262                           const char *dn, const char **attrs)
1263 {
1264         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1265                              attrs, res);
1266 }
1267 
1268 /**
1269  * Free up memory from a ads_search
1270  * @param ads connection to ads server
1271  * @param msg Search results to free
1272  **/
1273  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
     /* [<][>][^][v][top][bottom][index][help] */
1274 {
1275         if (!msg) return;
1276         ldap_msgfree(msg);
1277 }
1278 
1279 /**
1280  * Get a dn from search results
1281  * @param ads connection to ads server
1282  * @param msg Search result
1283  * @return dn string
1284  **/
1285  char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
     /* [<][>][^][v][top][bottom][index][help] */
1286 {
1287         char *utf8_dn, *unix_dn;
1288         size_t converted_size;
1289 
1290         utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1291 
1292         if (!utf8_dn) {
1293                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1294                 return NULL;
1295         }
1296 
1297         if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1298                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1299                         utf8_dn ));
1300                 return NULL;
1301         }
1302         ldap_memfree(utf8_dn);
1303         return unix_dn;
1304 }
1305 
1306 /**
1307  * Get the parent from a dn
1308  * @param dn the dn to return the parent from
1309  * @return parent dn string
1310  **/
1311 char *ads_parent_dn(const char *dn)
     /* [<][>][^][v][top][bottom][index][help] */
1312 {
1313         char *p;
1314 
1315         if (dn == NULL) {
1316                 return NULL;
1317         }
1318 
1319         p = strchr(dn, ',');
1320 
1321         if (p == NULL) {
1322                 return NULL;
1323         }
1324 
1325         return p+1;
1326 }
1327 
1328 /**
1329  * Find a machine account given a hostname
1330  * @param ads connection to ads server
1331  * @param res ** which will contain results - free res* with ads_msgfree()
1332  * @param host Hostname to search for
1333  * @return status of search
1334  **/
1335  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
     /* [<][>][^][v][top][bottom][index][help] */
1336                                   const char *machine)
1337 {
1338         ADS_STATUS status;
1339         char *expr;
1340         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1341 
1342         *res = NULL;
1343 
1344         /* the easiest way to find a machine account anywhere in the tree
1345            is to look for hostname$ */
1346         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1347                 DEBUG(1, ("asprintf failed!\n"));
1348                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1349         }
1350         
1351         status = ads_search(ads, res, expr, attrs);
1352         SAFE_FREE(expr);
1353         return status;
1354 }
1355 
1356 /**
1357  * Initialize a list of mods to be used in a modify request
1358  * @param ctx An initialized TALLOC_CTX
1359  * @return allocated ADS_MODLIST
1360  **/
1361 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
1362 {
1363 #define ADS_MODLIST_ALLOC_SIZE 10
1364         LDAPMod **mods;
1365         
1366         if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1367                 /* -1 is safety to make sure we don't go over the end.
1368                    need to reset it to NULL before doing ldap modify */
1369                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1370         
1371         return (ADS_MODLIST)mods;
1372 }
1373 
1374 
1375 /*
1376   add an attribute to the list, with values list already constructed
1377 */
1378 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
     /* [<][>][^][v][top][bottom][index][help] */
1379                                   int mod_op, const char *name, 
1380                                   const void *_invals)
1381 {
1382         const void **invals = (const void **)_invals;
1383         int curmod;
1384         LDAPMod **modlist = (LDAPMod **) *mods;
1385         struct berval **ber_values = NULL;
1386         char **char_values = NULL;
1387 
1388         if (!invals) {
1389                 mod_op = LDAP_MOD_DELETE;
1390         } else {
1391                 if (mod_op & LDAP_MOD_BVALUES)
1392                         ber_values = ads_dup_values(ctx, 
1393                                                 (const struct berval **)invals);
1394                 else
1395                         char_values = ads_push_strvals(ctx, 
1396                                                   (const char **) invals);
1397         }
1398 
1399         /* find the first empty slot */
1400         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1401              curmod++);
1402         if (modlist[curmod] == (LDAPMod *) -1) {
1403                 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1404                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1405                         return ADS_ERROR(LDAP_NO_MEMORY);
1406                 memset(&modlist[curmod], 0, 
1407                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1408                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1409                 *mods = (ADS_MODLIST)modlist;
1410         }
1411                 
1412         if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1413                 return ADS_ERROR(LDAP_NO_MEMORY);
1414         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1415         if (mod_op & LDAP_MOD_BVALUES) {
1416                 modlist[curmod]->mod_bvalues = ber_values;
1417         } else if (mod_op & LDAP_MOD_DELETE) {
1418                 modlist[curmod]->mod_values = NULL;
1419         } else {
1420                 modlist[curmod]->mod_values = char_values;
1421         }
1422 
1423         modlist[curmod]->mod_op = mod_op;
1424         return ADS_ERROR(LDAP_SUCCESS);
1425 }
1426 
1427 /**
1428  * Add a single string value to a mod list
1429  * @param ctx An initialized TALLOC_CTX
1430  * @param mods An initialized ADS_MODLIST
1431  * @param name The attribute name to add
1432  * @param val The value to add - NULL means DELETE
1433  * @return ADS STATUS indicating success of add
1434  **/
1435 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
     /* [<][>][^][v][top][bottom][index][help] */
1436                        const char *name, const char *val)
1437 {
1438         const char *values[2];
1439 
1440         values[0] = val;
1441         values[1] = NULL;
1442 
1443         if (!val)
1444                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1445         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1446 }
1447 
1448 /**
1449  * Add an array of string values to a mod list
1450  * @param ctx An initialized TALLOC_CTX
1451  * @param mods An initialized ADS_MODLIST
1452  * @param name The attribute name to add
1453  * @param vals The array of string values to add - NULL means DELETE
1454  * @return ADS STATUS indicating success of add
1455  **/
1456 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
     /* [<][>][^][v][top][bottom][index][help] */
1457                            const char *name, const char **vals)
1458 {
1459         if (!vals)
1460                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1461         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1462                                name, (const void **) vals);
1463 }
1464 
1465 #if 0
1466 /**
1467  * Add a single ber-encoded value to a mod list
1468  * @param ctx An initialized TALLOC_CTX
1469  * @param mods An initialized ADS_MODLIST
1470  * @param name The attribute name to add
1471  * @param val The value to add - NULL means DELETE
1472  * @return ADS STATUS indicating success of add
1473  **/
1474 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
     /* [<][>][^][v][top][bottom][index][help] */
1475                               const char *name, const struct berval *val)
1476 {
1477         const struct berval *values[2];
1478 
1479         values[0] = val;
1480         values[1] = NULL;
1481         if (!val)
1482                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1483         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1484                                name, (const void **) values);
1485 }
1486 #endif
1487 
1488 /**
1489  * Perform an ldap modify
1490  * @param ads connection to ads server
1491  * @param mod_dn DistinguishedName to modify
1492  * @param mods list of modifications to perform
1493  * @return status of modify
1494  **/
1495 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
     /* [<][>][^][v][top][bottom][index][help] */
1496 {
1497         int ret,i;
1498         char *utf8_dn = NULL;
1499         size_t converted_size;
1500         /* 
1501            this control is needed to modify that contains a currently 
1502            non-existent attribute (but allowable for the object) to run
1503         */
1504         LDAPControl PermitModify = {
1505                 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1506                 {0, NULL},
1507                 (char) 1};
1508         LDAPControl *controls[2];
1509 
1510         controls[0] = &PermitModify;
1511         controls[1] = NULL;
1512 
1513         if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1514                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1515         }
1516 
1517         /* find the end of the list, marked by NULL or -1 */
1518         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1519         /* make sure the end of the list is NULL */
1520         mods[i] = NULL;
1521         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1522                                 (LDAPMod **) mods, controls, NULL);
1523         SAFE_FREE(utf8_dn);
1524         return ADS_ERROR(ret);
1525 }
1526 
1527 /**
1528  * Perform an ldap add
1529  * @param ads connection to ads server
1530  * @param new_dn DistinguishedName to add
1531  * @param mods list of attributes and values for DN
1532  * @return status of add
1533  **/
1534 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
     /* [<][>][^][v][top][bottom][index][help] */
1535 {
1536         int ret, i;
1537         char *utf8_dn = NULL;
1538         size_t converted_size;
1539 
1540         if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1541                 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1542                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1543         }
1544         
1545         /* find the end of the list, marked by NULL or -1 */
1546         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1547         /* make sure the end of the list is NULL */
1548         mods[i] = NULL;
1549 
1550         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1551         SAFE_FREE(utf8_dn);
1552         return ADS_ERROR(ret);
1553 }
1554 
1555 /**
1556  * Delete a DistinguishedName
1557  * @param ads connection to ads server
1558  * @param new_dn DistinguishedName to delete
1559  * @return status of delete
1560  **/
1561 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
     /* [<][>][^][v][top][bottom][index][help] */
1562 {
1563         int ret;
1564         char *utf8_dn = NULL;
1565         size_t converted_size;
1566         if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1567                 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1568                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1569         }
1570         
1571         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1572         SAFE_FREE(utf8_dn);
1573         return ADS_ERROR(ret);
1574 }
1575 
1576 /**
1577  * Build an org unit string
1578  *  if org unit is Computers or blank then assume a container, otherwise
1579  *  assume a / separated list of organisational units.
1580  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1581  * @param ads connection to ads server
1582  * @param org_unit Organizational unit
1583  * @return org unit string - caller must free
1584  **/
1585 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
     /* [<][>][^][v][top][bottom][index][help] */
1586 {
1587         char *ret = NULL;
1588 
1589         if (!org_unit || !*org_unit) {
1590 
1591                 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1592 
1593                 /* samba4 might not yet respond to a wellknownobject-query */
1594                 return ret ? ret : SMB_STRDUP("cn=Computers");
1595         }
1596         
1597         if (strequal(org_unit, "Computers")) {
1598                 return SMB_STRDUP("cn=Computers");
1599         }
1600 
1601         /* jmcd: removed "\\" from the separation chars, because it is
1602            needed as an escape for chars like '#' which are valid in an
1603            OU name */
1604         return ads_build_path(org_unit, "/", "ou=", 1);
1605 }
1606 
1607 /**
1608  * Get a org unit string for a well-known GUID
1609  * @param ads connection to ads server
1610  * @param wknguid Well known GUID
1611  * @return org unit string - caller must free
1612  **/
1613 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
     /* [<][>][^][v][top][bottom][index][help] */
1614 {
1615         ADS_STATUS status;
1616         LDAPMessage *res = NULL;
1617         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1618                 **bind_dn_exp = NULL;
1619         const char *attrs[] = {"distinguishedName", NULL};
1620         int new_ln, wkn_ln, bind_ln, i;
1621 
1622         if (wknguid == NULL) {
1623                 return NULL;
1624         }
1625 
1626         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1627                 DEBUG(1, ("asprintf failed!\n"));
1628                 return NULL;
1629         }
1630 
1631         status = ads_search_dn(ads, &res, base, attrs);
1632         if (!ADS_ERR_OK(status)) {
1633                 DEBUG(1,("Failed while searching for: %s\n", base));
1634                 goto out;
1635         }
1636 
1637         if (ads_count_replies(ads, res) != 1) {
1638                 goto out;
1639         }
1640 
1641         /* substitute the bind-path from the well-known-guid-search result */
1642         wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1643         if (!wkn_dn) {
1644                 goto out;
1645         }
1646 
1647         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1648         if (!wkn_dn_exp) {
1649                 goto out;
1650         }
1651 
1652         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1653         if (!bind_dn_exp) {
1654                 goto out;
1655         }
1656 
1657         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1658                 ;
1659         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1660                 ;
1661 
1662         new_ln = wkn_ln - bind_ln;
1663 
1664         ret = SMB_STRDUP(wkn_dn_exp[0]);
1665         if (!ret) {
1666                 goto out;
1667         }
1668 
1669         for (i=1; i < new_ln; i++) {
1670                 char *s = NULL;
1671                 
1672                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1673                         SAFE_FREE(ret);
1674                         goto out;
1675                 }
1676 
1677                 SAFE_FREE(ret);
1678                 ret = SMB_STRDUP(s);
1679                 free(s);
1680                 if (!ret) {
1681                         goto out;
1682                 }
1683         }
1684 
1685  out:
1686         SAFE_FREE(base);
1687         ads_msgfree(ads, res);
1688         TALLOC_FREE(wkn_dn);
1689         if (wkn_dn_exp) {
1690                 ldap_value_free(wkn_dn_exp);
1691         }
1692         if (bind_dn_exp) {
1693                 ldap_value_free(bind_dn_exp);
1694         }
1695 
1696         return ret;
1697 }
1698 
1699 /**
1700  * Adds (appends) an item to an attribute array, rather then
1701  * replacing the whole list
1702  * @param ctx An initialized TALLOC_CTX
1703  * @param mods An initialized ADS_MODLIST
1704  * @param name name of the ldap attribute to append to
1705  * @param vals an array of values to add
1706  * @return status of addition
1707  **/
1708 
1709 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
     /* [<][>][^][v][top][bottom][index][help] */
1710                                 const char *name, const char **vals)
1711 {
1712         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1713                                (const void *) vals);
1714 }
1715 
1716 /**
1717  * Determines the an account's current KVNO via an LDAP lookup
1718  * @param ads An initialized ADS_STRUCT
1719  * @param account_name the NT samaccountname.
1720  * @return the kvno for the account, or -1 in case of a failure.
1721  **/
1722 
1723 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
     /* [<][>][^][v][top][bottom][index][help] */
1724 {
1725         LDAPMessage *res = NULL;
1726         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1727         char *filter;
1728         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1729         char *dn_string = NULL;
1730         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1731 
1732         DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1733         if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1734                 return kvno;
1735         }
1736         ret = ads_search(ads, &res, filter, attrs);
1737         SAFE_FREE(filter);
1738         if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1739                 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1740                 ads_msgfree(ads, res);
1741                 return kvno;
1742         }
1743 
1744         dn_string = ads_get_dn(ads, talloc_tos(), res);
1745         if (!dn_string) {
1746                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1747                 ads_msgfree(ads, res);
1748                 return kvno;
1749         }
1750         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1751         TALLOC_FREE(dn_string);
1752 
1753         /* ---------------------------------------------------------
1754          * 0 is returned as a default KVNO from this point on...
1755          * This is done because Windows 2000 does not support key
1756          * version numbers.  Chances are that a failure in the next
1757          * step is simply due to Windows 2000 being used for a
1758          * domain controller. */
1759         kvno = 0;
1760 
1761         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1762                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1763                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1764                 ads_msgfree(ads, res);
1765                 return kvno;
1766         }
1767 
1768         /* Success */
1769         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1770         ads_msgfree(ads, res);
1771         return kvno;
1772 }
1773 
1774 /**
1775  * Determines the computer account's current KVNO via an LDAP lookup
1776  * @param ads An initialized ADS_STRUCT
1777  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1778  * @return the kvno for the computer account, or -1 in case of a failure.
1779  **/
1780 
1781 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
     /* [<][>][^][v][top][bottom][index][help] */
1782 {
1783         char *computer_account = NULL;
1784         uint32_t kvno = -1;
1785 
1786         if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1787                 return kvno;
1788         }
1789 
1790         kvno = ads_get_kvno(ads, computer_account);
1791         free(computer_account);
1792 
1793         return kvno;
1794 }
1795 
1796 /**
1797  * This clears out all registered spn's for a given hostname
1798  * @param ads An initilaized ADS_STRUCT
1799  * @param machine_name the NetBIOS name of the computer.
1800  * @return 0 upon success, non-zero otherwise.
1801  **/
1802 
1803 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
     /* [<][>][^][v][top][bottom][index][help] */
1804 {
1805         TALLOC_CTX *ctx;
1806         LDAPMessage *res = NULL;
1807         ADS_MODLIST mods;
1808         const char *servicePrincipalName[1] = {NULL};
1809         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1810         char *dn_string = NULL;
1811 
1812         ret = ads_find_machine_acct(ads, &res, machine_name);
1813         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1814                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1815                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1816                 ads_msgfree(ads, res);
1817                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1818         }
1819 
1820         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1821         ctx = talloc_init("ads_clear_service_principal_names");
1822         if (!ctx) {
1823                 ads_msgfree(ads, res);
1824                 return ADS_ERROR(LDAP_NO_MEMORY);
1825         }
1826 
1827         if (!(mods = ads_init_mods(ctx))) {
1828                 talloc_destroy(ctx);
1829                 ads_msgfree(ads, res);
1830                 return ADS_ERROR(LDAP_NO_MEMORY);
1831         }
1832         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1833         if (!ADS_ERR_OK(ret)) {
1834                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1835                 ads_msgfree(ads, res);
1836                 talloc_destroy(ctx);
1837                 return ret;
1838         }
1839         dn_string = ads_get_dn(ads, talloc_tos(), res);
1840         if (!dn_string) {
1841                 talloc_destroy(ctx);
1842                 ads_msgfree(ads, res);
1843                 return ADS_ERROR(LDAP_NO_MEMORY);
1844         }
1845         ret = ads_gen_mod(ads, dn_string, mods);
1846         TALLOC_FREE(dn_string);
1847         if (!ADS_ERR_OK(ret)) {
1848                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1849                         machine_name));
1850                 ads_msgfree(ads, res);
1851                 talloc_destroy(ctx);
1852                 return ret;
1853         }
1854 
1855         ads_msgfree(ads, res);
1856         talloc_destroy(ctx);
1857         return ret;
1858 }
1859 
1860 /**
1861  * This adds a service principal name to an existing computer account
1862  * (found by hostname) in AD.
1863  * @param ads An initialized ADS_STRUCT
1864  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1865  * @param my_fqdn The fully qualified DNS name of the machine
1866  * @param spn A string of the service principal to add, i.e. 'host'
1867  * @return 0 upon sucess, or non-zero if a failure occurs
1868  **/
1869 
1870 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
     /* [<][>][^][v][top][bottom][index][help] */
1871                                           const char *my_fqdn, const char *spn)
1872 {
1873         ADS_STATUS ret;
1874         TALLOC_CTX *ctx;
1875         LDAPMessage *res = NULL;
1876         char *psp1, *psp2;
1877         ADS_MODLIST mods;
1878         char *dn_string = NULL;
1879         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1880 
1881         ret = ads_find_machine_acct(ads, &res, machine_name);
1882         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1883                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1884                         machine_name));
1885                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1886                         spn, machine_name, ads->config.realm));
1887                 ads_msgfree(ads, res);
1888                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1889         }
1890 
1891         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1892         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1893                 ads_msgfree(ads, res);
1894                 return ADS_ERROR(LDAP_NO_MEMORY);
1895         }
1896 
1897         /* add short name spn */
1898         
1899         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1900                 talloc_destroy(ctx);
1901                 ads_msgfree(ads, res);
1902                 return ADS_ERROR(LDAP_NO_MEMORY);
1903         }
1904         strupper_m(psp1);
1905         strlower_m(&psp1[strlen(spn)]);
1906         servicePrincipalName[0] = psp1;
1907         
1908         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1909                 psp1, machine_name));
1910 
1911 
1912         /* add fully qualified spn */
1913         
1914         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1915                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1916                 goto out;
1917         }
1918         strupper_m(psp2);
1919         strlower_m(&psp2[strlen(spn)]);
1920         servicePrincipalName[1] = psp2;
1921 
1922         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1923                 psp2, machine_name));
1924 
1925         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1926                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1927                 goto out;
1928         }
1929         
1930         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1931         if (!ADS_ERR_OK(ret)) {
1932                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1933                 goto out;
1934         }
1935         
1936         if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1937                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1938                 goto out;
1939         }
1940         
1941         ret = ads_gen_mod(ads, dn_string, mods);
1942         if (!ADS_ERR_OK(ret)) {
1943                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1944                 goto out;
1945         }
1946 
1947  out:
1948         TALLOC_FREE( ctx );
1949         ads_msgfree(ads, res);
1950         return ret;
1951 }
1952 
1953 /**
1954  * adds a machine account to the ADS server
1955  * @param ads An intialized ADS_STRUCT
1956  * @param machine_name - the NetBIOS machine name of this account.
1957  * @param account_type A number indicating the type of account to create
1958  * @param org_unit The LDAP path in which to place this account
1959  * @return 0 upon success, or non-zero otherwise
1960 **/
1961 
1962 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
     /* [<][>][^][v][top][bottom][index][help] */
1963                                    const char *org_unit)
1964 {
1965         ADS_STATUS ret;
1966         char *samAccountName, *controlstr;
1967         TALLOC_CTX *ctx;
1968         ADS_MODLIST mods;
1969         char *machine_escaped = NULL;
1970         char *new_dn;
1971         const char *objectClass[] = {"top", "person", "organizationalPerson",
1972                                      "user", "computer", NULL};
1973         LDAPMessage *res = NULL;
1974         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1975                                 UF_DONT_EXPIRE_PASSWD |\
1976                                 UF_ACCOUNTDISABLE );
1977                               
1978         if (!(ctx = talloc_init("ads_add_machine_acct")))
1979                 return ADS_ERROR(LDAP_NO_MEMORY);
1980 
1981         ret = ADS_ERROR(LDAP_NO_MEMORY);
1982 
1983         machine_escaped = escape_rdn_val_string_alloc(machine_name);
1984         if (!machine_escaped) {
1985                 goto done;
1986         }
1987 
1988         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1989         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1990 
1991         if ( !new_dn || !samAccountName ) {
1992                 goto done;
1993         }
1994         
1995 #ifndef ENCTYPE_ARCFOUR_HMAC
1996         acct_control |= UF_USE_DES_KEY_ONLY;
1997 #endif
1998 
1999         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2000                 goto done;
2001         }
2002 
2003         if (!(mods = ads_init_mods(ctx))) {
2004                 goto done;
2005         }
2006         
2007         ads_mod_str(ctx, &mods, "cn", machine_name);
2008         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2009         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2010         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2011 
2012         ret = ads_gen_add(ads, new_dn, mods);
2013 
2014 done:
2015         SAFE_FREE(machine_escaped);
2016         ads_msgfree(ads, res);
2017         talloc_destroy(ctx);
2018         
2019         return ret;
2020 }
2021 
2022 /**
2023  * move a machine account to another OU on the ADS server
2024  * @param ads - An intialized ADS_STRUCT
2025  * @param machine_name - the NetBIOS machine name of this account.
2026  * @param org_unit - The LDAP path in which to place this account
2027  * @param moved - whether we moved the machine account (optional)
2028  * @return 0 upon success, or non-zero otherwise
2029 **/
2030 
2031 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
     /* [<][>][^][v][top][bottom][index][help] */
2032                                  const char *org_unit, bool *moved)
2033 {
2034         ADS_STATUS rc;
2035         int ldap_status;
2036         LDAPMessage *res = NULL;
2037         char *filter = NULL;
2038         char *computer_dn = NULL;
2039         char *parent_dn;
2040         char *computer_rdn = NULL;
2041         bool need_move = False;
2042 
2043         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2044                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2045                 goto done;
2046         }
2047 
2048         /* Find pre-existing machine */
2049         rc = ads_search(ads, &res, filter, NULL);
2050         if (!ADS_ERR_OK(rc)) {
2051                 goto done;
2052         }
2053 
2054         computer_dn = ads_get_dn(ads, talloc_tos(), res);
2055         if (!computer_dn) {
2056                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2057                 goto done;
2058         }
2059 
2060         parent_dn = ads_parent_dn(computer_dn);
2061         if (strequal(parent_dn, org_unit)) {
2062                 goto done;
2063         }
2064 
2065         need_move = True;
2066 
2067         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2068                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2069                 goto done;
2070         }
2071 
2072         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
2073                                     org_unit, 1, NULL, NULL);
2074         rc = ADS_ERROR(ldap_status);
2075 
2076 done:
2077         ads_msgfree(ads, res);
2078         SAFE_FREE(filter);
2079         TALLOC_FREE(computer_dn);
2080         SAFE_FREE(computer_rdn);
2081 
2082         if (!ADS_ERR_OK(rc)) {
2083                 need_move = False;
2084         }
2085 
2086         if (moved) {
2087                 *moved = need_move;
2088         }
2089 
2090         return rc;
2091 }
2092 
2093 /*
2094   dump a binary result from ldap
2095 */
2096 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
     /* [<][>][^][v][top][bottom][index][help] */
2097 {
2098         int i, j;
2099         for (i=0; values[i]; i++) {
2100                 printf("%s: ", field);
2101                 for (j=0; j<values[i]->bv_len; j++) {
2102                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
2103                 }
2104                 printf("\n");
2105         }
2106 }
2107 
2108 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
     /* [<][>][^][v][top][bottom][index][help] */
2109 {
2110         int i;
2111         for (i=0; values[i]; i++) {
2112 
2113                 UUID_FLAT guid;
2114                 struct GUID tmp;
2115 
2116                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2117                 smb_uuid_unpack(guid, &tmp);
2118                 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2119         }
2120 }
2121 
2122 /*
2123   dump a sid result from ldap
2124 */
2125 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
     /* [<][>][^][v][top][bottom][index][help] */
2126 {
2127         int i;
2128         for (i=0; values[i]; i++) {
2129                 DOM_SID sid;
2130                 fstring tmp;
2131                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2132                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2133         }
2134 }
2135 
2136 /*
2137   dump ntSecurityDescriptor
2138 */
2139 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
     /* [<][>][^][v][top][bottom][index][help] */
2140 {
2141         TALLOC_CTX *frame = talloc_stackframe();
2142         struct security_descriptor *psd;
2143         NTSTATUS status;
2144 
2145         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2146                                      values[0]->bv_len, &psd);
2147         if (!NT_STATUS_IS_OK(status)) {
2148                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2149                           nt_errstr(status)));
2150                 TALLOC_FREE(frame);
2151                 return;
2152         }
2153 
2154         if (psd) {
2155                 ads_disp_sd(ads, talloc_tos(), psd);
2156         }
2157 
2158         TALLOC_FREE(frame);
2159 }
2160 
2161 /*
2162   dump a string result from ldap
2163 */
2164 static void dump_string(const char *field, char **values)
     /* [<][>][^][v][top][bottom][index][help] */
2165 {
2166         int i;
2167         for (i=0; values[i]; i++) {
2168                 printf("%s: %s\n", field, values[i]);
2169         }
2170 }
2171 
2172 /*
2173   dump a field from LDAP on stdout
2174   used for debugging
2175 */
2176 
2177 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
     /* [<][>][^][v][top][bottom][index][help] */
2178 {
2179         const struct {
2180                 const char *name;
2181                 bool string;
2182                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2183         } handlers[] = {
2184                 {"objectGUID", False, dump_guid},
2185                 {"netbootGUID", False, dump_guid},
2186                 {"nTSecurityDescriptor", False, dump_sd},
2187                 {"dnsRecord", False, dump_binary},
2188                 {"objectSid", False, dump_sid},
2189                 {"tokenGroups", False, dump_sid},
2190                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2191                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2192                 {"mS-DS-CreatorSID", False, dump_sid},
2193                 {"msExchMailboxGuid", False, dump_guid},
2194                 {NULL, True, NULL}
2195         };
2196         int i;
2197 
2198         if (!field) { /* must be end of an entry */
2199                 printf("\n");
2200                 return False;
2201         }
2202 
2203         for (i=0; handlers[i].name; i++) {
2204                 if (StrCaseCmp(handlers[i].name, field) == 0) {
2205                         if (!values) /* first time, indicate string or not */
2206                                 return handlers[i].string;
2207                         handlers[i].handler(ads, field, (struct berval **) values);
2208                         break;
2209                 }
2210         }
2211         if (!handlers[i].name) {
2212                 if (!values) /* first time, indicate string conversion */
2213                         return True;
2214                 dump_string(field, (char **)values);
2215         }
2216         return False;
2217 }
2218 
2219 /**
2220  * Dump a result from LDAP on stdout
2221  *  used for debugging
2222  * @param ads connection to ads server
2223  * @param res Results to dump
2224  **/
2225 
2226  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
     /* [<][>][^][v][top][bottom][index][help] */
2227 {
2228         ads_process_results(ads, res, ads_dump_field, NULL);
2229 }
2230 
2231 /**
2232  * Walk through results, calling a function for each entry found.
2233  *  The function receives a field name, a berval * array of values,
2234  *  and a data area passed through from the start.  The function is
2235  *  called once with null for field and values at the end of each
2236  *  entry.
2237  * @param ads connection to ads server
2238  * @param res Results to process
2239  * @param fn Function for processing each result
2240  * @param data_area user-defined area to pass to function
2241  **/
2242  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
     /* [<][>][^][v][top][bottom][index][help] */
2243                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2244                           void *data_area)
2245 {
2246         LDAPMessage *msg;
2247         TALLOC_CTX *ctx;
2248         size_t converted_size;
2249 
2250         if (!(ctx = talloc_init("ads_process_results")))
2251                 return;
2252 
2253         for (msg = ads_first_entry(ads, res); msg; 
2254              msg = ads_next_entry(ads, msg)) {
2255                 char *utf8_field;
2256                 BerElement *b;
2257         
2258                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2259                                                      (LDAPMessage *)msg,&b); 
2260                      utf8_field;
2261                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2262                                                     (LDAPMessage *)msg,b)) {
2263                         struct berval **ber_vals;
2264                         char **str_vals, **utf8_vals;
2265                         char *field;
2266                         bool string; 
2267 
2268                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2269                                               &converted_size))
2270                         {
2271                                 DEBUG(0,("ads_process_results: "
2272                                          "pull_utf8_talloc failed: %s",
2273                                          strerror(errno)));
2274                         }
2275 
2276                         string = fn(ads, field, NULL, data_area);
2277 
2278                         if (string) {
2279                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2280                                                  (LDAPMessage *)msg, field);
2281                                 str_vals = ads_pull_strvals(ctx, 
2282                                                   (const char **) utf8_vals);
2283                                 fn(ads, field, (void **) str_vals, data_area);
2284                                 ldap_value_free(utf8_vals);
2285                         } else {
2286                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2287                                                  (LDAPMessage *)msg, field);
2288                                 fn(ads, field, (void **) ber_vals, data_area);
2289 
2290                                 ldap_value_free_len(ber_vals);
2291                         }
2292                         ldap_memfree(utf8_field);
2293                 }
2294                 ber_free(b, 0);
2295                 talloc_free_children(ctx);
2296                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2297 
2298         }
2299         talloc_destroy(ctx);
2300 }
2301 
2302 /**
2303  * count how many replies are in a LDAPMessage
2304  * @param ads connection to ads server
2305  * @param res Results to count
2306  * @return number of replies
2307  **/
2308 int ads_count_replies(ADS_STRUCT *ads, void *res)
     /* [<][>][^][v][top][bottom][index][help] */
2309 {
2310         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2311 }
2312 
2313 /**
2314  * pull the first entry from a ADS result
2315  * @param ads connection to ads server
2316  * @param res Results of search
2317  * @return first entry from result
2318  **/
2319  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
     /* [<][>][^][v][top][bottom][index][help] */
2320 {
2321         return ldap_first_entry(ads->ldap.ld, res);
2322 }
2323 
2324 /**
2325  * pull the next entry from a ADS result
2326  * @param ads connection to ads server
2327  * @param res Results of search
2328  * @return next entry from result
2329  **/
2330  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
     /* [<][>][^][v][top][bottom][index][help] */
2331 {
2332         return ldap_next_entry(ads->ldap.ld, res);
2333 }
2334 
2335 /**
2336  * pull the first message from a ADS result
2337  * @param ads connection to ads server
2338  * @param res Results of search
2339  * @return first message from result
2340  **/
2341  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
     /* [<][>][^][v][top][bottom][index][help] */
2342 {
2343         return ldap_first_message(ads->ldap.ld, res);
2344 }
2345 
2346 /**
2347  * pull the next message from a ADS result
2348  * @param ads connection to ads server
2349  * @param res Results of search
2350  * @return next message from result
2351  **/
2352  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
     /* [<][>][^][v][top][bottom][index][help] */
2353 {
2354         return ldap_next_message(ads->ldap.ld, res);
2355 }
2356 
2357 /**
2358  * pull a single string from a ADS result
2359  * @param ads connection to ads server
2360  * @param mem_ctx TALLOC_CTX to use for allocating result string
2361  * @param msg Results of search
2362  * @param field Attribute to retrieve
2363  * @return Result string in talloc context
2364  **/
2365  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
     /* [<][>][^][v][top][bottom][index][help] */
2366                        const char *field)
2367 {
2368         char **values;
2369         char *ret = NULL;
2370         char *ux_string;
2371         size_t converted_size;
2372 
2373         values = ldap_get_values(ads->ldap.ld, msg, field);
2374         if (!values)
2375                 return NULL;
2376         
2377         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2378                                           &converted_size))
2379         {
2380                 ret = ux_string;
2381         }
2382         ldap_value_free(values);
2383         return ret;
2384 }
2385 
2386 /**
2387  * pull an array of strings from a ADS result
2388  * @param ads connection to ads server
2389  * @param mem_ctx TALLOC_CTX to use for allocating result string
2390  * @param msg Results of search
2391  * @param field Attribute to retrieve
2392  * @return Result strings in talloc context
2393  **/
2394  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
2395                          LDAPMessage *msg, const char *field,
2396                          size_t *num_values)
2397 {
2398         char **values;
2399         char **ret = NULL;
2400         int i;
2401         size_t converted_size;
2402 
2403         values = ldap_get_values(ads->ldap.ld, msg, field);
2404         if (!values)
2405                 return NULL;
2406 
2407         *num_values = ldap_count_values(values);
2408 
2409         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2410         if (!ret) {
2411                 ldap_value_free(values);
2412                 return NULL;
2413         }
2414 
2415         for (i=0;i<*num_values;i++) {
2416                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2417                                       &converted_size))
2418                 {
2419                         ldap_value_free(values);
2420                         return NULL;
2421                 }
2422         }
2423         ret[i] = NULL;
2424 
2425         ldap_value_free(values);
2426         return ret;
2427 }
2428 
2429 /**
2430  * pull an array of strings from a ADS result 
2431  *  (handle large multivalue attributes with range retrieval)
2432  * @param ads connection to ads server
2433  * @param mem_ctx TALLOC_CTX to use for allocating result string
2434  * @param msg Results of search
2435  * @param field Attribute to retrieve
2436  * @param current_strings strings returned by a previous call to this function
2437  * @param next_attribute The next query should ask for this attribute
2438  * @param num_values How many values did we get this time?
2439  * @param more_values Are there more values to get?
2440  * @return Result strings in talloc context
2441  **/
2442  char **ads_pull_strings_range(ADS_STRUCT *ads, 
     /* [<][>][^][v][top][bottom][index][help] */
2443                                TALLOC_CTX *mem_ctx,
2444                                LDAPMessage *msg, const char *field,
2445                                char **current_strings,
2446                                const char **next_attribute,
2447                                size_t *num_strings,
2448                                bool *more_strings)
2449 {
2450         char *attr;
2451         char *expected_range_attrib, *range_attr;
2452         BerElement *ptr = NULL;
2453         char **strings;
2454         char **new_strings;
2455         size_t num_new_strings;
2456         unsigned long int range_start;
2457         unsigned long int range_end;
2458         
2459         /* we might have been given the whole lot anyway */
2460         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2461                 *more_strings = False;
2462                 return strings;
2463         }
2464 
2465         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2466 
2467         /* look for Range result */
2468         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2469              attr; 
2470              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2471                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2472                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2473                         range_attr = attr;
2474                         break;
2475                 }
2476                 ldap_memfree(attr);
2477         }
2478         if (!attr) {
2479                 ber_free(ptr, 0);
2480                 /* nothing here - this field is just empty */
2481                 *more_strings = False;
2482                 return NULL;
2483         }
2484         
2485         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2486                    &range_start, &range_end) == 2) {
2487                 *more_strings = True;
2488         } else {
2489                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2490                            &range_start) == 1) {
2491                         *more_strings = False;
2492                 } else {
2493                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2494                                   range_attr));
2495                         ldap_memfree(range_attr);
2496                         *more_strings = False;
2497                         return NULL;
2498                 }
2499         }
2500 
2501         if ((*num_strings) != range_start) {
2502                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2503                           " - aborting range retreival\n",
2504                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2505                 ldap_memfree(range_attr);
2506                 *more_strings = False;
2507                 return NULL;
2508         }
2509 
2510         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2511         
2512         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2513                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2514                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2515                           range_attr, (unsigned long int)range_end - range_start + 1, 
2516                           (unsigned long int)num_new_strings));
2517                 ldap_memfree(range_attr);
2518                 *more_strings = False;
2519                 return NULL;
2520         }
2521 
2522         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2523                                  *num_strings + num_new_strings);
2524         
2525         if (strings == NULL) {
2526                 ldap_memfree(range_attr);
2527                 *more_strings = False;
2528                 return NULL;
2529         }
2530         
2531         if (new_strings && num_new_strings) {
2532                 memcpy(&strings[*num_strings], new_strings,
2533                        sizeof(*new_strings) * num_new_strings);
2534         }
2535 
2536         (*num_strings) += num_new_strings;
2537 
2538         if (*more_strings) {
2539                 *next_attribute = talloc_asprintf(mem_ctx,
2540                                                   "%s;range=%d-*", 
2541                                                   field,
2542                                                   (int)*num_strings);
2543                 
2544                 if (!*next_attribute) {
2545                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2546                         ldap_memfree(range_attr);
2547                         *more_strings = False;
2548                         return NULL;
2549                 }
2550         }
2551 
2552         ldap_memfree(range_attr);
2553 
2554         return strings;
2555 }
2556 
2557 /**
2558  * pull a single uint32 from a ADS result
2559  * @param ads connection to ads server
2560  * @param msg Results of search
2561  * @param field Attribute to retrieve
2562  * @param v Pointer to int to store result
2563  * @return boolean inidicating success
2564 */
2565  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
     /* [<][>][^][v][top][bottom][index][help] */
2566                       uint32 *v)
2567 {
2568         char **values;
2569 
2570         values = ldap_get_values(ads->ldap.ld, msg, field);
2571         if (!values)
2572                 return False;
2573         if (!values[0]) {
2574                 ldap_value_free(values);
2575                 return False;
2576         }
2577 
2578         *v = atoi(values[0]);
2579         ldap_value_free(values);
2580         return True;
2581 }
2582 
2583 /**
2584  * pull a single objectGUID from an ADS result
2585  * @param ads connection to ADS server
2586  * @param msg results of search
2587  * @param guid 37-byte area to receive text guid
2588  * @return boolean indicating success
2589  **/
2590  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
     /* [<][>][^][v][top][bottom][index][help] */
2591 {
2592         char **values;
2593         UUID_FLAT flat_guid;
2594 
2595         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2596         if (!values)
2597                 return False;
2598         
2599         if (values[0]) {
2600                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2601                 smb_uuid_unpack(flat_guid, guid);
2602                 ldap_value_free(values);
2603                 return True;
2604         }
2605         ldap_value_free(values);
2606         return False;
2607 
2608 }
2609 
2610 
2611 /**
2612  * pull a single DOM_SID from a ADS result
2613  * @param ads connection to ads server
2614  * @param msg Results of search
2615  * @param field Attribute to retrieve
2616  * @param sid Pointer to sid to store result
2617  * @return boolean inidicating success
2618 */
2619  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
     /* [<][>][^][v][top][bottom][index][help] */
2620                    DOM_SID *sid)
2621 {
2622         struct berval **values;
2623         bool ret = False;
2624 
2625         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2626 
2627         if (!values)
2628                 return False;
2629 
2630         if (values[0])
2631                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2632         
2633         ldap_value_free_len(values);
2634         return ret;
2635 }
2636 
2637 /**
2638  * pull an array of DOM_SIDs from a ADS result
2639  * @param ads connection to ads server
2640  * @param mem_ctx TALLOC_CTX for allocating sid array
2641  * @param msg Results of search
2642  * @param field Attribute to retrieve
2643  * @param sids pointer to sid array to allocate
2644  * @return the count of SIDs pulled
2645  **/
2646  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
2647                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2648 {
2649         struct berval **values;
2650         bool ret;
2651         int count, i;
2652 
2653         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2654 
2655         if (!values)
2656                 return 0;
2657 
2658         for (i=0; values[i]; i++)
2659                 /* nop */ ;
2660 
2661         if (i) {
2662                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2663                 if (!(*sids)) {
2664                         ldap_value_free_len(values);
2665                         return 0;
2666                 }
2667         } else {
2668                 (*sids) = NULL;
2669         }
2670 
2671         count = 0;
2672         for (i=0; values[i]; i++) {
2673                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2674                 if (ret) {
2675                         DEBUG(10, ("pulling SID: %s\n",
2676                                    sid_string_dbg(&(*sids)[count])));
2677                         count++;
2678                 }
2679         }
2680         
2681         ldap_value_free_len(values);
2682         return count;
2683 }
2684 
2685 /**
2686  * pull a SEC_DESC from a ADS result
2687  * @param ads connection to ads server
2688  * @param mem_ctx TALLOC_CTX for allocating sid array
2689  * @param msg Results of search
2690  * @param field Attribute to retrieve
2691  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2692  * @return boolean inidicating success
2693 */
2694  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
2695                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2696 {
2697         struct berval **values;
2698         bool ret = true;
2699 
2700         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2701 
2702         if (!values) return false;
2703 
2704         if (values[0]) {
2705                 NTSTATUS status;
2706                 status = unmarshall_sec_desc(mem_ctx,
2707                                              (uint8 *)values[0]->bv_val,
2708                                              values[0]->bv_len, sd);
2709                 if (!NT_STATUS_IS_OK(status)) {
2710                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2711                                   nt_errstr(status)));
2712                         ret = false;
2713                 }
2714         }
2715         
2716         ldap_value_free_len(values);
2717         return ret;
2718 }
2719 
2720 /* 
2721  * in order to support usernames longer than 21 characters we need to 
2722  * use both the sAMAccountName and the userPrincipalName attributes 
2723  * It seems that not all users have the userPrincipalName attribute set
2724  *
2725  * @param ads connection to ads server
2726  * @param mem_ctx TALLOC_CTX for allocating sid array
2727  * @param msg Results of search
2728  * @return the username
2729  */
2730  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
2731                          LDAPMessage *msg)
2732 {
2733 #if 0   /* JERRY */
2734         char *ret, *p;
2735 
2736         /* lookup_name() only works on the sAMAccountName to 
2737            returning the username portion of userPrincipalName
2738            breaks winbindd_getpwnam() */
2739 
2740         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2741         if (ret && (p = strchr_m(ret, '@'))) {
2742                 *p = 0;
2743                 return ret;
2744         }
2745 #endif
2746         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2747 }
2748 
2749 
2750 /**
2751  * find the update serial number - this is the core of the ldap cache
2752  * @param ads connection to ads server
2753  * @param ads connection to ADS server
2754  * @param usn Pointer to retrieved update serial number
2755  * @return status of search
2756  **/
2757 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
     /* [<][>][^][v][top][bottom][index][help] */
2758 {
2759         const char *attrs[] = {"highestCommittedUSN", NULL};
2760         ADS_STATUS status;
2761         LDAPMessage *res;
2762 
2763         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2764         if (!ADS_ERR_OK(status)) 
2765                 return status;
2766 
2767         if (ads_count_replies(ads, res) != 1) {
2768                 ads_msgfree(ads, res);
2769                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2770         }
2771 
2772         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2773                 ads_msgfree(ads, res);
2774                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2775         }
2776 
2777         ads_msgfree(ads, res);
2778         return ADS_SUCCESS;
2779 }
2780 
2781 /* parse a ADS timestring - typical string is
2782    '20020917091222.0Z0' which means 09:12.22 17th September
2783    2002, timezone 0 */
2784 static time_t ads_parse_time(const char *str)
     /* [<][>][^][v][top][bottom][index][help] */
2785 {
2786         struct tm tm;
2787 
2788         ZERO_STRUCT(tm);
2789 
2790         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2791                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2792                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2793                 return 0;
2794         }
2795         tm.tm_year -= 1900;
2796         tm.tm_mon -= 1;
2797 
2798         return timegm(&tm);
2799 }
2800 
2801 /********************************************************************
2802 ********************************************************************/
2803 
2804 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
     /* [<][>][^][v][top][bottom][index][help] */
2805 {
2806         const char *attrs[] = {"currentTime", NULL};
2807         ADS_STATUS status;
2808         LDAPMessage *res;
2809         char *timestr;
2810         TALLOC_CTX *ctx;
2811         ADS_STRUCT *ads_s = ads;
2812 
2813         if (!(ctx = talloc_init("ads_current_time"))) {
2814                 return ADS_ERROR(LDAP_NO_MEMORY);
2815         }
2816 
2817         /* establish a new ldap tcp session if necessary */
2818 
2819         if ( !ads->ldap.ld ) {
2820                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2821                         ads->server.ldap_server )) == NULL )
2822                 {
2823                         goto done;
2824                 }
2825                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2826                 status = ads_connect( ads_s );
2827                 if ( !ADS_ERR_OK(status))
2828                         goto done;
2829         }
2830 
2831         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2832         if (!ADS_ERR_OK(status)) {
2833                 goto done;
2834         }
2835 
2836         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2837         if (!timestr) {
2838                 ads_msgfree(ads_s, res);
2839                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2840                 goto done;
2841         }
2842 
2843         /* but save the time and offset in the original ADS_STRUCT */   
2844         
2845         ads->config.current_time = ads_parse_time(timestr);
2846 
2847         if (ads->config.current_time != 0) {
2848                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2849                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2850         }
2851 
2852         ads_msgfree(ads, res);
2853 
2854         status = ADS_SUCCESS;
2855 
2856 done:
2857         /* free any temporary ads connections */
2858         if ( ads_s != ads ) {
2859                 ads_destroy( &ads_s );
2860         }
2861         talloc_destroy(ctx);
2862 
2863         return status;
2864 }
2865 
2866 /********************************************************************
2867 ********************************************************************/
2868 
2869 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
     /* [<][>][^][v][top][bottom][index][help] */
2870 {
2871         const char *attrs[] = {"domainFunctionality", NULL};
2872         ADS_STATUS status;
2873         LDAPMessage *res;
2874         ADS_STRUCT *ads_s = ads;
2875         
2876         *val = DS_DOMAIN_FUNCTION_2000;
2877 
2878         /* establish a new ldap tcp session if necessary */
2879 
2880         if ( !ads->ldap.ld ) {
2881                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2882                         ads->server.ldap_server )) == NULL )
2883                 {
2884                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2885                         goto done;
2886                 }
2887                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2888                 status = ads_connect( ads_s );
2889                 if ( !ADS_ERR_OK(status))
2890                         goto done;
2891         }
2892 
2893         /* If the attribute does not exist assume it is a Windows 2000 
2894            functional domain */
2895            
2896         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2897         if (!ADS_ERR_OK(status)) {
2898                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2899                         status = ADS_SUCCESS;
2900                 }
2901                 goto done;
2902         }
2903 
2904         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2905                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2906         }
2907         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2908 
2909         
2910         ads_msgfree(ads, res);
2911 
2912 done:
2913         /* free any temporary ads connections */
2914         if ( ads_s != ads ) {
2915                 ads_destroy( &ads_s );
2916         }
2917 
2918         return status;
2919 }
2920 
2921 /**
2922  * find the domain sid for our domain
2923  * @param ads connection to ads server
2924  * @param sid Pointer to domain sid
2925  * @return status of search
2926  **/
2927 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
     /* [<][>][^][v][top][bottom][index][help] */
2928 {
2929         const char *attrs[] = {"objectSid", NULL};
2930         LDAPMessage *res;
2931         ADS_STATUS rc;
2932 
2933         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2934                            attrs, &res);
2935         if (!ADS_ERR_OK(rc)) return rc;
2936         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2937                 ads_msgfree(ads, res);
2938                 return ADS_ERROR_SYSTEM(ENOENT);
2939         }
2940         ads_msgfree(ads, res);
2941         
2942         return ADS_SUCCESS;
2943 }
2944 
2945 /**
2946  * find our site name 
2947  * @param ads connection to ads server
2948  * @param mem_ctx Pointer to talloc context
2949  * @param site_name Pointer to the sitename
2950  * @return status of search
2951  **/
2952 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
     /* [<][>][^][v][top][bottom][index][help] */
2953 {
2954         ADS_STATUS status;
2955         LDAPMessage *res;
2956         const char *dn, *service_name;
2957         const char *attrs[] = { "dsServiceName", NULL };
2958 
2959         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2960         if (!ADS_ERR_OK(status)) {
2961                 return status;
2962         }
2963 
2964         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2965         if (service_name == NULL) {
2966                 ads_msgfree(ads, res);
2967                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2968         }
2969 
2970         ads_msgfree(ads, res);
2971 
2972         /* go up three levels */
2973         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2974         if (dn == NULL) {
2975                 return ADS_ERROR(LDAP_NO_MEMORY);
2976         }
2977 
2978         *site_name = talloc_strdup(mem_ctx, dn);
2979         if (*site_name == NULL) {
2980                 return ADS_ERROR(LDAP_NO_MEMORY);
2981         }
2982 
2983         return status;
2984         /*
2985         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2986         */                                               
2987 }
2988 
2989 /**
2990  * find the site dn where a machine resides
2991  * @param ads connection to ads server
2992  * @param mem_ctx Pointer to talloc context
2993  * @param computer_name name of the machine
2994  * @param site_name Pointer to the sitename
2995  * @return status of search
2996  **/
2997 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
     /* [<][>][^][v][top][bottom][index][help] */
2998 {
2999         ADS_STATUS status;
3000         LDAPMessage *res;
3001         const char *parent, *filter;
3002         char *config_context = NULL;
3003         char *dn;
3004 
3005         /* shortcut a query */
3006         if (strequal(computer_name, ads->config.ldap_server_name)) {
3007                 return ads_site_dn(ads, mem_ctx, site_dn);
3008         }
3009 
3010         status = ads_config_path(ads, mem_ctx, &config_context);
3011         if (!ADS_ERR_OK(status)) {
3012                 return status;
3013         }
3014 
3015         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3016         if (filter == NULL) {
3017                 return ADS_ERROR(LDAP_NO_MEMORY);
3018         }
3019 
3020         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3021                                filter, NULL, &res);
3022         if (!ADS_ERR_OK(status)) {
3023                 return status;
3024         }
3025 
3026         if (ads_count_replies(ads, res) != 1) {
3027                 ads_msgfree(ads, res);
3028                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3029         }
3030 
3031         dn = ads_get_dn(ads, mem_ctx, res);
3032         if (dn == NULL) {
3033                 ads_msgfree(ads, res);
3034                 return ADS_ERROR(LDAP_NO_MEMORY);
3035         }
3036 
3037         /* go up three levels */
3038         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3039         if (parent == NULL) {
3040                 ads_msgfree(ads, res);
3041                 TALLOC_FREE(dn);
3042                 return ADS_ERROR(LDAP_NO_MEMORY);
3043         }
3044 
3045         *site_dn = talloc_strdup(mem_ctx, parent);
3046         if (*site_dn == NULL) {
3047                 ads_msgfree(ads, res);
3048                 TALLOC_FREE(dn);
3049                 return ADS_ERROR(LDAP_NO_MEMORY);
3050         }
3051 
3052         TALLOC_FREE(dn);
3053         ads_msgfree(ads, res);
3054 
3055         return status;
3056 }
3057 
3058 /**
3059  * get the upn suffixes for a domain
3060  * @param ads connection to ads server
3061  * @param mem_ctx Pointer to talloc context
3062  * @param suffixes Pointer to an array of suffixes
3063  * @param num_suffixes Pointer to the number of suffixes
3064  * @return status of search
3065  **/
3066 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
     /* [<][>][^][v][top][bottom][index][help] */
3067 {
3068         ADS_STATUS status;
3069         LDAPMessage *res;
3070         const char *base;
3071         char *config_context = NULL;
3072         const char *attrs[] = { "uPNSuffixes", NULL };
3073 
3074         status = ads_config_path(ads, mem_ctx, &config_context);
3075         if (!ADS_ERR_OK(status)) {
3076                 return status;
3077         }
3078 
3079         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3080         if (base == NULL) {
3081                 return ADS_ERROR(LDAP_NO_MEMORY);
3082         }
3083 
3084         status = ads_search_dn(ads, &res, base, attrs);
3085         if (!ADS_ERR_OK(status)) {
3086                 return status;
3087         }
3088 
3089         if (ads_count_replies(ads, res) != 1) {
3090                 ads_msgfree(ads, res);
3091                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3092         }
3093 
3094         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3095         if ((*suffixes) == NULL) {
3096                 ads_msgfree(ads, res);
3097                 return ADS_ERROR(LDAP_NO_MEMORY);
3098         }
3099 
3100         ads_msgfree(ads, res);
3101 
3102         return status;
3103 }
3104 
3105 /**
3106  * get the joinable ous for a domain
3107  * @param ads connection to ads server
3108  * @param mem_ctx Pointer to talloc context
3109  * @param ous Pointer to an array of ous
3110  * @param num_ous Pointer to the number of ous
3111  * @return status of search
3112  **/
3113 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
     /* [<][>][^][v][top][bottom][index][help] */
3114                                 TALLOC_CTX *mem_ctx,
3115                                 char ***ous,
3116                                 size_t *num_ous)
3117 {
3118         ADS_STATUS status;
3119         LDAPMessage *res = NULL;
3120         LDAPMessage *msg = NULL;
3121         const char *attrs[] = { "dn", NULL };
3122         int count = 0;
3123 
3124         status = ads_search(ads, &res,
3125                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3126                             attrs);
3127         if (!ADS_ERR_OK(status)) {
3128                 return status;
3129         }
3130 
3131         count = ads_count_replies(ads, res);
3132         if (count < 1) {
3133                 ads_msgfree(ads, res);
3134                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3135         }
3136 
3137         for (msg = ads_first_entry(ads, res); msg;
3138              msg = ads_next_entry(ads, msg)) {
3139 
3140                 char *dn = NULL;
3141 
3142                 dn = ads_get_dn(ads, talloc_tos(), msg);
3143                 if (!dn) {
3144                         ads_msgfree(ads, res);
3145                         return ADS_ERROR(LDAP_NO_MEMORY);
3146                 }
3147 
3148                 if (!add_string_to_array(mem_ctx, dn,
3149                                          (const char ***)ous,
3150                                          (int *)num_ous)) {
3151                         TALLOC_FREE(dn);
3152                         ads_msgfree(ads, res);
3153                         return ADS_ERROR(LDAP_NO_MEMORY);
3154                 }
3155 
3156                 TALLOC_FREE(dn);
3157         }
3158 
3159         ads_msgfree(ads, res);
3160 
3161         return status;
3162 }
3163 
3164 
3165 /**
3166  * pull a DOM_SID from an extended dn string
3167  * @param mem_ctx TALLOC_CTX
3168  * @param extended_dn string
3169  * @param flags string type of extended_dn
3170  * @param sid pointer to a DOM_SID
3171  * @return NT_STATUS_OK on success,
3172  *         NT_INVALID_PARAMETER on error,
3173  *         NT_STATUS_NOT_FOUND if no SID present
3174  **/
3175 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
3176                                         const char *extended_dn,
3177                                         enum ads_extended_dn_flags flags,
3178                                         DOM_SID *sid)
3179 {
3180         char *p, *q, *dn;
3181 
3182         if (!extended_dn) {
3183                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3184         }
3185 
3186         /* otherwise extended_dn gets stripped off */
3187         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3188                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3189         }
3190         /*
3191          * ADS_EXTENDED_DN_HEX_STRING:
3192          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3193          *
3194          * ADS_EXTENDED_DN_STRING (only with w2k3):
3195          * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3196          *
3197          * Object with no SID, such as an Exchange Public Folder
3198          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3199          */
3200 
3201         p = strchr(dn, ';');
3202         if (!p) {
3203                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3204         }
3205 
3206         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3207                 DEBUG(5,("No SID present in extended dn\n"));
3208                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3209         }
3210 
3211         p += strlen(";<SID=");
3212 
3213         q = strchr(p, '>');
3214         if (!q) {
3215                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3216         }
3217 
3218         *q = '\0';
3219 
3220         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3221 
3222         switch (flags) {
3223 
3224         case ADS_EXTENDED_DN_STRING:
3225                 if (!string_to_sid(sid, p)) {
3226                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3227                 }
3228                 break;
3229         case ADS_EXTENDED_DN_HEX_STRING: {
3230                 fstring buf;
3231                 size_t buf_len;
3232 
3233                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3234                 if (buf_len == 0) {
3235                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3236                 }
3237 
3238                 if (!sid_parse(buf, buf_len, sid)) {
3239                         DEBUG(10,("failed to parse sid\n"));
3240                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3241                 }
3242                 break;
3243                 }
3244         default:
3245                 DEBUG(10,("unknown extended dn format\n"));
3246                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3247         }
3248 
3249         return ADS_ERROR_NT(NT_STATUS_OK);
3250 }
3251 
3252 /**
3253  * pull an array of DOM_SIDs from a ADS result
3254  * @param ads connection to ads server
3255  * @param mem_ctx TALLOC_CTX for allocating sid array
3256  * @param msg Results of search
3257  * @param field Attribute to retrieve
3258  * @param flags string type of extended_dn
3259  * @param sids pointer to sid array to allocate
3260  * @return the count of SIDs pulled
3261  **/
3262  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
     /* [<][>][^][v][top][bottom][index][help] */
3263                                    TALLOC_CTX *mem_ctx,
3264                                    LDAPMessage *msg,
3265                                    const char *field,
3266                                    enum ads_extended_dn_flags flags,
3267                                    DOM_SID **sids)
3268 {
3269         int i;
3270         ADS_STATUS rc;
3271         size_t dn_count, ret_count = 0;
3272         char **dn_strings;
3273 
3274         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3275                                            &dn_count)) == NULL) {
3276                 return 0;
3277         }
3278 
3279         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3280         if (!(*sids)) {
3281                 TALLOC_FREE(dn_strings);
3282                 return 0;
3283         }
3284 
3285         for (i=0; i<dn_count; i++) {
3286                 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3287                                                   flags, &(*sids)[i]);
3288                 if (!ADS_ERR_OK(rc)) {
3289                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3290                             NT_STATUS_NOT_FOUND)) {
3291                                 continue;
3292                         }
3293                         else {
3294                                 TALLOC_FREE(*sids);
3295                                 TALLOC_FREE(dn_strings);
3296                                 return 0;
3297                         }
3298                 }
3299                 ret_count++;
3300         }
3301 
3302         TALLOC_FREE(dn_strings);
3303 
3304         return ret_count;
3305 }
3306 
3307 /********************************************************************
3308 ********************************************************************/
3309 
3310 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
     /* [<][>][^][v][top][bottom][index][help] */
3311 {
3312         LDAPMessage *res = NULL;
3313         ADS_STATUS status;
3314         int count = 0;
3315         char *name = NULL;
3316         
3317         status = ads_find_machine_acct(ads, &res, global_myname());
3318         if (!ADS_ERR_OK(status)) {
3319                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3320                         global_myname()));
3321                 goto out;
3322         }
3323                 
3324         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3325                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3326                 goto out;
3327         }
3328                 
3329         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3330                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3331         }
3332 
3333 out:
3334         ads_msgfree(ads, res);
3335         
3336         return name;
3337 }
3338 
3339 /********************************************************************
3340 ********************************************************************/
3341 
3342 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
     /* [<][>][^][v][top][bottom][index][help] */
3343 {
3344         LDAPMessage *res = NULL;
3345         ADS_STATUS status;
3346         int count = 0;
3347         char *name = NULL;
3348 
3349         status = ads_find_machine_acct(ads, &res, machine_name);
3350         if (!ADS_ERR_OK(status)) {
3351                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3352                         global_myname()));
3353                 goto out;
3354         }
3355 
3356         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3357                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3358                 goto out;
3359         }
3360 
3361         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3362                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3363         }
3364 
3365 out:
3366         ads_msgfree(ads, res);
3367 
3368         return name;
3369 }
3370 
3371 /********************************************************************
3372 ********************************************************************/
3373 
3374 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
     /* [<][>][^][v][top][bottom][index][help] */
3375 {
3376         LDAPMessage *res = NULL;
3377         ADS_STATUS status;
3378         int count = 0;
3379         char *name = NULL;
3380         
3381         status = ads_find_machine_acct(ads, &res, global_myname());
3382         if (!ADS_ERR_OK(status)) {
3383                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3384                         global_myname()));
3385                 goto out;
3386         }
3387                 
3388         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3389                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3390                 goto out;
3391         }
3392                 
3393         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3394                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3395         }
3396 
3397 out:
3398         ads_msgfree(ads, res);
3399         
3400         return name;
3401 }
3402 
3403 #if 0
3404 
3405    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3406 
3407 /**
3408  * Join a machine to a realm
3409  *  Creates the machine account and sets the machine password
3410  * @param ads connection to ads server
3411  * @param machine name of host to add
3412  * @param org_unit Organizational unit to place machine in
3413  * @return status of join
3414  **/
3415 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
     /* [<][>][^][v][top][bottom][index][help] */
3416                         uint32 account_type, const char *org_unit)
3417 {
3418         ADS_STATUS status;
3419         LDAPMessage *res = NULL;
3420         char *machine;
3421 
3422         /* machine name must be lowercase */
3423         machine = SMB_STRDUP(machine_name);
3424         strlower_m(machine);
3425 
3426         /*
3427         status = ads_find_machine_acct(ads, (void **)&res, machine);
3428         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3429                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3430                 status = ads_leave_realm(ads, machine);
3431                 if (!ADS_ERR_OK(status)) {
3432                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3433                                 machine, ads->config.realm));
3434                         return status;
3435                 }
3436         }
3437         */
3438         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3439         if (!ADS_ERR_OK(status)) {
3440                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3441                 SAFE_FREE(machine);
3442                 return status;
3443         }
3444 
3445         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3446         if (!ADS_ERR_OK(status)) {
3447                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3448                 SAFE_FREE(machine);
3449                 return status;
3450         }
3451 
3452         SAFE_FREE(machine);
3453         ads_msgfree(ads, res);
3454 
3455         return status;
3456 }
3457 #endif
3458 
3459 /**
3460  * Delete a machine from the realm
3461  * @param ads connection to ads server
3462  * @param hostname Machine to remove
3463  * @return status of delete
3464  **/
3465 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
     /* [<][>][^][v][top][bottom][index][help] */
3466 {
3467         ADS_STATUS status;
3468         void *msg;
3469         LDAPMessage *res;
3470         char *hostnameDN, *host;
3471         int rc;
3472         LDAPControl ldap_control;
3473         LDAPControl  * pldap_control[2] = {NULL, NULL};
3474 
3475         pldap_control[0] = &ldap_control;
3476         memset(&ldap_control, 0, sizeof(LDAPControl));
3477         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3478 
3479         /* hostname must be lowercase */
3480         host = SMB_STRDUP(hostname);
3481         strlower_m(host);
3482 
3483         status = ads_find_machine_acct(ads, &res, host);
3484         if (!ADS_ERR_OK(status)) {
3485                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3486                 SAFE_FREE(host);
3487                 return status;
3488         }
3489 
3490         msg = ads_first_entry(ads, res);
3491         if (!msg) {
3492                 SAFE_FREE(host);
3493                 return ADS_ERROR_SYSTEM(ENOENT);
3494         }
3495 
3496         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3497 
3498         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3499         if (rc) {
3500                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3501         }else {
3502                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3503         }
3504 
3505         if (rc != LDAP_SUCCESS) {
3506                 const char *attrs[] = { "cn", NULL };
3507                 LDAPMessage *msg_sub;
3508 
3509                 /* we only search with scope ONE, we do not expect any further
3510                  * objects to be created deeper */
3511 
3512                 status = ads_do_search_retry(ads, hostnameDN,
3513                                              LDAP_SCOPE_ONELEVEL,
3514                                              "(objectclass=*)", attrs, &res);
3515 
3516                 if (!ADS_ERR_OK(status)) {
3517                         SAFE_FREE(host);
3518                         TALLOC_FREE(hostnameDN);
3519                         return status;
3520                 }
3521 
3522                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3523                         msg_sub = ads_next_entry(ads, msg_sub)) {
3524 
3525                         char *dn = NULL;
3526 
3527                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3528                                 SAFE_FREE(host);
3529                                 TALLOC_FREE(hostnameDN);
3530                                 return ADS_ERROR(LDAP_NO_MEMORY);
3531                         }
3532 
3533                         status = ads_del_dn(ads, dn);
3534                         if (!ADS_ERR_OK(status)) {
3535                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3536                                 SAFE_FREE(host);
3537                                 TALLOC_FREE(dn);
3538                                 TALLOC_FREE(hostnameDN);
3539                                 return status;
3540                         }
3541 
3542                         TALLOC_FREE(dn);
3543                 }
3544 
3545                 /* there should be no subordinate objects anymore */
3546                 status = ads_do_search_retry(ads, hostnameDN,
3547                                              LDAP_SCOPE_ONELEVEL,
3548                                              "(objectclass=*)", attrs, &res);
3549 
3550                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3551                         SAFE_FREE(host);
3552                         TALLOC_FREE(hostnameDN);
3553                         return status;
3554                 }
3555 
3556                 /* delete hostnameDN now */
3557                 status = ads_del_dn(ads, hostnameDN);
3558                 if (!ADS_ERR_OK(status)) {
3559                         SAFE_FREE(host);
3560                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3561                         TALLOC_FREE(hostnameDN);
3562                         return status;
3563                 }
3564         }
3565 
3566         TALLOC_FREE(hostnameDN);
3567 
3568         status = ads_find_machine_acct(ads, &res, host);
3569         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3570                 DEBUG(3, ("Failed to remove host account.\n"));
3571                 SAFE_FREE(host);
3572                 return status;
3573         }
3574 
3575         SAFE_FREE(host);
3576         return status;
3577 }
3578 
3579 /**
3580  * pull all token-sids from an LDAP dn
3581  * @param ads connection to ads server
3582  * @param mem_ctx TALLOC_CTX for allocating sid array
3583  * @param dn of LDAP object
3584  * @param user_sid pointer to DOM_SID (objectSid)
3585  * @param primary_group_sid pointer to DOM_SID (self composed)
3586  * @param sids pointer to sid array to allocate
3587  * @param num_sids counter of SIDs pulled
3588  * @return status of token query
3589  **/
3590  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
     /* [<][>][^][v][top][bottom][index][help] */
3591                               TALLOC_CTX *mem_ctx,
3592                               const char *dn,
3593                               DOM_SID *user_sid,
3594                               DOM_SID *primary_group_sid,
3595                               DOM_SID **sids,
3596                               size_t *num_sids)
3597 {
3598         ADS_STATUS status;
3599         LDAPMessage *res = NULL;
3600         int count = 0;
3601         size_t tmp_num_sids;
3602         DOM_SID *tmp_sids;
3603         DOM_SID tmp_user_sid;
3604         DOM_SID tmp_primary_group_sid;
3605         uint32 pgid;
3606         const char *attrs[] = {
3607                 "objectSid",
3608                 "tokenGroups",
3609                 "primaryGroupID",
3610                 NULL
3611         };
3612 
3613         status = ads_search_retry_dn(ads, &res, dn, attrs);
3614         if (!ADS_ERR_OK(status)) {
3615                 return status;
3616         }
3617 
3618         count = ads_count_replies(ads, res);
3619         if (count != 1) {
3620                 ads_msgfree(ads, res);
3621                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3622         }
3623 
3624         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3625                 ads_msgfree(ads, res);
3626                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3627         }
3628 
3629         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3630                 ads_msgfree(ads, res);
3631                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3632         }
3633 
3634         {
3635                 /* hack to compose the primary group sid without knowing the
3636                  * domsid */
3637 
3638                 DOM_SID domsid;
3639                 uint32 dummy_rid;
3640 
3641                 sid_copy(&domsid, &tmp_user_sid);
3642 
3643                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3644                         ads_msgfree(ads, res);
3645                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3646                 }
3647 
3648                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3649                         ads_msgfree(ads, res);
3650                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3651                 }
3652         }
3653 
3654         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3655 
3656         if (tmp_num_sids == 0 || !tmp_sids) {
3657                 ads_msgfree(ads, res);
3658                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3659         }
3660 
3661         if (num_sids) {
3662                 *num_sids = tmp_num_sids;
3663         }
3664 
3665         if (sids) {
3666                 *sids = tmp_sids;
3667         }
3668 
3669         if (user_sid) {
3670                 *user_sid = tmp_user_sid;
3671         }
3672 
3673         if (primary_group_sid) {
3674                 *primary_group_sid = tmp_primary_group_sid;
3675         }
3676 
3677         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3678 
3679         ads_msgfree(ads, res);
3680         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3681 }
3682 
3683 /**
3684  * Find a sAMAccoutName in LDAP
3685  * @param ads connection to ads server
3686  * @param mem_ctx TALLOC_CTX for allocating sid array
3687  * @param samaccountname to search
3688  * @param uac_ret uint32 pointer userAccountControl attribute value
3689  * @param dn_ret pointer to dn
3690  * @return status of token query
3691  **/
3692 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
     /* [<][>][^][v][top][bottom][index][help] */
3693                                TALLOC_CTX *mem_ctx,
3694                                const char *samaccountname,
3695                                uint32 *uac_ret,
3696                                const char **dn_ret)
3697 {
3698         ADS_STATUS status;
3699         const char *attrs[] = { "userAccountControl", NULL };
3700         const char *filter;
3701         LDAPMessage *res = NULL;
3702         char *dn = NULL;
3703         uint32 uac = 0;
3704 
3705         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3706                 samaccountname);
3707         if (filter == NULL) {
3708                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3709                 goto out;
3710         }
3711 
3712         status = ads_do_search_all(ads, ads->config.bind_path,
3713                                    LDAP_SCOPE_SUBTREE,
3714                                    filter, attrs, &res);
3715         
3716         if (!ADS_ERR_OK(status)) {
3717                 goto out;
3718         }
3719 
3720         if (ads_count_replies(ads, res) != 1) {
3721                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3722                 goto out;
3723         }
3724 
3725         dn = ads_get_dn(ads, talloc_tos(), res);
3726         if (dn == NULL) {
3727                 status = ADS_ERROR(LDAP_NO_MEMORY);
3728                 goto out;
3729         }
3730 
3731         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3732                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3733                 goto out;
3734         }
3735 
3736         if (uac_ret) {
3737                 *uac_ret = uac;
3738         }
3739 
3740         if (dn_ret) {
3741                 *dn_ret = talloc_strdup(mem_ctx, dn);
3742                 if (!*dn_ret) {
3743                         status = ADS_ERROR(LDAP_NO_MEMORY);
3744                         goto out;
3745                 }
3746         }
3747  out:
3748         TALLOC_FREE(dn);
3749         ads_msgfree(ads, res);
3750 
3751         return status;
3752 }
3753 
3754 /**
3755  * find our configuration path 
3756  * @param ads connection to ads server
3757  * @param mem_ctx Pointer to talloc context
3758  * @param config_path Pointer to the config path
3759  * @return status of search
3760  **/
3761 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
     /* [<][>][^][v][top][bottom][index][help] */
3762                            TALLOC_CTX *mem_ctx, 
3763                            char **config_path)
3764 {
3765         ADS_STATUS status;
3766         LDAPMessage *res = NULL;
3767         const char *config_context = NULL;
3768         const char *attrs[] = { "configurationNamingContext", NULL };
3769 
3770         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3771                                "(objectclass=*)", attrs, &res);
3772         if (!ADS_ERR_OK(status)) {
3773                 return status;
3774         }
3775 
3776         config_context = ads_pull_string(ads, mem_ctx, res, 
3777                                          "configurationNamingContext");
3778         ads_msgfree(ads, res);
3779         if (!config_context) {
3780                 return ADS_ERROR(LDAP_NO_MEMORY);
3781         }
3782 
3783         if (config_path) {
3784                 *config_path = talloc_strdup(mem_ctx, config_context);
3785                 if (!*config_path) {
3786                         return ADS_ERROR(LDAP_NO_MEMORY);
3787                 }
3788         }
3789 
3790         return ADS_ERROR(LDAP_SUCCESS);
3791 }
3792 
3793 /**
3794  * find the displayName of an extended right 
3795  * @param ads connection to ads server
3796  * @param config_path The config path
3797  * @param mem_ctx Pointer to talloc context
3798  * @param GUID struct of the rightsGUID
3799  * @return status of search
3800  **/
3801 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
     /* [<][>][^][v][top][bottom][index][help] */
3802                                                 const char *config_path, 
3803                                                 TALLOC_CTX *mem_ctx, 
3804                                                 const struct GUID *rights_guid)
3805 {
3806         ADS_STATUS rc;
3807         LDAPMessage *res = NULL;
3808         char *expr = NULL;
3809         const char *attrs[] = { "displayName", NULL };
3810         const char *result = NULL;
3811         const char *path;
3812 
3813         if (!ads || !mem_ctx || !rights_guid) {
3814                 goto done;
3815         }
3816 
3817         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3818                                GUID_string(mem_ctx, rights_guid));
3819         if (!expr) {
3820                 goto done;
3821         }
3822 
3823         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3824         if (!path) {
3825                 goto done;
3826         }
3827 
3828         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3829                                  expr, attrs, &res);
3830         if (!ADS_ERR_OK(rc)) {
3831                 goto done;
3832         }
3833 
3834         if (ads_count_replies(ads, res) != 1) {
3835                 goto done;
3836         }
3837 
3838         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3839 
3840  done:
3841         ads_msgfree(ads, res);
3842         return result;
3843         
3844 }
3845 
3846 /**
3847  * verify or build and verify an account ou
3848  * @param mem_ctx Pointer to talloc context
3849  * @param ads connection to ads server
3850  * @param account_ou
3851  * @return status of search
3852  **/
3853 
3854 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
3855                            ADS_STRUCT *ads,
3856                            const char **account_ou)
3857 {
3858         struct ldb_dn *name_dn = NULL;
3859         const char *name = NULL;
3860         char *ou_string = NULL;
3861 
3862         name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3863         if (name_dn) {
3864                 return ADS_SUCCESS;
3865         }
3866 
3867         ou_string = ads_ou_string(ads, *account_ou);
3868         if (!ou_string) {
3869                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3870         }
3871 
3872         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3873                                ads->config.bind_path);
3874         SAFE_FREE(ou_string);
3875         if (!name) {
3876                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3877         }
3878 
3879         name_dn = ldb_dn_explode(mem_ctx, name);
3880         if (!name_dn) {
3881                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3882         }
3883 
3884         *account_ou = talloc_strdup(mem_ctx, name);
3885         if (!*account_ou) {
3886                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3887         }
3888 
3889         return ADS_SUCCESS;
3890 }
3891 
3892 #endif

/* [<][>][^][v][top][bottom][index][help] */