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