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