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