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