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