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