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