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