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