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