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