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