s3:dom_sid Global replace of DOM_SID with struct dom_sid
[kai/samba.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7    Copyright (C) Guenther Deschner 2005
8    Copyright (C) Gerald Carter 2006
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "libads/sitename_cache.h"
26
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(int signum)
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, 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, 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, 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, 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_name);
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_name, 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                 struct dom_sid sid;
2143                 fstring tmp;
2144                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2145                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2146         }
2147 }
2148
2149 /*
2150   dump ntSecurityDescriptor
2151 */
2152 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2153 {
2154         TALLOC_CTX *frame = talloc_stackframe();
2155         struct security_descriptor *psd;
2156         NTSTATUS status;
2157
2158         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2159                                      values[0]->bv_len, &psd);
2160         if (!NT_STATUS_IS_OK(status)) {
2161                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2162                           nt_errstr(status)));
2163                 TALLOC_FREE(frame);
2164                 return;
2165         }
2166
2167         if (psd) {
2168                 ads_disp_sd(ads, talloc_tos(), psd);
2169         }
2170
2171         TALLOC_FREE(frame);
2172 }
2173
2174 /*
2175   dump a string result from ldap
2176 */
2177 static void dump_string(const char *field, char **values)
2178 {
2179         int i;
2180         for (i=0; values[i]; i++) {
2181                 printf("%s: %s\n", field, values[i]);
2182         }
2183 }
2184
2185 /*
2186   dump a field from LDAP on stdout
2187   used for debugging
2188 */
2189
2190 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2191 {
2192         const struct {
2193                 const char *name;
2194                 bool string;
2195                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2196         } handlers[] = {
2197                 {"objectGUID", False, dump_guid},
2198                 {"netbootGUID", False, dump_guid},
2199                 {"nTSecurityDescriptor", False, dump_sd},
2200                 {"dnsRecord", False, dump_binary},
2201                 {"objectSid", False, dump_sid},
2202                 {"tokenGroups", False, dump_sid},
2203                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2204                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2205                 {"mS-DS-CreatorSID", False, dump_sid},
2206                 {"msExchMailboxGuid", False, dump_guid},
2207                 {NULL, True, NULL}
2208         };
2209         int i;
2210
2211         if (!field) { /* must be end of an entry */
2212                 printf("\n");
2213                 return False;
2214         }
2215
2216         for (i=0; handlers[i].name; i++) {
2217                 if (StrCaseCmp(handlers[i].name, field) == 0) {
2218                         if (!values) /* first time, indicate string or not */
2219                                 return handlers[i].string;
2220                         handlers[i].handler(ads, field, (struct berval **) values);
2221                         break;
2222                 }
2223         }
2224         if (!handlers[i].name) {
2225                 if (!values) /* first time, indicate string conversion */
2226                         return True;
2227                 dump_string(field, (char **)values);
2228         }
2229         return False;
2230 }
2231
2232 /**
2233  * Dump a result from LDAP on stdout
2234  *  used for debugging
2235  * @param ads connection to ads server
2236  * @param res Results to dump
2237  **/
2238
2239  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2240 {
2241         ads_process_results(ads, res, ads_dump_field, NULL);
2242 }
2243
2244 /**
2245  * Walk through results, calling a function for each entry found.
2246  *  The function receives a field name, a berval * array of values,
2247  *  and a data area passed through from the start.  The function is
2248  *  called once with null for field and values at the end of each
2249  *  entry.
2250  * @param ads connection to ads server
2251  * @param res Results to process
2252  * @param fn Function for processing each result
2253  * @param data_area user-defined area to pass to function
2254  **/
2255  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2256                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2257                           void *data_area)
2258 {
2259         LDAPMessage *msg;
2260         TALLOC_CTX *ctx;
2261         size_t converted_size;
2262
2263         if (!(ctx = talloc_init("ads_process_results")))
2264                 return;
2265
2266         for (msg = ads_first_entry(ads, res); msg; 
2267              msg = ads_next_entry(ads, msg)) {
2268                 char *utf8_field;
2269                 BerElement *b;
2270
2271                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2272                                                      (LDAPMessage *)msg,&b); 
2273                      utf8_field;
2274                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2275                                                     (LDAPMessage *)msg,b)) {
2276                         struct berval **ber_vals;
2277                         char **str_vals, **utf8_vals;
2278                         char *field;
2279                         bool string; 
2280
2281                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2282                                               &converted_size))
2283                         {
2284                                 DEBUG(0,("ads_process_results: "
2285                                          "pull_utf8_talloc failed: %s",
2286                                          strerror(errno)));
2287                         }
2288
2289                         string = fn(ads, field, NULL, data_area);
2290
2291                         if (string) {
2292                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2293                                                  (LDAPMessage *)msg, field);
2294                                 str_vals = ads_pull_strvals(ctx, 
2295                                                   (const char **) utf8_vals);
2296                                 fn(ads, field, (void **) str_vals, data_area);
2297                                 ldap_value_free(utf8_vals);
2298                         } else {
2299                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2300                                                  (LDAPMessage *)msg, field);
2301                                 fn(ads, field, (void **) ber_vals, data_area);
2302
2303                                 ldap_value_free_len(ber_vals);
2304                         }
2305                         ldap_memfree(utf8_field);
2306                 }
2307                 ber_free(b, 0);
2308                 talloc_free_children(ctx);
2309                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2310
2311         }
2312         talloc_destroy(ctx);
2313 }
2314
2315 /**
2316  * count how many replies are in a LDAPMessage
2317  * @param ads connection to ads server
2318  * @param res Results to count
2319  * @return number of replies
2320  **/
2321 int ads_count_replies(ADS_STRUCT *ads, void *res)
2322 {
2323         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2324 }
2325
2326 /**
2327  * pull the first entry from a ADS result
2328  * @param ads connection to ads server
2329  * @param res Results of search
2330  * @return first entry from result
2331  **/
2332  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2333 {
2334         return ldap_first_entry(ads->ldap.ld, res);
2335 }
2336
2337 /**
2338  * pull the next entry from a ADS result
2339  * @param ads connection to ads server
2340  * @param res Results of search
2341  * @return next entry from result
2342  **/
2343  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2344 {
2345         return ldap_next_entry(ads->ldap.ld, res);
2346 }
2347
2348 /**
2349  * pull the first message from a ADS result
2350  * @param ads connection to ads server
2351  * @param res Results of search
2352  * @return first message from result
2353  **/
2354  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2355 {
2356         return ldap_first_message(ads->ldap.ld, res);
2357 }
2358
2359 /**
2360  * pull the next message from a ADS result
2361  * @param ads connection to ads server
2362  * @param res Results of search
2363  * @return next message from result
2364  **/
2365  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2366 {
2367         return ldap_next_message(ads->ldap.ld, res);
2368 }
2369
2370 /**
2371  * pull a single string from a ADS result
2372  * @param ads connection to ads server
2373  * @param mem_ctx TALLOC_CTX to use for allocating result string
2374  * @param msg Results of search
2375  * @param field Attribute to retrieve
2376  * @return Result string in talloc context
2377  **/
2378  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2379                        const char *field)
2380 {
2381         char **values;
2382         char *ret = NULL;
2383         char *ux_string;
2384         size_t converted_size;
2385
2386         values = ldap_get_values(ads->ldap.ld, msg, field);
2387         if (!values)
2388                 return NULL;
2389
2390         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2391                                           &converted_size))
2392         {
2393                 ret = ux_string;
2394         }
2395         ldap_value_free(values);
2396         return ret;
2397 }
2398
2399 /**
2400  * pull an array of strings from a ADS result
2401  * @param ads connection to ads server
2402  * @param mem_ctx TALLOC_CTX to use for allocating result string
2403  * @param msg Results of search
2404  * @param field Attribute to retrieve
2405  * @return Result strings in talloc context
2406  **/
2407  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2408                          LDAPMessage *msg, const char *field,
2409                          size_t *num_values)
2410 {
2411         char **values;
2412         char **ret = NULL;
2413         int i;
2414         size_t converted_size;
2415
2416         values = ldap_get_values(ads->ldap.ld, msg, field);
2417         if (!values)
2418                 return NULL;
2419
2420         *num_values = ldap_count_values(values);
2421
2422         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2423         if (!ret) {
2424                 ldap_value_free(values);
2425                 return NULL;
2426         }
2427
2428         for (i=0;i<*num_values;i++) {
2429                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2430                                       &converted_size))
2431                 {
2432                         ldap_value_free(values);
2433                         return NULL;
2434                 }
2435         }
2436         ret[i] = NULL;
2437
2438         ldap_value_free(values);
2439         return ret;
2440 }
2441
2442 /**
2443  * pull an array of strings from a ADS result 
2444  *  (handle large multivalue attributes with range retrieval)
2445  * @param ads connection to ads server
2446  * @param mem_ctx TALLOC_CTX to use for allocating result string
2447  * @param msg Results of search
2448  * @param field Attribute to retrieve
2449  * @param current_strings strings returned by a previous call to this function
2450  * @param next_attribute The next query should ask for this attribute
2451  * @param num_values How many values did we get this time?
2452  * @param more_values Are there more values to get?
2453  * @return Result strings in talloc context
2454  **/
2455  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2456                                TALLOC_CTX *mem_ctx,
2457                                LDAPMessage *msg, const char *field,
2458                                char **current_strings,
2459                                const char **next_attribute,
2460                                size_t *num_strings,
2461                                bool *more_strings)
2462 {
2463         char *attr;
2464         char *expected_range_attrib, *range_attr;
2465         BerElement *ptr = NULL;
2466         char **strings;
2467         char **new_strings;
2468         size_t num_new_strings;
2469         unsigned long int range_start;
2470         unsigned long int range_end;
2471
2472         /* we might have been given the whole lot anyway */
2473         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2474                 *more_strings = False;
2475                 return strings;
2476         }
2477
2478         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2479
2480         /* look for Range result */
2481         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2482              attr; 
2483              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2484                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2485                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2486                         range_attr = attr;
2487                         break;
2488                 }
2489                 ldap_memfree(attr);
2490         }
2491         if (!attr) {
2492                 ber_free(ptr, 0);
2493                 /* nothing here - this field is just empty */
2494                 *more_strings = False;
2495                 return NULL;
2496         }
2497
2498         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2499                    &range_start, &range_end) == 2) {
2500                 *more_strings = True;
2501         } else {
2502                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2503                            &range_start) == 1) {
2504                         *more_strings = False;
2505                 } else {
2506                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2507                                   range_attr));
2508                         ldap_memfree(range_attr);
2509                         *more_strings = False;
2510                         return NULL;
2511                 }
2512         }
2513
2514         if ((*num_strings) != range_start) {
2515                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2516                           " - aborting range retreival\n",
2517                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2518                 ldap_memfree(range_attr);
2519                 *more_strings = False;
2520                 return NULL;
2521         }
2522
2523         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2524
2525         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2526                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2527                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2528                           range_attr, (unsigned long int)range_end - range_start + 1, 
2529                           (unsigned long int)num_new_strings));
2530                 ldap_memfree(range_attr);
2531                 *more_strings = False;
2532                 return NULL;
2533         }
2534
2535         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2536                                  *num_strings + num_new_strings);
2537
2538         if (strings == NULL) {
2539                 ldap_memfree(range_attr);
2540                 *more_strings = False;
2541                 return NULL;
2542         }
2543
2544         if (new_strings && num_new_strings) {
2545                 memcpy(&strings[*num_strings], new_strings,
2546                        sizeof(*new_strings) * num_new_strings);
2547         }
2548
2549         (*num_strings) += num_new_strings;
2550
2551         if (*more_strings) {
2552                 *next_attribute = talloc_asprintf(mem_ctx,
2553                                                   "%s;range=%d-*", 
2554                                                   field,
2555                                                   (int)*num_strings);
2556
2557                 if (!*next_attribute) {
2558                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2559                         ldap_memfree(range_attr);
2560                         *more_strings = False;
2561                         return NULL;
2562                 }
2563         }
2564
2565         ldap_memfree(range_attr);
2566
2567         return strings;
2568 }
2569
2570 /**
2571  * pull a single uint32 from a ADS result
2572  * @param ads connection to ads server
2573  * @param msg Results of search
2574  * @param field Attribute to retrieve
2575  * @param v Pointer to int to store result
2576  * @return boolean inidicating success
2577 */
2578  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2579                       uint32 *v)
2580 {
2581         char **values;
2582
2583         values = ldap_get_values(ads->ldap.ld, msg, field);
2584         if (!values)
2585                 return False;
2586         if (!values[0]) {
2587                 ldap_value_free(values);
2588                 return False;
2589         }
2590
2591         *v = atoi(values[0]);
2592         ldap_value_free(values);
2593         return True;
2594 }
2595
2596 /**
2597  * pull a single objectGUID from an ADS result
2598  * @param ads connection to ADS server
2599  * @param msg results of search
2600  * @param guid 37-byte area to receive text guid
2601  * @return boolean indicating success
2602  **/
2603  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2604 {
2605         char **values;
2606         UUID_FLAT flat_guid;
2607
2608         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2609         if (!values)
2610                 return False;
2611
2612         if (values[0]) {
2613                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2614                 smb_uuid_unpack(flat_guid, guid);
2615                 ldap_value_free(values);
2616                 return True;
2617         }
2618         ldap_value_free(values);
2619         return False;
2620
2621 }
2622
2623
2624 /**
2625  * pull a single struct dom_sid from a ADS result
2626  * @param ads connection to ads server
2627  * @param msg Results of search
2628  * @param field Attribute to retrieve
2629  * @param sid Pointer to sid to store result
2630  * @return boolean inidicating success
2631 */
2632  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2633                    struct dom_sid *sid)
2634 {
2635         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2636 }
2637
2638 /**
2639  * pull an array of struct dom_sids from a ADS result
2640  * @param ads connection to ads server
2641  * @param mem_ctx TALLOC_CTX for allocating sid array
2642  * @param msg Results of search
2643  * @param field Attribute to retrieve
2644  * @param sids pointer to sid array to allocate
2645  * @return the count of SIDs pulled
2646  **/
2647  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2648                    LDAPMessage *msg, const char *field, struct dom_sid **sids)
2649 {
2650         struct berval **values;
2651         bool ret;
2652         int count, i;
2653
2654         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2655
2656         if (!values)
2657                 return 0;
2658
2659         for (i=0; values[i]; i++)
2660                 /* nop */ ;
2661
2662         if (i) {
2663                 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2664                 if (!(*sids)) {
2665                         ldap_value_free_len(values);
2666                         return 0;
2667                 }
2668         } else {
2669                 (*sids) = NULL;
2670         }
2671
2672         count = 0;
2673         for (i=0; values[i]; i++) {
2674                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2675                 if (ret) {
2676                         DEBUG(10, ("pulling SID: %s\n",
2677                                    sid_string_dbg(&(*sids)[count])));
2678                         count++;
2679                 }
2680         }
2681
2682         ldap_value_free_len(values);
2683         return count;
2684 }
2685
2686 /**
2687  * pull a struct security_descriptor from a ADS result
2688  * @param ads connection to ads server
2689  * @param mem_ctx TALLOC_CTX for allocating sid array
2690  * @param msg Results of search
2691  * @param field Attribute to retrieve
2692  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2693  * @return boolean inidicating success
2694 */
2695  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2696                   LDAPMessage *msg, const char *field,
2697                   struct security_descriptor **sd)
2698 {
2699         struct berval **values;
2700         bool ret = true;
2701
2702         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2703
2704         if (!values) return false;
2705
2706         if (values[0]) {
2707                 NTSTATUS status;
2708                 status = unmarshall_sec_desc(mem_ctx,
2709                                              (uint8 *)values[0]->bv_val,
2710                                              values[0]->bv_len, sd);
2711                 if (!NT_STATUS_IS_OK(status)) {
2712                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2713                                   nt_errstr(status)));
2714                         ret = false;
2715                 }
2716         }
2717
2718         ldap_value_free_len(values);
2719         return ret;
2720 }
2721
2722 /* 
2723  * in order to support usernames longer than 21 characters we need to 
2724  * use both the sAMAccountName and the userPrincipalName attributes 
2725  * It seems that not all users have the userPrincipalName attribute set
2726  *
2727  * @param ads connection to ads server
2728  * @param mem_ctx TALLOC_CTX for allocating sid array
2729  * @param msg Results of search
2730  * @return the username
2731  */
2732  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2733                          LDAPMessage *msg)
2734 {
2735 #if 0   /* JERRY */
2736         char *ret, *p;
2737
2738         /* lookup_name() only works on the sAMAccountName to 
2739            returning the username portion of userPrincipalName
2740            breaks winbindd_getpwnam() */
2741
2742         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2743         if (ret && (p = strchr_m(ret, '@'))) {
2744                 *p = 0;
2745                 return ret;
2746         }
2747 #endif
2748         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2749 }
2750
2751
2752 /**
2753  * find the update serial number - this is the core of the ldap cache
2754  * @param ads connection to ads server
2755  * @param ads connection to ADS server
2756  * @param usn Pointer to retrieved update serial number
2757  * @return status of search
2758  **/
2759 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2760 {
2761         const char *attrs[] = {"highestCommittedUSN", NULL};
2762         ADS_STATUS status;
2763         LDAPMessage *res;
2764
2765         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2766         if (!ADS_ERR_OK(status)) 
2767                 return status;
2768
2769         if (ads_count_replies(ads, res) != 1) {
2770                 ads_msgfree(ads, res);
2771                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2772         }
2773
2774         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2775                 ads_msgfree(ads, res);
2776                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2777         }
2778
2779         ads_msgfree(ads, res);
2780         return ADS_SUCCESS;
2781 }
2782
2783 /* parse a ADS timestring - typical string is
2784    '20020917091222.0Z0' which means 09:12.22 17th September
2785    2002, timezone 0 */
2786 static time_t ads_parse_time(const char *str)
2787 {
2788         struct tm tm;
2789
2790         ZERO_STRUCT(tm);
2791
2792         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2793                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2794                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2795                 return 0;
2796         }
2797         tm.tm_year -= 1900;
2798         tm.tm_mon -= 1;
2799
2800         return timegm(&tm);
2801 }
2802
2803 /********************************************************************
2804 ********************************************************************/
2805
2806 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2807 {
2808         const char *attrs[] = {"currentTime", NULL};
2809         ADS_STATUS status;
2810         LDAPMessage *res;
2811         char *timestr;
2812         TALLOC_CTX *ctx;
2813         ADS_STRUCT *ads_s = ads;
2814
2815         if (!(ctx = talloc_init("ads_current_time"))) {
2816                 return ADS_ERROR(LDAP_NO_MEMORY);
2817         }
2818
2819         /* establish a new ldap tcp session if necessary */
2820
2821         if ( !ads->ldap.ld ) {
2822                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2823                         ads->server.ldap_server )) == NULL )
2824                 {
2825                         goto done;
2826                 }
2827                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2828                 status = ads_connect( ads_s );
2829                 if ( !ADS_ERR_OK(status))
2830                         goto done;
2831         }
2832
2833         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2834         if (!ADS_ERR_OK(status)) {
2835                 goto done;
2836         }
2837
2838         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2839         if (!timestr) {
2840                 ads_msgfree(ads_s, res);
2841                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2842                 goto done;
2843         }
2844
2845         /* but save the time and offset in the original ADS_STRUCT */   
2846
2847         ads->config.current_time = ads_parse_time(timestr);
2848
2849         if (ads->config.current_time != 0) {
2850                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2851                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2852         }
2853
2854         ads_msgfree(ads, res);
2855
2856         status = ADS_SUCCESS;
2857
2858 done:
2859         /* free any temporary ads connections */
2860         if ( ads_s != ads ) {
2861                 ads_destroy( &ads_s );
2862         }
2863         talloc_destroy(ctx);
2864
2865         return status;
2866 }
2867
2868 /********************************************************************
2869 ********************************************************************/
2870
2871 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2872 {
2873         const char *attrs[] = {"domainFunctionality", NULL};
2874         ADS_STATUS status;
2875         LDAPMessage *res;
2876         ADS_STRUCT *ads_s = ads;
2877
2878         *val = DS_DOMAIN_FUNCTION_2000;
2879
2880         /* establish a new ldap tcp session if necessary */
2881
2882         if ( !ads->ldap.ld ) {
2883                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2884                         ads->server.ldap_server )) == NULL )
2885                 {
2886                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2887                         goto done;
2888                 }
2889                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2890                 status = ads_connect( ads_s );
2891                 if ( !ADS_ERR_OK(status))
2892                         goto done;
2893         }
2894
2895         /* If the attribute does not exist assume it is a Windows 2000 
2896            functional domain */
2897
2898         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2899         if (!ADS_ERR_OK(status)) {
2900                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2901                         status = ADS_SUCCESS;
2902                 }
2903                 goto done;
2904         }
2905
2906         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2907                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2908         }
2909         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2910
2911
2912         ads_msgfree(ads, res);
2913
2914 done:
2915         /* free any temporary ads connections */
2916         if ( ads_s != ads ) {
2917                 ads_destroy( &ads_s );
2918         }
2919
2920         return status;
2921 }
2922
2923 /**
2924  * find the domain sid for our domain
2925  * @param ads connection to ads server
2926  * @param sid Pointer to domain sid
2927  * @return status of search
2928  **/
2929 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2930 {
2931         const char *attrs[] = {"objectSid", NULL};
2932         LDAPMessage *res;
2933         ADS_STATUS rc;
2934
2935         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2936                            attrs, &res);
2937         if (!ADS_ERR_OK(rc)) return rc;
2938         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2939                 ads_msgfree(ads, res);
2940                 return ADS_ERROR_SYSTEM(ENOENT);
2941         }
2942         ads_msgfree(ads, res);
2943
2944         return ADS_SUCCESS;
2945 }
2946
2947 /**
2948  * find our site name 
2949  * @param ads connection to ads server
2950  * @param mem_ctx Pointer to talloc context
2951  * @param site_name Pointer to the sitename
2952  * @return status of search
2953  **/
2954 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2955 {
2956         ADS_STATUS status;
2957         LDAPMessage *res;
2958         const char *dn, *service_name;
2959         const char *attrs[] = { "dsServiceName", NULL };
2960
2961         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2962         if (!ADS_ERR_OK(status)) {
2963                 return status;
2964         }
2965
2966         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2967         if (service_name == NULL) {
2968                 ads_msgfree(ads, res);
2969                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2970         }
2971
2972         ads_msgfree(ads, res);
2973
2974         /* go up three levels */
2975         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2976         if (dn == NULL) {
2977                 return ADS_ERROR(LDAP_NO_MEMORY);
2978         }
2979
2980         *site_name = talloc_strdup(mem_ctx, dn);
2981         if (*site_name == NULL) {
2982                 return ADS_ERROR(LDAP_NO_MEMORY);
2983         }
2984
2985         return status;
2986         /*
2987         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2988         */                                               
2989 }
2990
2991 /**
2992  * find the site dn where a machine resides
2993  * @param ads connection to ads server
2994  * @param mem_ctx Pointer to talloc context
2995  * @param computer_name name of the machine
2996  * @param site_name Pointer to the sitename
2997  * @return status of search
2998  **/
2999 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3000 {
3001         ADS_STATUS status;
3002         LDAPMessage *res;
3003         const char *parent, *filter;
3004         char *config_context = NULL;
3005         char *dn;
3006
3007         /* shortcut a query */
3008         if (strequal(computer_name, ads->config.ldap_server_name)) {
3009                 return ads_site_dn(ads, mem_ctx, site_dn);
3010         }
3011
3012         status = ads_config_path(ads, mem_ctx, &config_context);
3013         if (!ADS_ERR_OK(status)) {
3014                 return status;
3015         }
3016
3017         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3018         if (filter == NULL) {
3019                 return ADS_ERROR(LDAP_NO_MEMORY);
3020         }
3021
3022         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3023                                filter, NULL, &res);
3024         if (!ADS_ERR_OK(status)) {
3025                 return status;
3026         }
3027
3028         if (ads_count_replies(ads, res) != 1) {
3029                 ads_msgfree(ads, res);
3030                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3031         }
3032
3033         dn = ads_get_dn(ads, mem_ctx, res);
3034         if (dn == NULL) {
3035                 ads_msgfree(ads, res);
3036                 return ADS_ERROR(LDAP_NO_MEMORY);
3037         }
3038
3039         /* go up three levels */
3040         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3041         if (parent == NULL) {
3042                 ads_msgfree(ads, res);
3043                 TALLOC_FREE(dn);
3044                 return ADS_ERROR(LDAP_NO_MEMORY);
3045         }
3046
3047         *site_dn = talloc_strdup(mem_ctx, parent);
3048         if (*site_dn == NULL) {
3049                 ads_msgfree(ads, res);
3050                 TALLOC_FREE(dn);
3051                 return ADS_ERROR(LDAP_NO_MEMORY);
3052         }
3053
3054         TALLOC_FREE(dn);
3055         ads_msgfree(ads, res);
3056
3057         return status;
3058 }
3059
3060 /**
3061  * get the upn suffixes for a domain
3062  * @param ads connection to ads server
3063  * @param mem_ctx Pointer to talloc context
3064  * @param suffixes Pointer to an array of suffixes
3065  * @param num_suffixes Pointer to the number of suffixes
3066  * @return status of search
3067  **/
3068 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3069 {
3070         ADS_STATUS status;
3071         LDAPMessage *res;
3072         const char *base;
3073         char *config_context = NULL;
3074         const char *attrs[] = { "uPNSuffixes", NULL };
3075
3076         status = ads_config_path(ads, mem_ctx, &config_context);
3077         if (!ADS_ERR_OK(status)) {
3078                 return status;
3079         }
3080
3081         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3082         if (base == NULL) {
3083                 return ADS_ERROR(LDAP_NO_MEMORY);
3084         }
3085
3086         status = ads_search_dn(ads, &res, base, attrs);
3087         if (!ADS_ERR_OK(status)) {
3088                 return status;
3089         }
3090
3091         if (ads_count_replies(ads, res) != 1) {
3092                 ads_msgfree(ads, res);
3093                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3094         }
3095
3096         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3097         if ((*suffixes) == NULL) {
3098                 ads_msgfree(ads, res);
3099                 return ADS_ERROR(LDAP_NO_MEMORY);
3100         }
3101
3102         ads_msgfree(ads, res);
3103
3104         return status;
3105 }
3106
3107 /**
3108  * get the joinable ous for a domain
3109  * @param ads connection to ads server
3110  * @param mem_ctx Pointer to talloc context
3111  * @param ous Pointer to an array of ous
3112  * @param num_ous Pointer to the number of ous
3113  * @return status of search
3114  **/
3115 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3116                                 TALLOC_CTX *mem_ctx,
3117                                 char ***ous,
3118                                 size_t *num_ous)
3119 {
3120         ADS_STATUS status;
3121         LDAPMessage *res = NULL;
3122         LDAPMessage *msg = NULL;
3123         const char *attrs[] = { "dn", NULL };
3124         int count = 0;
3125
3126         status = ads_search(ads, &res,
3127                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3128                             attrs);
3129         if (!ADS_ERR_OK(status)) {
3130                 return status;
3131         }
3132
3133         count = ads_count_replies(ads, res);
3134         if (count < 1) {
3135                 ads_msgfree(ads, res);
3136                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3137         }
3138
3139         for (msg = ads_first_entry(ads, res); msg;
3140              msg = ads_next_entry(ads, msg)) {
3141
3142                 char *dn = NULL;
3143
3144                 dn = ads_get_dn(ads, talloc_tos(), msg);
3145                 if (!dn) {
3146                         ads_msgfree(ads, res);
3147                         return ADS_ERROR(LDAP_NO_MEMORY);
3148                 }
3149
3150                 if (!add_string_to_array(mem_ctx, dn,
3151                                          (const char ***)ous,
3152                                          (int *)num_ous)) {
3153                         TALLOC_FREE(dn);
3154                         ads_msgfree(ads, res);
3155                         return ADS_ERROR(LDAP_NO_MEMORY);
3156                 }
3157
3158                 TALLOC_FREE(dn);
3159         }
3160
3161         ads_msgfree(ads, res);
3162
3163         return status;
3164 }
3165
3166
3167 /**
3168  * pull a struct dom_sid from an extended dn string
3169  * @param mem_ctx TALLOC_CTX
3170  * @param extended_dn string
3171  * @param flags string type of extended_dn
3172  * @param sid pointer to a struct dom_sid
3173  * @return NT_STATUS_OK on success,
3174  *         NT_INVALID_PARAMETER on error,
3175  *         NT_STATUS_NOT_FOUND if no SID present
3176  **/
3177 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3178                                         const char *extended_dn,
3179                                         enum ads_extended_dn_flags flags,
3180                                         struct dom_sid *sid)
3181 {
3182         char *p, *q, *dn;
3183
3184         if (!extended_dn) {
3185                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3186         }
3187
3188         /* otherwise extended_dn gets stripped off */
3189         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3190                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3191         }
3192         /*
3193          * ADS_EXTENDED_DN_HEX_STRING:
3194          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3195          *
3196          * ADS_EXTENDED_DN_STRING (only with w2k3):
3197          * <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
3198          *
3199          * Object with no SID, such as an Exchange Public Folder
3200          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3201          */
3202
3203         p = strchr(dn, ';');
3204         if (!p) {
3205                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3206         }
3207
3208         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3209                 DEBUG(5,("No SID present in extended dn\n"));
3210                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3211         }
3212
3213         p += strlen(";<SID=");
3214
3215         q = strchr(p, '>');
3216         if (!q) {
3217                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3218         }
3219
3220         *q = '\0';
3221
3222         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3223
3224         switch (flags) {
3225
3226         case ADS_EXTENDED_DN_STRING:
3227                 if (!string_to_sid(sid, p)) {
3228                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3229                 }
3230                 break;
3231         case ADS_EXTENDED_DN_HEX_STRING: {
3232                 fstring buf;
3233                 size_t buf_len;
3234
3235                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3236                 if (buf_len == 0) {
3237                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3238                 }
3239
3240                 if (!sid_parse(buf, buf_len, sid)) {
3241                         DEBUG(10,("failed to parse sid\n"));
3242                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3243                 }
3244                 break;
3245                 }
3246         default:
3247                 DEBUG(10,("unknown extended dn format\n"));
3248                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3249         }
3250
3251         return ADS_ERROR_NT(NT_STATUS_OK);
3252 }
3253
3254 /**
3255  * pull an array of struct dom_sids from a ADS result
3256  * @param ads connection to ads server
3257  * @param mem_ctx TALLOC_CTX for allocating sid array
3258  * @param msg Results of search
3259  * @param field Attribute to retrieve
3260  * @param flags string type of extended_dn
3261  * @param sids pointer to sid array to allocate
3262  * @return the count of SIDs pulled
3263  **/
3264  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3265                                    TALLOC_CTX *mem_ctx,
3266                                    LDAPMessage *msg,
3267                                    const char *field,
3268                                    enum ads_extended_dn_flags flags,
3269                                    struct dom_sid **sids)
3270 {
3271         int i;
3272         ADS_STATUS rc;
3273         size_t dn_count, ret_count = 0;
3274         char **dn_strings;
3275
3276         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3277                                            &dn_count)) == NULL) {
3278                 return 0;
3279         }
3280
3281         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3282         if (!(*sids)) {
3283                 TALLOC_FREE(dn_strings);
3284                 return 0;
3285         }
3286
3287         for (i=0; i<dn_count; i++) {
3288                 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3289                                                   flags, &(*sids)[i]);
3290                 if (!ADS_ERR_OK(rc)) {
3291                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3292                             NT_STATUS_NOT_FOUND)) {
3293                                 continue;
3294                         }
3295                         else {
3296                                 TALLOC_FREE(*sids);
3297                                 TALLOC_FREE(dn_strings);
3298                                 return 0;
3299                         }
3300                 }
3301                 ret_count++;
3302         }
3303
3304         TALLOC_FREE(dn_strings);
3305
3306         return ret_count;
3307 }
3308
3309 /********************************************************************
3310 ********************************************************************/
3311
3312 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3313 {
3314         LDAPMessage *res = NULL;
3315         ADS_STATUS status;
3316         int count = 0;
3317         char *name = NULL;
3318
3319         status = ads_find_machine_acct(ads, &res, global_myname());
3320         if (!ADS_ERR_OK(status)) {
3321                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3322                         global_myname()));
3323                 goto out;
3324         }
3325
3326         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3327                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3328                 goto out;
3329         }
3330
3331         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3332                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3333         }
3334
3335 out:
3336         ads_msgfree(ads, res);
3337
3338         return name;
3339 }
3340
3341 /********************************************************************
3342 ********************************************************************/
3343
3344 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3345 {
3346         LDAPMessage *res = NULL;
3347         ADS_STATUS status;
3348         int count = 0;
3349         char *name = NULL;
3350
3351         status = ads_find_machine_acct(ads, &res, machine_name);
3352         if (!ADS_ERR_OK(status)) {
3353                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3354                         global_myname()));
3355                 goto out;
3356         }
3357
3358         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3359                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3360                 goto out;
3361         }
3362
3363         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3364                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3365         }
3366
3367 out:
3368         ads_msgfree(ads, res);
3369
3370         return name;
3371 }
3372
3373 /********************************************************************
3374 ********************************************************************/
3375
3376 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3377 {
3378         LDAPMessage *res = NULL;
3379         ADS_STATUS status;
3380         int count = 0;
3381         char *name = NULL;
3382
3383         status = ads_find_machine_acct(ads, &res, global_myname());
3384         if (!ADS_ERR_OK(status)) {
3385                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3386                         global_myname()));
3387                 goto out;
3388         }
3389
3390         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3391                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3392                 goto out;
3393         }
3394
3395         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3396                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3397         }
3398
3399 out:
3400         ads_msgfree(ads, res);
3401
3402         return name;
3403 }
3404
3405 #if 0
3406
3407    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3408
3409 /**
3410  * Join a machine to a realm
3411  *  Creates the machine account and sets the machine password
3412  * @param ads connection to ads server
3413  * @param machine name of host to add
3414  * @param org_unit Organizational unit to place machine in
3415  * @return status of join
3416  **/
3417 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3418                         uint32 account_type, const char *org_unit)
3419 {
3420         ADS_STATUS status;
3421         LDAPMessage *res = NULL;
3422         char *machine;
3423
3424         /* machine name must be lowercase */
3425         machine = SMB_STRDUP(machine_name);
3426         strlower_m(machine);
3427
3428         /*
3429         status = ads_find_machine_acct(ads, (void **)&res, machine);
3430         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3431                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3432                 status = ads_leave_realm(ads, machine);
3433                 if (!ADS_ERR_OK(status)) {
3434                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3435                                 machine, ads->config.realm));
3436                         return status;
3437                 }
3438         }
3439         */
3440         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3441         if (!ADS_ERR_OK(status)) {
3442                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3443                 SAFE_FREE(machine);
3444                 return status;
3445         }
3446
3447         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3448         if (!ADS_ERR_OK(status)) {
3449                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3450                 SAFE_FREE(machine);
3451                 return status;
3452         }
3453
3454         SAFE_FREE(machine);
3455         ads_msgfree(ads, res);
3456
3457         return status;
3458 }
3459 #endif
3460
3461 /**
3462  * Delete a machine from the realm
3463  * @param ads connection to ads server
3464  * @param hostname Machine to remove
3465  * @return status of delete
3466  **/
3467 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3468 {
3469         ADS_STATUS status;
3470         void *msg;
3471         LDAPMessage *res;
3472         char *hostnameDN, *host;
3473         int rc;
3474         LDAPControl ldap_control;
3475         LDAPControl  * pldap_control[2] = {NULL, NULL};
3476
3477         pldap_control[0] = &ldap_control;
3478         memset(&ldap_control, 0, sizeof(LDAPControl));
3479         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3480
3481         /* hostname must be lowercase */
3482         host = SMB_STRDUP(hostname);
3483         strlower_m(host);
3484
3485         status = ads_find_machine_acct(ads, &res, host);
3486         if (!ADS_ERR_OK(status)) {
3487                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3488                 SAFE_FREE(host);
3489                 return status;
3490         }
3491
3492         msg = ads_first_entry(ads, res);
3493         if (!msg) {
3494                 SAFE_FREE(host);
3495                 return ADS_ERROR_SYSTEM(ENOENT);
3496         }
3497
3498         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3499
3500         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3501         if (rc) {
3502                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3503         }else {
3504                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3505         }
3506
3507         if (rc != LDAP_SUCCESS) {
3508                 const char *attrs[] = { "cn", NULL };
3509                 LDAPMessage *msg_sub;
3510
3511                 /* we only search with scope ONE, we do not expect any further
3512                  * objects to be created deeper */
3513
3514                 status = ads_do_search_retry(ads, hostnameDN,
3515                                              LDAP_SCOPE_ONELEVEL,
3516                                              "(objectclass=*)", attrs, &res);
3517
3518                 if (!ADS_ERR_OK(status)) {
3519                         SAFE_FREE(host);
3520                         TALLOC_FREE(hostnameDN);
3521                         return status;
3522                 }
3523
3524                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3525                         msg_sub = ads_next_entry(ads, msg_sub)) {
3526
3527                         char *dn = NULL;
3528
3529                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3530                                 SAFE_FREE(host);
3531                                 TALLOC_FREE(hostnameDN);
3532                                 return ADS_ERROR(LDAP_NO_MEMORY);
3533                         }
3534
3535                         status = ads_del_dn(ads, dn);
3536                         if (!ADS_ERR_OK(status)) {
3537                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3538                                 SAFE_FREE(host);
3539                                 TALLOC_FREE(dn);
3540                                 TALLOC_FREE(hostnameDN);
3541                                 return status;
3542                         }
3543
3544                         TALLOC_FREE(dn);
3545                 }
3546
3547                 /* there should be no subordinate objects anymore */
3548                 status = ads_do_search_retry(ads, hostnameDN,
3549                                              LDAP_SCOPE_ONELEVEL,
3550                                              "(objectclass=*)", attrs, &res);
3551
3552                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3553                         SAFE_FREE(host);
3554                         TALLOC_FREE(hostnameDN);
3555                         return status;
3556                 }
3557
3558                 /* delete hostnameDN now */
3559                 status = ads_del_dn(ads, hostnameDN);
3560                 if (!ADS_ERR_OK(status)) {
3561                         SAFE_FREE(host);
3562                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3563                         TALLOC_FREE(hostnameDN);
3564                         return status;
3565                 }
3566         }
3567
3568         TALLOC_FREE(hostnameDN);
3569
3570         status = ads_find_machine_acct(ads, &res, host);
3571         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3572                 DEBUG(3, ("Failed to remove host account.\n"));
3573                 SAFE_FREE(host);
3574                 return status;
3575         }
3576
3577         SAFE_FREE(host);
3578         return status;
3579 }
3580
3581 /**
3582  * pull all token-sids from an LDAP dn
3583  * @param ads connection to ads server
3584  * @param mem_ctx TALLOC_CTX for allocating sid array
3585  * @param dn of LDAP object
3586  * @param user_sid pointer to struct dom_sid (objectSid)
3587  * @param primary_group_sid pointer to struct dom_sid (self composed)
3588  * @param sids pointer to sid array to allocate
3589  * @param num_sids counter of SIDs pulled
3590  * @return status of token query
3591  **/
3592  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3593                               TALLOC_CTX *mem_ctx,
3594                               const char *dn,
3595                               struct dom_sid *user_sid,
3596                               struct dom_sid *primary_group_sid,
3597                               struct dom_sid **sids,
3598                               size_t *num_sids)
3599 {
3600         ADS_STATUS status;
3601         LDAPMessage *res = NULL;
3602         int count = 0;
3603         size_t tmp_num_sids;
3604         struct dom_sid *tmp_sids;
3605         struct dom_sid tmp_user_sid;
3606         struct dom_sid tmp_primary_group_sid;
3607         uint32 pgid;
3608         const char *attrs[] = {
3609                 "objectSid",
3610                 "tokenGroups",
3611                 "primaryGroupID",
3612                 NULL
3613         };
3614
3615         status = ads_search_retry_dn(ads, &res, dn, attrs);
3616         if (!ADS_ERR_OK(status)) {
3617                 return status;
3618         }
3619
3620         count = ads_count_replies(ads, res);
3621         if (count != 1) {
3622                 ads_msgfree(ads, res);
3623                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3624         }
3625
3626         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3627                 ads_msgfree(ads, res);
3628                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3629         }
3630
3631         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3632                 ads_msgfree(ads, res);
3633                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3634         }
3635
3636         {
3637                 /* hack to compose the primary group sid without knowing the
3638                  * domsid */
3639
3640                 struct dom_sid domsid;
3641                 uint32 dummy_rid;
3642
3643                 sid_copy(&domsid, &tmp_user_sid);
3644
3645                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3646                         ads_msgfree(ads, res);
3647                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3648                 }
3649
3650                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3651                         ads_msgfree(ads, res);
3652                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3653                 }
3654         }
3655
3656         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3657
3658         if (tmp_num_sids == 0 || !tmp_sids) {
3659                 ads_msgfree(ads, res);
3660                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3661         }
3662
3663         if (num_sids) {
3664                 *num_sids = tmp_num_sids;
3665         }
3666
3667         if (sids) {
3668                 *sids = tmp_sids;
3669         }
3670
3671         if (user_sid) {
3672                 *user_sid = tmp_user_sid;
3673         }
3674
3675         if (primary_group_sid) {
3676                 *primary_group_sid = tmp_primary_group_sid;
3677         }
3678
3679         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3680
3681         ads_msgfree(ads, res);
3682         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3683 }
3684
3685 /**
3686  * Find a sAMAccoutName in LDAP
3687  * @param ads connection to ads server
3688  * @param mem_ctx TALLOC_CTX for allocating sid array
3689  * @param samaccountname to search
3690  * @param uac_ret uint32 pointer userAccountControl attribute value
3691  * @param dn_ret pointer to dn
3692  * @return status of token query
3693  **/
3694 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3695                                TALLOC_CTX *mem_ctx,
3696                                const char *samaccountname,
3697                                uint32 *uac_ret,
3698                                const char **dn_ret)
3699 {
3700         ADS_STATUS status;
3701         const char *attrs[] = { "userAccountControl", NULL };
3702         const char *filter;
3703         LDAPMessage *res = NULL;
3704         char *dn = NULL;
3705         uint32 uac = 0;
3706
3707         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3708                 samaccountname);
3709         if (filter == NULL) {
3710                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3711                 goto out;
3712         }
3713
3714         status = ads_do_search_all(ads, ads->config.bind_path,
3715                                    LDAP_SCOPE_SUBTREE,
3716                                    filter, attrs, &res);
3717
3718         if (!ADS_ERR_OK(status)) {
3719                 goto out;
3720         }
3721
3722         if (ads_count_replies(ads, res) != 1) {
3723                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3724                 goto out;
3725         }
3726
3727         dn = ads_get_dn(ads, talloc_tos(), res);
3728         if (dn == NULL) {
3729                 status = ADS_ERROR(LDAP_NO_MEMORY);
3730                 goto out;
3731         }
3732
3733         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3734                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3735                 goto out;
3736         }
3737
3738         if (uac_ret) {
3739                 *uac_ret = uac;
3740         }
3741
3742         if (dn_ret) {
3743                 *dn_ret = talloc_strdup(mem_ctx, dn);
3744                 if (!*dn_ret) {
3745                         status = ADS_ERROR(LDAP_NO_MEMORY);
3746                         goto out;
3747                 }
3748         }
3749  out:
3750         TALLOC_FREE(dn);
3751         ads_msgfree(ads, res);
3752
3753         return status;
3754 }
3755
3756 /**
3757  * find our configuration path 
3758  * @param ads connection to ads server
3759  * @param mem_ctx Pointer to talloc context
3760  * @param config_path Pointer to the config path
3761  * @return status of search
3762  **/
3763 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3764                            TALLOC_CTX *mem_ctx, 
3765                            char **config_path)
3766 {
3767         ADS_STATUS status;
3768         LDAPMessage *res = NULL;
3769         const char *config_context = NULL;
3770         const char *attrs[] = { "configurationNamingContext", NULL };
3771
3772         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3773                                "(objectclass=*)", attrs, &res);
3774         if (!ADS_ERR_OK(status)) {
3775                 return status;
3776         }
3777
3778         config_context = ads_pull_string(ads, mem_ctx, res, 
3779                                          "configurationNamingContext");
3780         ads_msgfree(ads, res);
3781         if (!config_context) {
3782                 return ADS_ERROR(LDAP_NO_MEMORY);
3783         }
3784
3785         if (config_path) {
3786                 *config_path = talloc_strdup(mem_ctx, config_context);
3787                 if (!*config_path) {
3788                         return ADS_ERROR(LDAP_NO_MEMORY);
3789                 }
3790         }
3791
3792         return ADS_ERROR(LDAP_SUCCESS);
3793 }
3794
3795 /**
3796  * find the displayName of an extended right 
3797  * @param ads connection to ads server
3798  * @param config_path The config path
3799  * @param mem_ctx Pointer to talloc context
3800  * @param GUID struct of the rightsGUID
3801  * @return status of search
3802  **/
3803 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3804                                                 const char *config_path, 
3805                                                 TALLOC_CTX *mem_ctx, 
3806                                                 const struct GUID *rights_guid)
3807 {
3808         ADS_STATUS rc;
3809         LDAPMessage *res = NULL;
3810         char *expr = NULL;
3811         const char *attrs[] = { "displayName", NULL };
3812         const char *result = NULL;
3813         const char *path;
3814
3815         if (!ads || !mem_ctx || !rights_guid) {
3816                 goto done;
3817         }
3818
3819         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3820                                GUID_string(mem_ctx, rights_guid));
3821         if (!expr) {
3822                 goto done;
3823         }
3824
3825         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3826         if (!path) {
3827                 goto done;
3828         }
3829
3830         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3831                                  expr, attrs, &res);
3832         if (!ADS_ERR_OK(rc)) {
3833                 goto done;
3834         }
3835
3836         if (ads_count_replies(ads, res) != 1) {
3837                 goto done;
3838         }
3839
3840         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3841
3842  done:
3843         ads_msgfree(ads, res);
3844         return result;
3845 }
3846
3847 /**
3848  * verify or build and verify an account ou
3849  * @param mem_ctx Pointer to talloc context
3850  * @param ads connection to ads server
3851  * @param account_ou
3852  * @return status of search
3853  **/
3854
3855 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3856                            ADS_STRUCT *ads,
3857                            const char **account_ou)
3858 {
3859         char **exploded_dn;
3860         const char *name;
3861         char *ou_string;
3862
3863         exploded_dn = ldap_explode_dn(*account_ou, 0);
3864         if (exploded_dn) {
3865                 ldap_value_free(exploded_dn);
3866                 return ADS_SUCCESS;
3867         }
3868
3869         ou_string = ads_ou_string(ads, *account_ou);
3870         if (!ou_string) {
3871                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3872         }
3873
3874         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3875                                ads->config.bind_path);
3876         SAFE_FREE(ou_string);
3877
3878         if (!name) {
3879                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3880         }
3881
3882         exploded_dn = ldap_explode_dn(name, 0);
3883         if (!exploded_dn) {
3884                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3885         }
3886         ldap_value_free(exploded_dn);
3887
3888         *account_ou = name;
3889         return ADS_SUCCESS;
3890 }
3891
3892 #endif