s3 move the sitename cache in its own file
[obnox/samba/samba-obnox.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7    Copyright (C) Guenther Deschner 2005
8    Copyright (C) Gerald Carter 2006
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "libads/sitename_cache.h"
27
28 #ifdef HAVE_LDAP
29
30 /**
31  * @file ldap.c
32  * @brief basic ldap client-side routines for ads server communications
33  *
34  * The routines contained here should do the necessary ldap calls for
35  * ads setups.
36  * 
37  * Important note: attribute names passed into ads_ routines must
38  * already be in UTF-8 format.  We do not convert them because in almost
39  * all cases, they are just ascii (which is represented with the same
40  * codepoints in UTF-8).  This may have to change at some point
41  **/
42
43
44 #define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
45
46 static SIG_ATOMIC_T gotalarm;
47
48 /***************************************************************
49  Signal function to tell us we timed out.
50 ****************************************************************/
51
52 static void gotalarm_sig(int signum)
53 {
54         gotalarm = 1;
55 }
56
57  LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 {
59         LDAP *ldp = NULL;
60
61
62         DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
63                    "%u seconds\n", server, port, to));
64
65         /* Setup timeout */
66         gotalarm = 0;
67         CatchSignal(SIGALRM, gotalarm_sig);
68         alarm(to);
69         /* End setup timeout. */
70
71         ldp = ldap_open(server, port);
72
73         if (ldp == NULL) {
74                 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
75                          server, port, strerror(errno)));
76         } else {
77                 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
78         }
79
80         /* Teardown timeout. */
81         CatchSignal(SIGALRM, SIG_IGN);
82         alarm(0);
83
84         return ldp;
85 }
86
87 static int ldap_search_with_timeout(LDAP *ld,
88                                     LDAP_CONST char *base,
89                                     int scope,
90                                     LDAP_CONST char *filter,
91                                     char **attrs,
92                                     int attrsonly,
93                                     LDAPControl **sctrls,
94                                     LDAPControl **cctrls,
95                                     int sizelimit,
96                                     LDAPMessage **res )
97 {
98         struct timeval timeout;
99         int result;
100
101         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
102         timeout.tv_sec = lp_ldap_timeout();
103         timeout.tv_usec = 0;
104
105         /* Setup alarm timeout.... Do we need both of these ? JRA. */
106         gotalarm = 0;
107         CatchSignal(SIGALRM, gotalarm_sig);
108         alarm(lp_ldap_timeout());
109         /* End setup timeout. */
110
111         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
112                                    attrsonly, sctrls, cctrls, &timeout,
113                                    sizelimit, res);
114
115         /* Teardown timeout. */
116         CatchSignal(SIGALRM, SIG_IGN);
117         alarm(0);
118
119         if (gotalarm != 0)
120                 return LDAP_TIMELIMIT_EXCEEDED;
121
122         /*
123          * A bug in OpenLDAP means ldap_search_ext_s can return
124          * LDAP_SUCCESS but with a NULL res pointer. Cope with
125          * this. See bug #6279 for details. JRA.
126          */
127
128         if (*res == NULL) {
129                 return LDAP_TIMELIMIT_EXCEEDED;
130         }
131
132         return result;
133 }
134
135 /**********************************************
136  Do client and server sitename match ?
137 **********************************************/
138
139 bool ads_sitename_match(ADS_STRUCT *ads)
140 {
141         if (ads->config.server_site_name == NULL &&
142             ads->config.client_site_name == NULL ) {
143                 DEBUG(10,("ads_sitename_match: both null\n"));
144                 return True;
145         }
146         if (ads->config.server_site_name &&
147             ads->config.client_site_name &&
148             strequal(ads->config.server_site_name,
149                      ads->config.client_site_name)) {
150                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
151                 return True;
152         }
153         DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
154                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
155                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
156         return False;
157 }
158
159 /**********************************************
160  Is this the closest DC ?
161 **********************************************/
162
163 bool ads_closest_dc(ADS_STRUCT *ads)
164 {
165         if (ads->config.flags & NBT_SERVER_CLOSEST) {
166                 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
167                 return True;
168         }
169
170         /* not sure if this can ever happen */
171         if (ads_sitename_match(ads)) {
172                 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
173                 return True;
174         }
175
176         if (ads->config.client_site_name == NULL) {
177                 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
178                 return True;
179         }
180
181         DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", 
182                 ads->config.ldap_server_name));
183
184         return False;
185 }
186
187
188 /*
189   try a connection to a given ldap server, returning True and setting the servers IP
190   in the ads struct if successful
191  */
192 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
193 {
194         char *srv;
195         struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
196         TALLOC_CTX *frame = talloc_stackframe();
197         bool ret = false;
198
199         if (!server || !*server) {
200                 TALLOC_FREE(frame);
201                 return False;
202         }
203
204         if (!is_ipaddress(server)) {
205                 struct sockaddr_storage ss;
206                 char addr[INET6_ADDRSTRLEN];
207
208                 if (!resolve_name(server, &ss, 0x20, true)) {
209                         DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
210                                 server ));
211                         TALLOC_FREE(frame);
212                         return false;
213                 }
214                 print_sockaddr(addr, sizeof(addr), &ss);
215                 srv = talloc_strdup(frame, addr);
216         } else {
217                 /* this copes with inet_ntoa brokenness */
218                 srv = talloc_strdup(frame, server);
219         }
220
221         if (!srv) {
222                 TALLOC_FREE(frame);
223                 return false;
224         }
225
226         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
227                 srv, ads->server.realm));
228
229         ZERO_STRUCT( cldap_reply );
230
231         if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
232                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
233                 ret = false;
234                 goto out;
235         }
236
237         /* Check the CLDAP reply flags */
238
239         if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
240                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
241                         srv));
242                 ret = false;
243                 goto out;
244         }
245
246         /* Fill in the ads->config values */
247
248         SAFE_FREE(ads->config.realm);
249         SAFE_FREE(ads->config.bind_path);
250         SAFE_FREE(ads->config.ldap_server_name);
251         SAFE_FREE(ads->config.server_site_name);
252         SAFE_FREE(ads->config.client_site_name);
253         SAFE_FREE(ads->server.workgroup);
254
255         ads->config.flags              = cldap_reply.server_type;
256         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.pdc_dns_name);
257         ads->config.realm              = SMB_STRDUP(cldap_reply.dns_domain);
258         strupper_m(ads->config.realm);
259         ads->config.bind_path          = ads_build_dn(ads->config.realm);
260         if (*cldap_reply.server_site) {
261                 ads->config.server_site_name =
262                         SMB_STRDUP(cldap_reply.server_site);
263         }
264         if (*cldap_reply.client_site) {
265                 ads->config.client_site_name =
266                         SMB_STRDUP(cldap_reply.client_site);
267         }
268         ads->server.workgroup          = SMB_STRDUP(cldap_reply.domain);
269
270         ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
271         if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
272                 DEBUG(1,("ads_try_connect: unable to convert %s "
273                         "to an address\n",
274                         srv));
275                 ret = false;
276                 goto out;
277         }
278
279         /* Store our site name. */
280         sitename_store( cldap_reply.domain, cldap_reply.client_site);
281         sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
282
283         ret = true;
284
285  out:
286
287         TALLOC_FREE(frame);
288         return ret;
289 }
290
291 /**********************************************************************
292  Try to find an AD dc using our internal name resolution routines
293  Try the realm first and then then workgroup name if netbios is not 
294  disabled
295 **********************************************************************/
296
297 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
298 {
299         const char *c_domain;
300         const char *c_realm;
301         int count, i=0;
302         struct ip_service *ip_list;
303         const char *realm;
304         const char *domain;
305         bool got_realm = False;
306         bool use_own_domain = False;
307         char *sitename;
308         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
309
310         /* if the realm and workgroup are both empty, assume they are ours */
311
312         /* realm */
313         c_realm = ads->server.realm;
314
315         if ( !c_realm || !*c_realm ) {
316                 /* special case where no realm and no workgroup means our own */
317                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
318                         use_own_domain = True;
319                         c_realm = lp_realm();
320                 }
321         }
322
323         if (c_realm && *c_realm)
324                 got_realm = True;
325
326         /* we need to try once with the realm name and fallback to the
327            netbios domain name if we fail (if netbios has not been disabled */
328
329         if ( !got_realm && !lp_disable_netbios() ) {
330                 c_realm = ads->server.workgroup;
331                 if (!c_realm || !*c_realm) {
332                         if ( use_own_domain )
333                                 c_realm = lp_workgroup();
334                 }
335         }
336
337         if ( !c_realm || !*c_realm ) {
338                 DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
339                 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
340         }
341
342         if ( use_own_domain ) {
343                 c_domain = lp_workgroup();
344         } else {
345                 c_domain = ads->server.workgroup;
346         }
347
348         realm = c_realm;
349         domain = c_domain;
350
351         /*
352          * In case of LDAP we use get_dc_name() as that
353          * creates the custom krb5.conf file
354          */
355         if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
356                 fstring srv_name;
357                 struct sockaddr_storage ip_out;
358
359                 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
360                         (got_realm ? "realm" : "domain"), realm));
361
362                 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
363                         /*
364                          * we call ads_try_connect() to fill in the
365                          * ads->config details
366                          */
367                         if (ads_try_connect(ads, srv_name, false)) {
368                                 return NT_STATUS_OK;
369                         }
370                 }
371
372                 return NT_STATUS_NO_LOGON_SERVERS;
373         }
374
375         sitename = sitename_fetch(realm);
376
377  again:
378
379         DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
380                 (got_realm ? "realm" : "domain"), realm));
381
382         status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
383         if (!NT_STATUS_IS_OK(status)) {
384                 /* fall back to netbios if we can */
385                 if ( got_realm && !lp_disable_netbios() ) {
386                         got_realm = False;
387                         goto again;
388                 }
389
390                 SAFE_FREE(sitename);
391                 return status;
392         }
393
394         /* if we fail this loop, then giveup since all the IP addresses returned were dead */
395         for ( i=0; i<count; i++ ) {
396                 char server[INET6_ADDRSTRLEN];
397
398                 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
399
400                 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
401                         continue;
402
403                 if (!got_realm) {
404                         /* realm in this case is a workgroup name. We need
405                            to ignore any IP addresses in the negative connection
406                            cache that match ip addresses returned in the ad realm
407                            case. It sucks that I have to reproduce the logic above... */
408                         c_realm = ads->server.realm;
409                         if ( !c_realm || !*c_realm ) {
410                                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
411                                         c_realm = lp_realm();
412                                 }
413                         }
414                         if (c_realm && *c_realm &&
415                                         !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
416                                 /* Ensure we add the workgroup name for this
417                                    IP address as negative too. */
418                                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
419                                 continue;
420                         }
421                 }
422
423                 if ( ads_try_connect(ads, server, false) ) {
424                         SAFE_FREE(ip_list);
425                         SAFE_FREE(sitename);
426                         return NT_STATUS_OK;
427                 }
428
429                 /* keep track of failures */
430                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
431         }
432
433         SAFE_FREE(ip_list);
434
435         /* In case we failed to contact one of our closest DC on our site we
436          * need to try to find another DC, retry with a site-less SRV DNS query
437          * - Guenther */
438
439         if (sitename) {
440                 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
441                                 "trying to find another DC\n", sitename));
442                 SAFE_FREE(sitename);
443                 namecache_delete(realm, 0x1C);
444                 goto again;
445         }
446
447         return NT_STATUS_NO_LOGON_SERVERS;
448 }
449
450 /*********************************************************************
451  *********************************************************************/
452
453 static NTSTATUS ads_lookup_site(void)
454 {
455         ADS_STRUCT *ads = NULL;
456         ADS_STATUS ads_status;
457         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
458
459         ads = ads_init(lp_realm(), NULL, NULL);
460         if (!ads) {
461                 return NT_STATUS_NO_MEMORY;
462         }
463
464         /* The NO_BIND here will find a DC and set the client site
465            but not establish the TCP connection */
466
467         ads->auth.flags = ADS_AUTH_NO_BIND;
468         ads_status = ads_connect(ads);
469         if (!ADS_ERR_OK(ads_status)) {
470                 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
471                           ads_errstr(ads_status)));
472         }
473         nt_status = ads_ntstatus(ads_status);
474
475         if (ads) {
476                 ads_destroy(&ads);
477         }
478
479         return nt_status;
480 }
481
482 /*********************************************************************
483  *********************************************************************/
484
485 static const char* host_dns_domain(const char *fqdn)
486 {
487         const char *p = fqdn;
488
489         /* go to next char following '.' */
490
491         if ((p = strchr_m(fqdn, '.')) != NULL) {
492                 p++;
493         }
494
495         return p;
496 }
497
498
499 /**
500  * Connect to the Global Catalog server
501  * @param ads Pointer to an existing ADS_STRUCT
502  * @return status of connection
503  *
504  * Simple wrapper around ads_connect() that fills in the
505  * GC ldap server information
506  **/
507
508 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
509 {
510         TALLOC_CTX *frame = talloc_stackframe();
511         struct dns_rr_srv *gcs_list;
512         int num_gcs;
513         char *realm = ads->server.realm;
514         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
515         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
516         int i;
517         bool done = false;
518         char *sitename = NULL;
519
520         if (!realm)
521                 realm = lp_realm();
522
523         if ((sitename = sitename_fetch(realm)) == NULL) {
524                 ads_lookup_site();
525                 sitename = sitename_fetch(realm);
526         }
527
528         do {
529                 /* We try once with a sitename and once without
530                    (unless we don't have a sitename and then we're
531                    done */
532
533                 if (sitename == NULL)
534                         done = true;
535
536                 nt_status = ads_dns_query_gcs(frame, realm, sitename,
537                                               &gcs_list, &num_gcs);
538
539                 SAFE_FREE(sitename);
540
541                 if (!NT_STATUS_IS_OK(nt_status)) {
542                         ads_status = ADS_ERROR_NT(nt_status);
543                         goto done;
544                 }
545
546                 /* Loop until we get a successful connection or have gone
547                    through them all.  When connecting a GC server, make sure that
548                    the realm is the server's DNS name and not the forest root */
549
550                 for (i=0; i<num_gcs; i++) {
551                         ads->server.gc = true;
552                         ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
553                         ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
554                         ads_status = ads_connect(ads);
555                         if (ADS_ERR_OK(ads_status)) {
556                                 /* Reset the bind_dn to "".  A Global Catalog server
557                                    may host  multiple domain trees in a forest.
558                                    Windows 2003 GC server will accept "" as the search
559                                    path to imply search all domain trees in the forest */
560
561                                 SAFE_FREE(ads->config.bind_path);
562                                 ads->config.bind_path = SMB_STRDUP("");
563
564
565                                 goto done;
566                         }
567                         SAFE_FREE(ads->server.ldap_server);
568                         SAFE_FREE(ads->server.realm);
569                 }
570
571                 TALLOC_FREE(gcs_list);
572                 num_gcs = 0;
573         } while (!done);
574
575 done:
576         SAFE_FREE(sitename);
577         talloc_destroy(frame);
578
579         return ads_status;
580 }
581
582
583 /**
584  * Connect to the LDAP server
585  * @param ads Pointer to an existing ADS_STRUCT
586  * @return status of connection
587  **/
588 ADS_STATUS ads_connect(ADS_STRUCT *ads)
589 {
590         int version = LDAP_VERSION3;
591         ADS_STATUS status;
592         NTSTATUS ntstatus;
593         char addr[INET6_ADDRSTRLEN];
594
595         ZERO_STRUCT(ads->ldap);
596         ads->ldap.last_attempt  = time(NULL);
597         ads->ldap.wrap_type     = ADS_SASLWRAP_TYPE_PLAIN;
598
599         /* try with a user specified server */
600
601         if (DEBUGLEVEL >= 11) {
602                 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
603                 DEBUG(11,("ads_connect: entering\n"));
604                 DEBUGADD(11,("%s\n", s));
605                 TALLOC_FREE(s);
606         }
607
608         if (ads->server.ldap_server)
609         {
610                 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
611                         goto got_connection;
612                 }
613
614                 /* The choice of which GC use is handled one level up in
615                    ads_connect_gc().  If we continue on from here with
616                    ads_find_dc() we will get GC searches on port 389 which
617                    doesn't work.   --jerry */
618
619                 if (ads->server.gc == true) {
620                         return ADS_ERROR(LDAP_OPERATIONS_ERROR);
621                 }
622         }
623
624         ntstatus = ads_find_dc(ads);
625         if (NT_STATUS_IS_OK(ntstatus)) {
626                 goto got_connection;
627         }
628
629         status = ADS_ERROR_NT(ntstatus);
630         goto out;
631
632 got_connection:
633
634         print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
635         DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
636
637         if (!ads->auth.user_name) {
638                 /* Must use the userPrincipalName value here or sAMAccountName
639                    and not servicePrincipalName; found by Guenther Deschner */
640
641                 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
642                         DEBUG(0,("ads_connect: asprintf fail.\n"));
643                         ads->auth.user_name = NULL;
644                 }
645         }
646
647         if (!ads->auth.realm) {
648                 ads->auth.realm = SMB_STRDUP(ads->config.realm);
649         }
650
651         if (!ads->auth.kdc_server) {
652                 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
653                 ads->auth.kdc_server = SMB_STRDUP(addr);
654         }
655
656 #if KRB5_DNS_HACK
657         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
658            to MIT kerberos to work (tridge) */
659         {
660                 char *env = NULL;
661                 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
662                         setenv(env, ads->auth.kdc_server, 1);
663                         free(env);
664                 }
665         }
666 #endif
667
668         /* If the caller() requested no LDAP bind, then we are done */
669
670         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
671                 status = ADS_SUCCESS;
672                 goto out;
673         }
674
675         ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
676         if (!ads->ldap.mem_ctx) {
677                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
678                 goto out;
679         }
680
681         /* Otherwise setup the TCP LDAP session */
682
683         ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
684                                               ads->ldap.port, lp_ldap_timeout());
685         if (ads->ldap.ld == NULL) {
686                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
687                 goto out;
688         }
689         DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
690
691         /* cache the successful connection for workgroup and realm */
692         if (ads_closest_dc(ads)) {
693                 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
694                 saf_store( ads->server.realm, ads->config.ldap_server_name);
695         }
696
697         ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
698
699         if ( lp_ldap_ssl_ads() ) {
700                 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
701                 if (!ADS_ERR_OK(status)) {
702                         goto out;
703                 }
704         }
705
706         /* fill in the current time and offsets */
707
708         status = ads_current_time( ads );
709         if ( !ADS_ERR_OK(status) ) {
710                 goto out;
711         }
712
713         /* Now do the bind */
714
715         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
716                 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
717                 goto out;
718         }
719
720         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
721                 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
722                 goto out;
723         }
724
725         status = ads_sasl_bind(ads);
726
727  out:
728         if (DEBUGLEVEL >= 11) {
729                 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
730                 DEBUG(11,("ads_connect: leaving with: %s\n",
731                         ads_errstr(status)));
732                 DEBUGADD(11,("%s\n", s));
733                 TALLOC_FREE(s);
734         }
735
736         return status;
737 }
738
739 /**
740  * Connect to the LDAP server using given credentials
741  * @param ads Pointer to an existing ADS_STRUCT
742  * @return status of connection
743  **/
744 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
745 {
746         ads->auth.flags |= ADS_AUTH_USER_CREDS;
747
748         return ads_connect(ads);
749 }
750
751 /**
752  * Disconnect the LDAP server
753  * @param ads Pointer to an existing ADS_STRUCT
754  **/
755 void ads_disconnect(ADS_STRUCT *ads)
756 {
757         if (ads->ldap.ld) {
758                 ldap_unbind(ads->ldap.ld);
759                 ads->ldap.ld = NULL;
760         }
761         if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
762                 ads->ldap.wrap_ops->disconnect(ads);
763         }
764         if (ads->ldap.mem_ctx) {
765                 talloc_free(ads->ldap.mem_ctx);
766         }
767         ZERO_STRUCT(ads->ldap);
768 }
769
770 /*
771   Duplicate a struct berval into talloc'ed memory
772  */
773 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
774 {
775         struct berval *value;
776
777         if (!in_val) return NULL;
778
779         value = TALLOC_ZERO_P(ctx, struct berval);
780         if (value == NULL)
781                 return NULL;
782         if (in_val->bv_len == 0) return value;
783
784         value->bv_len = in_val->bv_len;
785         value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
786                                               in_val->bv_len);
787         return value;
788 }
789
790 /*
791   Make a values list out of an array of (struct berval *)
792  */
793 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
794                                       const struct berval **in_vals)
795 {
796         struct berval **values;
797         int i;
798
799         if (!in_vals) return NULL;
800         for (i=0; in_vals[i]; i++)
801                 ; /* count values */
802         values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
803         if (!values) return NULL;
804
805         for (i=0; in_vals[i]; i++) {
806                 values[i] = dup_berval(ctx, in_vals[i]);
807         }
808         return values;
809 }
810
811 /*
812   UTF8-encode a values list out of an array of (char *)
813  */
814 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
815 {
816         char **values;
817         int i;
818         size_t size;
819
820         if (!in_vals) return NULL;
821         for (i=0; in_vals[i]; i++)
822                 ; /* count values */
823         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
824         if (!values) return NULL;
825
826         for (i=0; in_vals[i]; i++) {
827                 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
828                         TALLOC_FREE(values);
829                         return NULL;
830                 }
831         }
832         return values;
833 }
834
835 /*
836   Pull a (char *) array out of a UTF8-encoded values list
837  */
838 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
839 {
840         char **values;
841         int i;
842         size_t converted_size;
843
844         if (!in_vals) return NULL;
845         for (i=0; in_vals[i]; i++)
846                 ; /* count values */
847         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
848         if (!values) return NULL;
849
850         for (i=0; in_vals[i]; i++) {
851                 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
852                                       &converted_size)) {
853                         DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
854                                  "%s", strerror(errno)));
855                 }
856         }
857         return values;
858 }
859
860 /**
861  * Do a search with paged results.  cookie must be null on the first
862  *  call, and then returned on each subsequent call.  It will be null
863  *  again when the entire search is complete 
864  * @param ads connection to ads server 
865  * @param bind_path Base dn for the search
866  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
867  * @param expr Search expression - specified in local charset
868  * @param attrs Attributes to retrieve - specified in utf8 or ascii
869  * @param res ** which will contain results - free res* with ads_msgfree()
870  * @param count Number of entries retrieved on this page
871  * @param cookie The paged results cookie to be returned on subsequent calls
872  * @return status of search
873  **/
874 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
875                                            const char *bind_path,
876                                            int scope, const char *expr,
877                                            const char **attrs, void *args,
878                                            LDAPMessage **res, 
879                                            int *count, struct berval **cookie)
880 {
881         int rc, i, version;
882         char *utf8_expr, *utf8_path, **search_attrs = NULL;
883         size_t converted_size;
884         LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
885         BerElement *cookie_be = NULL;
886         struct berval *cookie_bv= NULL;
887         BerElement *ext_be = NULL;
888         struct berval *ext_bv= NULL;
889
890         TALLOC_CTX *ctx;
891         ads_control *external_control = (ads_control *) args;
892
893         *res = NULL;
894
895         if (!(ctx = talloc_init("ads_do_paged_search_args")))
896                 return ADS_ERROR(LDAP_NO_MEMORY);
897
898         /* 0 means the conversion worked but the result was empty 
899            so we only fail if it's -1.  In any case, it always 
900            at least nulls out the dest */
901         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
902             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
903         {
904                 rc = LDAP_NO_MEMORY;
905                 goto done;
906         }
907
908         if (!attrs || !(*attrs))
909                 search_attrs = NULL;
910         else {
911                 /* This would be the utf8-encoded version...*/
912                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
913                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
914                         rc = LDAP_NO_MEMORY;
915                         goto done;
916                 }
917         }
918
919         /* Paged results only available on ldap v3 or later */
920         ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
921         if (version < LDAP_VERSION3) {
922                 rc =  LDAP_NOT_SUPPORTED;
923                 goto done;
924         }
925
926         cookie_be = ber_alloc_t(LBER_USE_DER);
927         if (*cookie) {
928                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
929                 ber_bvfree(*cookie); /* don't need it from last time */
930                 *cookie = NULL;
931         } else {
932                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
933         }
934         ber_flatten(cookie_be, &cookie_bv);
935         PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
936         PagedResults.ldctl_iscritical = (char) 1;
937         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
938         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
939
940         NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
941         NoReferrals.ldctl_iscritical = (char) 0;
942         NoReferrals.ldctl_value.bv_len = 0;
943         NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
944
945         if (external_control && 
946             (strequal(external_control->control, ADS_EXTENDED_DN_OID) || 
947              strequal(external_control->control, ADS_SD_FLAGS_OID))) {
948
949                 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
950                 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
951
952                 /* win2k does not accept a ldctl_value beeing passed in */
953
954                 if (external_control->val != 0) {
955
956                         if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
957                                 rc = LDAP_NO_MEMORY;
958                                 goto done;
959                         }
960
961                         if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
962                                 rc = LDAP_NO_MEMORY;
963                                 goto done;
964                         }
965                         if ((ber_flatten(ext_be, &ext_bv)) == -1) {
966                                 rc = LDAP_NO_MEMORY;
967                                 goto done;
968                         }
969
970                         ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
971                         ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
972
973                 } else {
974                         ExternalCtrl.ldctl_value.bv_len = 0;
975                         ExternalCtrl.ldctl_value.bv_val = NULL;
976                 }
977
978                 controls[0] = &NoReferrals;
979                 controls[1] = &PagedResults;
980                 controls[2] = &ExternalCtrl;
981                 controls[3] = NULL;
982
983         } else {
984                 controls[0] = &NoReferrals;
985                 controls[1] = &PagedResults;
986                 controls[2] = NULL;
987         }
988
989         /* we need to disable referrals as the openldap libs don't
990            handle them and paged results at the same time.  Using them
991            together results in the result record containing the server 
992            page control being removed from the result list (tridge/jmcd) 
993
994            leaving this in despite the control that says don't generate
995            referrals, in case the server doesn't support it (jmcd)
996         */
997         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
998
999         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, 
1000                                       search_attrs, 0, controls,
1001                                       NULL, LDAP_NO_LIMIT,
1002                                       (LDAPMessage **)res);
1003
1004         ber_free(cookie_be, 1);
1005         ber_bvfree(cookie_bv);
1006
1007         if (rc) {
1008                 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1009                          ldap_err2string(rc)));
1010                 goto done;
1011         }
1012
1013         rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1014                                         NULL, &rcontrols,  0);
1015
1016         if (!rcontrols) {
1017                 goto done;
1018         }
1019
1020         for (i=0; rcontrols[i]; i++) {
1021                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1022                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1023                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1024                                   &cookie_bv);
1025                         /* the berval is the cookie, but must be freed when
1026                            it is all done */
1027                         if (cookie_bv->bv_len) /* still more to do */
1028                                 *cookie=ber_bvdup(cookie_bv);
1029                         else
1030                                 *cookie=NULL;
1031                         ber_bvfree(cookie_bv);
1032                         ber_free(cookie_be, 1);
1033                         break;
1034                 }
1035         }
1036         ldap_controls_free(rcontrols);
1037
1038 done:
1039         talloc_destroy(ctx);
1040
1041         if (ext_be) {
1042                 ber_free(ext_be, 1);
1043         }
1044
1045         if (ext_bv) {
1046                 ber_bvfree(ext_bv);
1047         }
1048
1049         /* if/when we decide to utf8-encode attrs, take out this next line */
1050         TALLOC_FREE(search_attrs);
1051
1052         return ADS_ERROR(rc);
1053 }
1054
1055 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1056                                       int scope, const char *expr,
1057                                       const char **attrs, LDAPMessage **res, 
1058                                       int *count, struct berval **cookie)
1059 {
1060         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1061 }
1062
1063
1064 /**
1065  * Get all results for a search.  This uses ads_do_paged_search() to return 
1066  * all entries in a large search.
1067  * @param ads connection to ads server 
1068  * @param bind_path Base dn for the search
1069  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1070  * @param expr Search expression
1071  * @param attrs Attributes to retrieve
1072  * @param res ** which will contain results - free res* with ads_msgfree()
1073  * @return status of search
1074  **/
1075  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1076                                    int scope, const char *expr,
1077                                    const char **attrs, void *args,
1078                                    LDAPMessage **res)
1079 {
1080         struct berval *cookie = NULL;
1081         int count = 0;
1082         ADS_STATUS status;
1083
1084         *res = NULL;
1085         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1086                                      &count, &cookie);
1087
1088         if (!ADS_ERR_OK(status)) 
1089                 return status;
1090
1091 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1092         while (cookie) {
1093                 LDAPMessage *res2 = NULL;
1094                 ADS_STATUS status2;
1095                 LDAPMessage *msg, *next;
1096
1097                 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
1098                                               attrs, args, &res2, &count, &cookie);
1099
1100                 if (!ADS_ERR_OK(status2)) break;
1101
1102                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1103                    that this works on all ldap libs, but I have only tested with openldap */
1104                 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1105                         next = ads_next_message(ads, msg);
1106                         ldap_add_result_entry((LDAPMessage **)res, msg);
1107                 }
1108                 /* note that we do not free res2, as the memory is now
1109                    part of the main returned list */
1110         }
1111 #else
1112         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1113         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1114 #endif
1115
1116         return status;
1117 }
1118
1119  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1120                               int scope, const char *expr,
1121                               const char **attrs, LDAPMessage **res)
1122 {
1123         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1124 }
1125
1126  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1127                                        int scope, const char *expr,
1128                                        const char **attrs, uint32 sd_flags, 
1129                                        LDAPMessage **res)
1130 {
1131         ads_control args;
1132
1133         args.control = ADS_SD_FLAGS_OID;
1134         args.val = sd_flags;
1135         args.critical = True;
1136
1137         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1138 }
1139
1140
1141 /**
1142  * Run a function on all results for a search.  Uses ads_do_paged_search() and
1143  *  runs the function as each page is returned, using ads_process_results()
1144  * @param ads connection to ads server
1145  * @param bind_path Base dn for the search
1146  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1147  * @param expr Search expression - specified in local charset
1148  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1149  * @param fn Function which takes attr name, values list, and data_area
1150  * @param data_area Pointer which is passed to function on each call
1151  * @return status of search
1152  **/
1153 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1154                                 int scope, const char *expr, const char **attrs,
1155                                 bool (*fn)(ADS_STRUCT *, char *, void **, void *), 
1156                                 void *data_area)
1157 {
1158         struct berval *cookie = NULL;
1159         int count = 0;
1160         ADS_STATUS status;
1161         LDAPMessage *res;
1162
1163         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1164                                      &count, &cookie);
1165
1166         if (!ADS_ERR_OK(status)) return status;
1167
1168         ads_process_results(ads, res, fn, data_area);
1169         ads_msgfree(ads, res);
1170
1171         while (cookie) {
1172                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1173                                              &res, &count, &cookie);
1174
1175                 if (!ADS_ERR_OK(status)) break;
1176
1177                 ads_process_results(ads, res, fn, data_area);
1178                 ads_msgfree(ads, res);
1179         }
1180
1181         return status;
1182 }
1183
1184 /**
1185  * Do a search with a timeout.
1186  * @param ads connection to ads server
1187  * @param bind_path Base dn for the search
1188  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1189  * @param expr Search expression
1190  * @param attrs Attributes to retrieve
1191  * @param res ** which will contain results - free res* with ads_msgfree()
1192  * @return status of search
1193  **/
1194  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
1195                           const char *expr,
1196                           const char **attrs, LDAPMessage **res)
1197 {
1198         int rc;
1199         char *utf8_expr, *utf8_path, **search_attrs = NULL;
1200         size_t converted_size;
1201         TALLOC_CTX *ctx;
1202
1203         *res = NULL;
1204         if (!(ctx = talloc_init("ads_do_search"))) {
1205                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1206                 return ADS_ERROR(LDAP_NO_MEMORY);
1207         }
1208
1209         /* 0 means the conversion worked but the result was empty 
1210            so we only fail if it's negative.  In any case, it always 
1211            at least nulls out the dest */
1212         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1213             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1214         {
1215                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1216                 rc = LDAP_NO_MEMORY;
1217                 goto done;
1218         }
1219
1220         if (!attrs || !(*attrs))
1221                 search_attrs = NULL;
1222         else {
1223                 /* This would be the utf8-encoded version...*/
1224                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
1225                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1226                 {
1227                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1228                         rc = LDAP_NO_MEMORY;
1229                         goto done;
1230                 }
1231         }
1232
1233         /* see the note in ads_do_paged_search - we *must* disable referrals */
1234         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1235
1236         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1237                                       search_attrs, 0, NULL, NULL, 
1238                                       LDAP_NO_LIMIT,
1239                                       (LDAPMessage **)res);
1240
1241         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1242                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1243                 rc = 0;
1244         }
1245
1246  done:
1247         talloc_destroy(ctx);
1248         /* if/when we decide to utf8-encode attrs, take out this next line */
1249         TALLOC_FREE(search_attrs);
1250         return ADS_ERROR(rc);
1251 }
1252 /**
1253  * Do a general ADS search
1254  * @param ads connection to ads server
1255  * @param res ** which will contain results - free res* with ads_msgfree()
1256  * @param expr Search expression
1257  * @param attrs Attributes to retrieve
1258  * @return status of search
1259  **/
1260  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
1261                        const char *expr, const char **attrs)
1262 {
1263         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
1264                              expr, attrs, res);
1265 }
1266
1267 /**
1268  * Do a search on a specific DistinguishedName
1269  * @param ads connection to ads server
1270  * @param res ** which will contain results - free res* with ads_msgfree()
1271  * @param dn DistinguishName to search
1272  * @param attrs Attributes to retrieve
1273  * @return status of search
1274  **/
1275  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
1276                           const char *dn, const char **attrs)
1277 {
1278         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1279                              attrs, res);
1280 }
1281
1282 /**
1283  * Free up memory from a ads_search
1284  * @param ads connection to ads server
1285  * @param msg Search results to free
1286  **/
1287  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1288 {
1289         if (!msg) return;
1290         ldap_msgfree(msg);
1291 }
1292
1293 /**
1294  * Get a dn from search results
1295  * @param ads connection to ads server
1296  * @param msg Search result
1297  * @return dn string
1298  **/
1299  char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1300 {
1301         char *utf8_dn, *unix_dn;
1302         size_t converted_size;
1303
1304         utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1305
1306         if (!utf8_dn) {
1307                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1308                 return NULL;
1309         }
1310
1311         if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1312                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1313                         utf8_dn ));
1314                 return NULL;
1315         }
1316         ldap_memfree(utf8_dn);
1317         return unix_dn;
1318 }
1319
1320 /**
1321  * Get the parent from a dn
1322  * @param dn the dn to return the parent from
1323  * @return parent dn string
1324  **/
1325 char *ads_parent_dn(const char *dn)
1326 {
1327         char *p;
1328
1329         if (dn == NULL) {
1330                 return NULL;
1331         }
1332
1333         p = strchr(dn, ',');
1334
1335         if (p == NULL) {
1336                 return NULL;
1337         }
1338
1339         return p+1;
1340 }
1341
1342 /**
1343  * Find a machine account given a hostname
1344  * @param ads connection to ads server
1345  * @param res ** which will contain results - free res* with ads_msgfree()
1346  * @param host Hostname to search for
1347  * @return status of search
1348  **/
1349  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1350                                   const char *machine)
1351 {
1352         ADS_STATUS status;
1353         char *expr;
1354         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1355
1356         *res = NULL;
1357
1358         /* the easiest way to find a machine account anywhere in the tree
1359            is to look for hostname$ */
1360         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1361                 DEBUG(1, ("asprintf failed!\n"));
1362                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1363         }
1364
1365         status = ads_search(ads, res, expr, attrs);
1366         SAFE_FREE(expr);
1367         return status;
1368 }
1369
1370 /**
1371  * Initialize a list of mods to be used in a modify request
1372  * @param ctx An initialized TALLOC_CTX
1373  * @return allocated ADS_MODLIST
1374  **/
1375 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1376 {
1377 #define ADS_MODLIST_ALLOC_SIZE 10
1378         LDAPMod **mods;
1379
1380         if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1381                 /* -1 is safety to make sure we don't go over the end.
1382                    need to reset it to NULL before doing ldap modify */
1383                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1384
1385         return (ADS_MODLIST)mods;
1386 }
1387
1388
1389 /*
1390   add an attribute to the list, with values list already constructed
1391 */
1392 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1393                                   int mod_op, const char *name, 
1394                                   const void *_invals)
1395 {
1396         const void **invals = (const void **)_invals;
1397         int curmod;
1398         LDAPMod **modlist = (LDAPMod **) *mods;
1399         struct berval **ber_values = NULL;
1400         char **char_values = NULL;
1401
1402         if (!invals) {
1403                 mod_op = LDAP_MOD_DELETE;
1404         } else {
1405                 if (mod_op & LDAP_MOD_BVALUES)
1406                         ber_values = ads_dup_values(ctx, 
1407                                                 (const struct berval **)invals);
1408                 else
1409                         char_values = ads_push_strvals(ctx, 
1410                                                   (const char **) invals);
1411         }
1412
1413         /* find the first empty slot */
1414         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1415              curmod++);
1416         if (modlist[curmod] == (LDAPMod *) -1) {
1417                 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1418                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1419                         return ADS_ERROR(LDAP_NO_MEMORY);
1420                 memset(&modlist[curmod], 0, 
1421                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1422                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1423                 *mods = (ADS_MODLIST)modlist;
1424         }
1425
1426         if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1427                 return ADS_ERROR(LDAP_NO_MEMORY);
1428         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1429         if (mod_op & LDAP_MOD_BVALUES) {
1430                 modlist[curmod]->mod_bvalues = ber_values;
1431         } else if (mod_op & LDAP_MOD_DELETE) {
1432                 modlist[curmod]->mod_values = NULL;
1433         } else {
1434                 modlist[curmod]->mod_values = char_values;
1435         }
1436
1437         modlist[curmod]->mod_op = mod_op;
1438         return ADS_ERROR(LDAP_SUCCESS);
1439 }
1440
1441 /**
1442  * Add a single string value to a mod list
1443  * @param ctx An initialized TALLOC_CTX
1444  * @param mods An initialized ADS_MODLIST
1445  * @param name The attribute name to add
1446  * @param val The value to add - NULL means DELETE
1447  * @return ADS STATUS indicating success of add
1448  **/
1449 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1450                        const char *name, const char *val)
1451 {
1452         const char *values[2];
1453
1454         values[0] = val;
1455         values[1] = NULL;
1456
1457         if (!val)
1458                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1459         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1460 }
1461
1462 /**
1463  * Add an array of string values to a mod list
1464  * @param ctx An initialized TALLOC_CTX
1465  * @param mods An initialized ADS_MODLIST
1466  * @param name The attribute name to add
1467  * @param vals The array of string values to add - NULL means DELETE
1468  * @return ADS STATUS indicating success of add
1469  **/
1470 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1471                            const char *name, const char **vals)
1472 {
1473         if (!vals)
1474                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1475         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1476                                name, (const void **) vals);
1477 }
1478
1479 #if 0
1480 /**
1481  * Add a single ber-encoded value to a mod list
1482  * @param ctx An initialized TALLOC_CTX
1483  * @param mods An initialized ADS_MODLIST
1484  * @param name The attribute name to add
1485  * @param val The value to add - NULL means DELETE
1486  * @return ADS STATUS indicating success of add
1487  **/
1488 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1489                               const char *name, const struct berval *val)
1490 {
1491         const struct berval *values[2];
1492
1493         values[0] = val;
1494         values[1] = NULL;
1495         if (!val)
1496                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1497         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1498                                name, (const void **) values);
1499 }
1500 #endif
1501
1502 /**
1503  * Perform an ldap modify
1504  * @param ads connection to ads server
1505  * @param mod_dn DistinguishedName to modify
1506  * @param mods list of modifications to perform
1507  * @return status of modify
1508  **/
1509 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1510 {
1511         int ret,i;
1512         char *utf8_dn = NULL;
1513         size_t converted_size;
1514         /* 
1515            this control is needed to modify that contains a currently 
1516            non-existent attribute (but allowable for the object) to run
1517         */
1518         LDAPControl PermitModify = {
1519                 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1520                 {0, NULL},
1521                 (char) 1};
1522         LDAPControl *controls[2];
1523
1524         controls[0] = &PermitModify;
1525         controls[1] = NULL;
1526
1527         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1528                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1529         }
1530
1531         /* find the end of the list, marked by NULL or -1 */
1532         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1533         /* make sure the end of the list is NULL */
1534         mods[i] = NULL;
1535         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1536                                 (LDAPMod **) mods, controls, NULL);
1537         TALLOC_FREE(utf8_dn);
1538         return ADS_ERROR(ret);
1539 }
1540
1541 /**
1542  * Perform an ldap add
1543  * @param ads connection to ads server
1544  * @param new_dn DistinguishedName to add
1545  * @param mods list of attributes and values for DN
1546  * @return status of add
1547  **/
1548 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1549 {
1550         int ret, i;
1551         char *utf8_dn = NULL;
1552         size_t converted_size;
1553
1554         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1555                 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1556                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1557         }
1558
1559         /* find the end of the list, marked by NULL or -1 */
1560         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1561         /* make sure the end of the list is NULL */
1562         mods[i] = NULL;
1563
1564         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1565         TALLOC_FREE(utf8_dn);
1566         return ADS_ERROR(ret);
1567 }
1568
1569 /**
1570  * Delete a DistinguishedName
1571  * @param ads connection to ads server
1572  * @param new_dn DistinguishedName to delete
1573  * @return status of delete
1574  **/
1575 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1576 {
1577         int ret;
1578         char *utf8_dn = NULL;
1579         size_t converted_size;
1580         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1581                 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1582                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1583         }
1584
1585         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1586         TALLOC_FREE(utf8_dn);
1587         return ADS_ERROR(ret);
1588 }
1589
1590 /**
1591  * Build an org unit string
1592  *  if org unit is Computers or blank then assume a container, otherwise
1593  *  assume a / separated list of organisational units.
1594  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1595  * @param ads connection to ads server
1596  * @param org_unit Organizational unit
1597  * @return org unit string - caller must free
1598  **/
1599 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1600 {
1601         char *ret = NULL;
1602
1603         if (!org_unit || !*org_unit) {
1604
1605                 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1606
1607                 /* samba4 might not yet respond to a wellknownobject-query */
1608                 return ret ? ret : SMB_STRDUP("cn=Computers");
1609         }
1610
1611         if (strequal(org_unit, "Computers")) {
1612                 return SMB_STRDUP("cn=Computers");
1613         }
1614
1615         /* jmcd: removed "\\" from the separation chars, because it is
1616            needed as an escape for chars like '#' which are valid in an
1617            OU name */
1618         return ads_build_path(org_unit, "/", "ou=", 1);
1619 }
1620
1621 /**
1622  * Get a org unit string for a well-known GUID
1623  * @param ads connection to ads server
1624  * @param wknguid Well known GUID
1625  * @return org unit string - caller must free
1626  **/
1627 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1628 {
1629         ADS_STATUS status;
1630         LDAPMessage *res = NULL;
1631         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1632                 **bind_dn_exp = NULL;
1633         const char *attrs[] = {"distinguishedName", NULL};
1634         int new_ln, wkn_ln, bind_ln, i;
1635
1636         if (wknguid == NULL) {
1637                 return NULL;
1638         }
1639
1640         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1641                 DEBUG(1, ("asprintf failed!\n"));
1642                 return NULL;
1643         }
1644
1645         status = ads_search_dn(ads, &res, base, attrs);
1646         if (!ADS_ERR_OK(status)) {
1647                 DEBUG(1,("Failed while searching for: %s\n", base));
1648                 goto out;
1649         }
1650
1651         if (ads_count_replies(ads, res) != 1) {
1652                 goto out;
1653         }
1654
1655         /* substitute the bind-path from the well-known-guid-search result */
1656         wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1657         if (!wkn_dn) {
1658                 goto out;
1659         }
1660
1661         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1662         if (!wkn_dn_exp) {
1663                 goto out;
1664         }
1665
1666         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1667         if (!bind_dn_exp) {
1668                 goto out;
1669         }
1670
1671         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1672                 ;
1673         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1674                 ;
1675
1676         new_ln = wkn_ln - bind_ln;
1677
1678         ret = SMB_STRDUP(wkn_dn_exp[0]);
1679         if (!ret) {
1680                 goto out;
1681         }
1682
1683         for (i=1; i < new_ln; i++) {
1684                 char *s = NULL;
1685
1686                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1687                         SAFE_FREE(ret);
1688                         goto out;
1689                 }
1690
1691                 SAFE_FREE(ret);
1692                 ret = SMB_STRDUP(s);
1693                 free(s);
1694                 if (!ret) {
1695                         goto out;
1696                 }
1697         }
1698
1699  out:
1700         SAFE_FREE(base);
1701         ads_msgfree(ads, res);
1702         TALLOC_FREE(wkn_dn);
1703         if (wkn_dn_exp) {
1704                 ldap_value_free(wkn_dn_exp);
1705         }
1706         if (bind_dn_exp) {
1707                 ldap_value_free(bind_dn_exp);
1708         }
1709
1710         return ret;
1711 }
1712
1713 /**
1714  * Adds (appends) an item to an attribute array, rather then
1715  * replacing the whole list
1716  * @param ctx An initialized TALLOC_CTX
1717  * @param mods An initialized ADS_MODLIST
1718  * @param name name of the ldap attribute to append to
1719  * @param vals an array of values to add
1720  * @return status of addition
1721  **/
1722
1723 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1724                                 const char *name, const char **vals)
1725 {
1726         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1727                                (const void *) vals);
1728 }
1729
1730 /**
1731  * Determines the an account's current KVNO via an LDAP lookup
1732  * @param ads An initialized ADS_STRUCT
1733  * @param account_name the NT samaccountname.
1734  * @return the kvno for the account, or -1 in case of a failure.
1735  **/
1736
1737 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1738 {
1739         LDAPMessage *res = NULL;
1740         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1741         char *filter;
1742         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1743         char *dn_string = NULL;
1744         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1745
1746         DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1747         if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1748                 return kvno;
1749         }
1750         ret = ads_search(ads, &res, filter, attrs);
1751         SAFE_FREE(filter);
1752         if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1753                 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1754                 ads_msgfree(ads, res);
1755                 return kvno;
1756         }
1757
1758         dn_string = ads_get_dn(ads, talloc_tos(), res);
1759         if (!dn_string) {
1760                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1761                 ads_msgfree(ads, res);
1762                 return kvno;
1763         }
1764         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1765         TALLOC_FREE(dn_string);
1766
1767         /* ---------------------------------------------------------
1768          * 0 is returned as a default KVNO from this point on...
1769          * This is done because Windows 2000 does not support key
1770          * version numbers.  Chances are that a failure in the next
1771          * step is simply due to Windows 2000 being used for a
1772          * domain controller. */
1773         kvno = 0;
1774
1775         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1776                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1777                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1778                 ads_msgfree(ads, res);
1779                 return kvno;
1780         }
1781
1782         /* Success */
1783         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1784         ads_msgfree(ads, res);
1785         return kvno;
1786 }
1787
1788 /**
1789  * Determines the computer account's current KVNO via an LDAP lookup
1790  * @param ads An initialized ADS_STRUCT
1791  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1792  * @return the kvno for the computer account, or -1 in case of a failure.
1793  **/
1794
1795 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1796 {
1797         char *computer_account = NULL;
1798         uint32_t kvno = -1;
1799
1800         if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1801                 return kvno;
1802         }
1803
1804         kvno = ads_get_kvno(ads, computer_account);
1805         free(computer_account);
1806
1807         return kvno;
1808 }
1809
1810 /**
1811  * This clears out all registered spn's for a given hostname
1812  * @param ads An initilaized ADS_STRUCT
1813  * @param machine_name the NetBIOS name of the computer.
1814  * @return 0 upon success, non-zero otherwise.
1815  **/
1816
1817 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1818 {
1819         TALLOC_CTX *ctx;
1820         LDAPMessage *res = NULL;
1821         ADS_MODLIST mods;
1822         const char *servicePrincipalName[1] = {NULL};
1823         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1824         char *dn_string = NULL;
1825
1826         ret = ads_find_machine_acct(ads, &res, machine_name);
1827         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1828                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1829                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1830                 ads_msgfree(ads, res);
1831                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1832         }
1833
1834         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1835         ctx = talloc_init("ads_clear_service_principal_names");
1836         if (!ctx) {
1837                 ads_msgfree(ads, res);
1838                 return ADS_ERROR(LDAP_NO_MEMORY);
1839         }
1840
1841         if (!(mods = ads_init_mods(ctx))) {
1842                 talloc_destroy(ctx);
1843                 ads_msgfree(ads, res);
1844                 return ADS_ERROR(LDAP_NO_MEMORY);
1845         }
1846         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1847         if (!ADS_ERR_OK(ret)) {
1848                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1849                 ads_msgfree(ads, res);
1850                 talloc_destroy(ctx);
1851                 return ret;
1852         }
1853         dn_string = ads_get_dn(ads, talloc_tos(), res);
1854         if (!dn_string) {
1855                 talloc_destroy(ctx);
1856                 ads_msgfree(ads, res);
1857                 return ADS_ERROR(LDAP_NO_MEMORY);
1858         }
1859         ret = ads_gen_mod(ads, dn_string, mods);
1860         TALLOC_FREE(dn_string);
1861         if (!ADS_ERR_OK(ret)) {
1862                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1863                         machine_name));
1864                 ads_msgfree(ads, res);
1865                 talloc_destroy(ctx);
1866                 return ret;
1867         }
1868
1869         ads_msgfree(ads, res);
1870         talloc_destroy(ctx);
1871         return ret;
1872 }
1873
1874 /**
1875  * This adds a service principal name to an existing computer account
1876  * (found by hostname) in AD.
1877  * @param ads An initialized ADS_STRUCT
1878  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1879  * @param my_fqdn The fully qualified DNS name of the machine
1880  * @param spn A string of the service principal to add, i.e. 'host'
1881  * @return 0 upon sucess, or non-zero if a failure occurs
1882  **/
1883
1884 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1885                                           const char *my_fqdn, const char *spn)
1886 {
1887         ADS_STATUS ret;
1888         TALLOC_CTX *ctx;
1889         LDAPMessage *res = NULL;
1890         char *psp1, *psp2;
1891         ADS_MODLIST mods;
1892         char *dn_string = NULL;
1893         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1894
1895         ret = ads_find_machine_acct(ads, &res, machine_name);
1896         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1897                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1898                         machine_name));
1899                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1900                         spn, machine_name, ads->config.realm));
1901                 ads_msgfree(ads, res);
1902                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1903         }
1904
1905         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1906         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1907                 ads_msgfree(ads, res);
1908                 return ADS_ERROR(LDAP_NO_MEMORY);
1909         }
1910
1911         /* add short name spn */
1912
1913         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1914                 talloc_destroy(ctx);
1915                 ads_msgfree(ads, res);
1916                 return ADS_ERROR(LDAP_NO_MEMORY);
1917         }
1918         strupper_m(psp1);
1919         strlower_m(&psp1[strlen(spn)]);
1920         servicePrincipalName[0] = psp1;
1921
1922         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1923                 psp1, machine_name));
1924
1925
1926         /* add fully qualified spn */
1927
1928         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1929                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1930                 goto out;
1931         }
1932         strupper_m(psp2);
1933         strlower_m(&psp2[strlen(spn)]);
1934         servicePrincipalName[1] = psp2;
1935
1936         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1937                 psp2, machine_name));
1938
1939         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1940                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1941                 goto out;
1942         }
1943
1944         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1945         if (!ADS_ERR_OK(ret)) {
1946                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1947                 goto out;
1948         }
1949
1950         if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1951                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1952                 goto out;
1953         }
1954
1955         ret = ads_gen_mod(ads, dn_string, mods);
1956         if (!ADS_ERR_OK(ret)) {
1957                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1958                 goto out;
1959         }
1960
1961  out:
1962         TALLOC_FREE( ctx );
1963         ads_msgfree(ads, res);
1964         return ret;
1965 }
1966
1967 /**
1968  * adds a machine account to the ADS server
1969  * @param ads An intialized ADS_STRUCT
1970  * @param machine_name - the NetBIOS machine name of this account.
1971  * @param account_type A number indicating the type of account to create
1972  * @param org_unit The LDAP path in which to place this account
1973  * @return 0 upon success, or non-zero otherwise
1974 **/
1975
1976 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1977                                    const char *org_unit)
1978 {
1979         ADS_STATUS ret;
1980         char *samAccountName, *controlstr;
1981         TALLOC_CTX *ctx;
1982         ADS_MODLIST mods;
1983         char *machine_escaped = NULL;
1984         char *new_dn;
1985         const char *objectClass[] = {"top", "person", "organizationalPerson",
1986                                      "user", "computer", NULL};
1987         LDAPMessage *res = NULL;
1988         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1989                                 UF_DONT_EXPIRE_PASSWD |\
1990                                 UF_ACCOUNTDISABLE );
1991
1992         if (!(ctx = talloc_init("ads_add_machine_acct")))
1993                 return ADS_ERROR(LDAP_NO_MEMORY);
1994
1995         ret = ADS_ERROR(LDAP_NO_MEMORY);
1996
1997         machine_escaped = escape_rdn_val_string_alloc(machine_name);
1998         if (!machine_escaped) {
1999                 goto done;
2000         }
2001
2002         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2003         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2004
2005         if ( !new_dn || !samAccountName ) {
2006                 goto done;
2007         }
2008
2009 #ifndef ENCTYPE_ARCFOUR_HMAC
2010         acct_control |= UF_USE_DES_KEY_ONLY;
2011 #endif
2012
2013         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2014                 goto done;
2015         }
2016
2017         if (!(mods = ads_init_mods(ctx))) {
2018                 goto done;
2019         }
2020
2021         ads_mod_str(ctx, &mods, "cn", machine_name);
2022         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2023         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2024         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2025
2026         ret = ads_gen_add(ads, new_dn, mods);
2027
2028 done:
2029         SAFE_FREE(machine_escaped);
2030         ads_msgfree(ads, res);
2031         talloc_destroy(ctx);
2032
2033         return ret;
2034 }
2035
2036 /**
2037  * move a machine account to another OU on the ADS server
2038  * @param ads - An intialized ADS_STRUCT
2039  * @param machine_name - the NetBIOS machine name of this account.
2040  * @param org_unit - The LDAP path in which to place this account
2041  * @param moved - whether we moved the machine account (optional)
2042  * @return 0 upon success, or non-zero otherwise
2043 **/
2044
2045 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
2046                                  const char *org_unit, bool *moved)
2047 {
2048         ADS_STATUS rc;
2049         int ldap_status;
2050         LDAPMessage *res = NULL;
2051         char *filter = NULL;
2052         char *computer_dn = NULL;
2053         char *parent_dn;
2054         char *computer_rdn = NULL;
2055         bool need_move = False;
2056
2057         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2058                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2059                 goto done;
2060         }
2061
2062         /* Find pre-existing machine */
2063         rc = ads_search(ads, &res, filter, NULL);
2064         if (!ADS_ERR_OK(rc)) {
2065                 goto done;
2066         }
2067
2068         computer_dn = ads_get_dn(ads, talloc_tos(), res);
2069         if (!computer_dn) {
2070                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2071                 goto done;
2072         }
2073
2074         parent_dn = ads_parent_dn(computer_dn);
2075         if (strequal(parent_dn, org_unit)) {
2076                 goto done;
2077         }
2078
2079         need_move = True;
2080
2081         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2082                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2083                 goto done;
2084         }
2085
2086         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
2087                                     org_unit, 1, NULL, NULL);
2088         rc = ADS_ERROR(ldap_status);
2089
2090 done:
2091         ads_msgfree(ads, res);
2092         SAFE_FREE(filter);
2093         TALLOC_FREE(computer_dn);
2094         SAFE_FREE(computer_rdn);
2095
2096         if (!ADS_ERR_OK(rc)) {
2097                 need_move = False;
2098         }
2099
2100         if (moved) {
2101                 *moved = need_move;
2102         }
2103
2104         return rc;
2105 }
2106
2107 /*
2108   dump a binary result from ldap
2109 */
2110 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2111 {
2112         int i, j;
2113         for (i=0; values[i]; i++) {
2114                 printf("%s: ", field);
2115                 for (j=0; j<values[i]->bv_len; j++) {
2116                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
2117                 }
2118                 printf("\n");
2119         }
2120 }
2121
2122 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2123 {
2124         int i;
2125         for (i=0; values[i]; i++) {
2126
2127                 UUID_FLAT guid;
2128                 struct GUID tmp;
2129
2130                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2131                 smb_uuid_unpack(guid, &tmp);
2132                 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2133         }
2134 }
2135
2136 /*
2137   dump a sid result from ldap
2138 */
2139 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2140 {
2141         int i;
2142         for (i=0; values[i]; i++) {
2143                 DOM_SID sid;
2144                 fstring tmp;
2145                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2146                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2147         }
2148 }
2149
2150 /*
2151   dump ntSecurityDescriptor
2152 */
2153 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2154 {
2155         TALLOC_CTX *frame = talloc_stackframe();
2156         struct security_descriptor *psd;
2157         NTSTATUS status;
2158
2159         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2160                                      values[0]->bv_len, &psd);
2161         if (!NT_STATUS_IS_OK(status)) {
2162                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2163                           nt_errstr(status)));
2164                 TALLOC_FREE(frame);
2165                 return;
2166         }
2167
2168         if (psd) {
2169                 ads_disp_sd(ads, talloc_tos(), psd);
2170         }
2171
2172         TALLOC_FREE(frame);
2173 }
2174
2175 /*
2176   dump a string result from ldap
2177 */
2178 static void dump_string(const char *field, char **values)
2179 {
2180         int i;
2181         for (i=0; values[i]; i++) {
2182                 printf("%s: %s\n", field, values[i]);
2183         }
2184 }
2185
2186 /*
2187   dump a field from LDAP on stdout
2188   used for debugging
2189 */
2190
2191 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2192 {
2193         const struct {
2194                 const char *name;
2195                 bool string;
2196                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2197         } handlers[] = {
2198                 {"objectGUID", False, dump_guid},
2199                 {"netbootGUID", False, dump_guid},
2200                 {"nTSecurityDescriptor", False, dump_sd},
2201                 {"dnsRecord", False, dump_binary},
2202                 {"objectSid", False, dump_sid},
2203                 {"tokenGroups", False, dump_sid},
2204                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2205                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2206                 {"mS-DS-CreatorSID", False, dump_sid},
2207                 {"msExchMailboxGuid", False, dump_guid},
2208                 {NULL, True, NULL}
2209         };
2210         int i;
2211
2212         if (!field) { /* must be end of an entry */
2213                 printf("\n");
2214                 return False;
2215         }
2216
2217         for (i=0; handlers[i].name; i++) {
2218                 if (StrCaseCmp(handlers[i].name, field) == 0) {
2219                         if (!values) /* first time, indicate string or not */
2220                                 return handlers[i].string;
2221                         handlers[i].handler(ads, field, (struct berval **) values);
2222                         break;
2223                 }
2224         }
2225         if (!handlers[i].name) {
2226                 if (!values) /* first time, indicate string conversion */
2227                         return True;
2228                 dump_string(field, (char **)values);
2229         }
2230         return False;
2231 }
2232
2233 /**
2234  * Dump a result from LDAP on stdout
2235  *  used for debugging
2236  * @param ads connection to ads server
2237  * @param res Results to dump
2238  **/
2239
2240  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2241 {
2242         ads_process_results(ads, res, ads_dump_field, NULL);
2243 }
2244
2245 /**
2246  * Walk through results, calling a function for each entry found.
2247  *  The function receives a field name, a berval * array of values,
2248  *  and a data area passed through from the start.  The function is
2249  *  called once with null for field and values at the end of each
2250  *  entry.
2251  * @param ads connection to ads server
2252  * @param res Results to process
2253  * @param fn Function for processing each result
2254  * @param data_area user-defined area to pass to function
2255  **/
2256  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2257                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2258                           void *data_area)
2259 {
2260         LDAPMessage *msg;
2261         TALLOC_CTX *ctx;
2262         size_t converted_size;
2263
2264         if (!(ctx = talloc_init("ads_process_results")))
2265                 return;
2266
2267         for (msg = ads_first_entry(ads, res); msg; 
2268              msg = ads_next_entry(ads, msg)) {
2269                 char *utf8_field;
2270                 BerElement *b;
2271
2272                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2273                                                      (LDAPMessage *)msg,&b); 
2274                      utf8_field;
2275                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2276                                                     (LDAPMessage *)msg,b)) {
2277                         struct berval **ber_vals;
2278                         char **str_vals, **utf8_vals;
2279                         char *field;
2280                         bool string; 
2281
2282                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2283                                               &converted_size))
2284                         {
2285                                 DEBUG(0,("ads_process_results: "
2286                                          "pull_utf8_talloc failed: %s",
2287                                          strerror(errno)));
2288                         }
2289
2290                         string = fn(ads, field, NULL, data_area);
2291
2292                         if (string) {
2293                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2294                                                  (LDAPMessage *)msg, field);
2295                                 str_vals = ads_pull_strvals(ctx, 
2296                                                   (const char **) utf8_vals);
2297                                 fn(ads, field, (void **) str_vals, data_area);
2298                                 ldap_value_free(utf8_vals);
2299                         } else {
2300                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2301                                                  (LDAPMessage *)msg, field);
2302                                 fn(ads, field, (void **) ber_vals, data_area);
2303
2304                                 ldap_value_free_len(ber_vals);
2305                         }
2306                         ldap_memfree(utf8_field);
2307                 }
2308                 ber_free(b, 0);
2309                 talloc_free_children(ctx);
2310                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2311
2312         }
2313         talloc_destroy(ctx);
2314 }
2315
2316 /**
2317  * count how many replies are in a LDAPMessage
2318  * @param ads connection to ads server
2319  * @param res Results to count
2320  * @return number of replies
2321  **/
2322 int ads_count_replies(ADS_STRUCT *ads, void *res)
2323 {
2324         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2325 }
2326
2327 /**
2328  * pull the first entry from a ADS result
2329  * @param ads connection to ads server
2330  * @param res Results of search
2331  * @return first entry from result
2332  **/
2333  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2334 {
2335         return ldap_first_entry(ads->ldap.ld, res);
2336 }
2337
2338 /**
2339  * pull the next entry from a ADS result
2340  * @param ads connection to ads server
2341  * @param res Results of search
2342  * @return next entry from result
2343  **/
2344  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2345 {
2346         return ldap_next_entry(ads->ldap.ld, res);
2347 }
2348
2349 /**
2350  * pull the first message from a ADS result
2351  * @param ads connection to ads server
2352  * @param res Results of search
2353  * @return first message from result
2354  **/
2355  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2356 {
2357         return ldap_first_message(ads->ldap.ld, res);
2358 }
2359
2360 /**
2361  * pull the next message from a ADS result
2362  * @param ads connection to ads server
2363  * @param res Results of search
2364  * @return next message from result
2365  **/
2366  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2367 {
2368         return ldap_next_message(ads->ldap.ld, res);
2369 }
2370
2371 /**
2372  * pull a single string from a ADS result
2373  * @param ads connection to ads server
2374  * @param mem_ctx TALLOC_CTX to use for allocating result string
2375  * @param msg Results of search
2376  * @param field Attribute to retrieve
2377  * @return Result string in talloc context
2378  **/
2379  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2380                        const char *field)
2381 {
2382         char **values;
2383         char *ret = NULL;
2384         char *ux_string;
2385         size_t converted_size;
2386
2387         values = ldap_get_values(ads->ldap.ld, msg, field);
2388         if (!values)
2389                 return NULL;
2390
2391         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2392                                           &converted_size))
2393         {
2394                 ret = ux_string;
2395         }
2396         ldap_value_free(values);
2397         return ret;
2398 }
2399
2400 /**
2401  * pull an array of strings from a ADS result
2402  * @param ads connection to ads server
2403  * @param mem_ctx TALLOC_CTX to use for allocating result string
2404  * @param msg Results of search
2405  * @param field Attribute to retrieve
2406  * @return Result strings in talloc context
2407  **/
2408  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2409                          LDAPMessage *msg, const char *field,
2410                          size_t *num_values)
2411 {
2412         char **values;
2413         char **ret = NULL;
2414         int i;
2415         size_t converted_size;
2416
2417         values = ldap_get_values(ads->ldap.ld, msg, field);
2418         if (!values)
2419                 return NULL;
2420
2421         *num_values = ldap_count_values(values);
2422
2423         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2424         if (!ret) {
2425                 ldap_value_free(values);
2426                 return NULL;
2427         }
2428
2429         for (i=0;i<*num_values;i++) {
2430                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2431                                       &converted_size))
2432                 {
2433                         ldap_value_free(values);
2434                         return NULL;
2435                 }
2436         }
2437         ret[i] = NULL;
2438
2439         ldap_value_free(values);
2440         return ret;
2441 }
2442
2443 /**
2444  * pull an array of strings from a ADS result 
2445  *  (handle large multivalue attributes with range retrieval)
2446  * @param ads connection to ads server
2447  * @param mem_ctx TALLOC_CTX to use for allocating result string
2448  * @param msg Results of search
2449  * @param field Attribute to retrieve
2450  * @param current_strings strings returned by a previous call to this function
2451  * @param next_attribute The next query should ask for this attribute
2452  * @param num_values How many values did we get this time?
2453  * @param more_values Are there more values to get?
2454  * @return Result strings in talloc context
2455  **/
2456  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2457                                TALLOC_CTX *mem_ctx,
2458                                LDAPMessage *msg, const char *field,
2459                                char **current_strings,
2460                                const char **next_attribute,
2461                                size_t *num_strings,
2462                                bool *more_strings)
2463 {
2464         char *attr;
2465         char *expected_range_attrib, *range_attr;
2466         BerElement *ptr = NULL;
2467         char **strings;
2468         char **new_strings;
2469         size_t num_new_strings;
2470         unsigned long int range_start;
2471         unsigned long int range_end;
2472
2473         /* we might have been given the whole lot anyway */
2474         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2475                 *more_strings = False;
2476                 return strings;
2477         }
2478
2479         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2480
2481         /* look for Range result */
2482         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2483              attr; 
2484              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2485                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2486                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2487                         range_attr = attr;
2488                         break;
2489                 }
2490                 ldap_memfree(attr);
2491         }
2492         if (!attr) {
2493                 ber_free(ptr, 0);
2494                 /* nothing here - this field is just empty */
2495                 *more_strings = False;
2496                 return NULL;
2497         }
2498
2499         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2500                    &range_start, &range_end) == 2) {
2501                 *more_strings = True;
2502         } else {
2503                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2504                            &range_start) == 1) {
2505                         *more_strings = False;
2506                 } else {
2507                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2508                                   range_attr));
2509                         ldap_memfree(range_attr);
2510                         *more_strings = False;
2511                         return NULL;
2512                 }
2513         }
2514
2515         if ((*num_strings) != range_start) {
2516                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2517                           " - aborting range retreival\n",
2518                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2519                 ldap_memfree(range_attr);
2520                 *more_strings = False;
2521                 return NULL;
2522         }
2523
2524         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2525
2526         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2527                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2528                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2529                           range_attr, (unsigned long int)range_end - range_start + 1, 
2530                           (unsigned long int)num_new_strings));
2531                 ldap_memfree(range_attr);
2532                 *more_strings = False;
2533                 return NULL;
2534         }
2535
2536         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2537                                  *num_strings + num_new_strings);
2538
2539         if (strings == NULL) {
2540                 ldap_memfree(range_attr);
2541                 *more_strings = False;
2542                 return NULL;
2543         }
2544
2545         if (new_strings && num_new_strings) {
2546                 memcpy(&strings[*num_strings], new_strings,
2547                        sizeof(*new_strings) * num_new_strings);
2548         }
2549
2550         (*num_strings) += num_new_strings;
2551
2552         if (*more_strings) {
2553                 *next_attribute = talloc_asprintf(mem_ctx,
2554                                                   "%s;range=%d-*", 
2555                                                   field,
2556                                                   (int)*num_strings);
2557
2558                 if (!*next_attribute) {
2559                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2560                         ldap_memfree(range_attr);
2561                         *more_strings = False;
2562                         return NULL;
2563                 }
2564         }
2565
2566         ldap_memfree(range_attr);
2567
2568         return strings;
2569 }
2570
2571 /**
2572  * pull a single uint32 from a ADS result
2573  * @param ads connection to ads server
2574  * @param msg Results of search
2575  * @param field Attribute to retrieve
2576  * @param v Pointer to int to store result
2577  * @return boolean inidicating success
2578 */
2579  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2580                       uint32 *v)
2581 {
2582         char **values;
2583
2584         values = ldap_get_values(ads->ldap.ld, msg, field);
2585         if (!values)
2586                 return False;
2587         if (!values[0]) {
2588                 ldap_value_free(values);
2589                 return False;
2590         }
2591
2592         *v = atoi(values[0]);
2593         ldap_value_free(values);
2594         return True;
2595 }
2596
2597 /**
2598  * pull a single objectGUID from an ADS result
2599  * @param ads connection to ADS server
2600  * @param msg results of search
2601  * @param guid 37-byte area to receive text guid
2602  * @return boolean indicating success
2603  **/
2604  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2605 {
2606         char **values;
2607         UUID_FLAT flat_guid;
2608
2609         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2610         if (!values)
2611                 return False;
2612
2613         if (values[0]) {
2614                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2615                 smb_uuid_unpack(flat_guid, guid);
2616                 ldap_value_free(values);
2617                 return True;
2618         }
2619         ldap_value_free(values);
2620         return False;
2621
2622 }
2623
2624
2625 /**
2626  * pull a single DOM_SID from a ADS result
2627  * @param ads connection to ads server
2628  * @param msg Results of search
2629  * @param field Attribute to retrieve
2630  * @param sid Pointer to sid to store result
2631  * @return boolean inidicating success
2632 */
2633  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2634                    DOM_SID *sid)
2635 {
2636         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2637 }
2638
2639 /**
2640  * pull an array of DOM_SIDs from a ADS result
2641  * @param ads connection to ads server
2642  * @param mem_ctx TALLOC_CTX for allocating sid array
2643  * @param msg Results of search
2644  * @param field Attribute to retrieve
2645  * @param sids pointer to sid array to allocate
2646  * @return the count of SIDs pulled
2647  **/
2648  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2649                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2650 {
2651         struct berval **values;
2652         bool ret;
2653         int count, i;
2654
2655         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2656
2657         if (!values)
2658                 return 0;
2659
2660         for (i=0; values[i]; i++)
2661                 /* nop */ ;
2662
2663         if (i) {
2664                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2665                 if (!(*sids)) {
2666                         ldap_value_free_len(values);
2667                         return 0;
2668                 }
2669         } else {
2670                 (*sids) = NULL;
2671         }
2672
2673         count = 0;
2674         for (i=0; values[i]; i++) {
2675                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2676                 if (ret) {
2677                         DEBUG(10, ("pulling SID: %s\n",
2678                                    sid_string_dbg(&(*sids)[count])));
2679                         count++;
2680                 }
2681         }
2682
2683         ldap_value_free_len(values);
2684         return count;
2685 }
2686
2687 /**
2688  * pull a SEC_DESC from a ADS result
2689  * @param ads connection to ads server
2690  * @param mem_ctx TALLOC_CTX for allocating sid array
2691  * @param msg Results of search
2692  * @param field Attribute to retrieve
2693  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2694  * @return boolean inidicating success
2695 */
2696  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2697                   LDAPMessage *msg, const char *field, SEC_DESC **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, 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 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 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                                         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 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                                    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, 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 DOM_SID (objectSid)
3587  * @param primary_group_sid pointer to 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                               DOM_SID *user_sid,
3596                               DOM_SID *primary_group_sid,
3597                               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         DOM_SID *tmp_sids;
3605         DOM_SID tmp_user_sid;
3606         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                 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         struct ldb_dn *name_dn = NULL;
3860         const char *name = NULL;
3861         char *ou_string = NULL;
3862         struct ldb_context *ldb = ldb_init(mem_ctx, NULL);
3863
3864         name_dn = ldb_dn_new(mem_ctx, ldb, *account_ou);
3865         if (name_dn && ldb_dn_validate(name_dn)) {
3866                 talloc_free(ldb);
3867                 return ADS_SUCCESS;
3868         }
3869
3870         ou_string = ads_ou_string(ads, *account_ou);
3871         if (!ou_string) {
3872                 talloc_free(ldb);
3873                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3874         }
3875
3876         name_dn = ldb_dn_new_fmt(mem_ctx, ldb, "%s,%s", ou_string,
3877                                  ads->config.bind_path);
3878         SAFE_FREE(ou_string);
3879
3880         if (!name_dn || !ldb_dn_validate(name_dn)) {
3881                 talloc_free(ldb);
3882                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3883         }
3884
3885         *account_ou = talloc_strdup(mem_ctx, name);
3886         if (!*account_ou) {
3887                 talloc_free(ldb);
3888                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3889         }
3890
3891         talloc_free(ldb);
3892         return ADS_SUCCESS;
3893 }
3894
3895 #endif