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