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