Merge branch 'master' of ssh://git.samba.org/data/git/samba into regsrv
[kai/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 NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
180         TALLOC_CTX *mem_ctx = NULL;
181         bool ret = false;
182
183         if (!server || !*server) {
184                 return False;
185         }
186         
187         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
188                 server, ads->server.realm));
189
190         mem_ctx = talloc_init("ads_try_connect");
191         if (!mem_ctx) {
192                 DEBUG(0,("out of memory\n"));
193                 return false;
194         }
195
196         /* this copes with inet_ntoa brokenness */
197         
198         srv = SMB_STRDUP(server);
199
200         ZERO_STRUCT( cldap_reply );
201
202         if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
203                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
204                 ret = false;
205                 goto out;
206         }
207
208         /* Check the CLDAP reply flags */
209
210         if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
211                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
212                         srv));
213                 ret = false;
214                 goto out;
215         }
216
217         /* Fill in the ads->config values */
218
219         SAFE_FREE(ads->config.realm);
220         SAFE_FREE(ads->config.bind_path);
221         SAFE_FREE(ads->config.ldap_server_name);
222         SAFE_FREE(ads->config.server_site_name);
223         SAFE_FREE(ads->config.client_site_name);
224         SAFE_FREE(ads->server.workgroup);
225
226         ads->config.flags              = cldap_reply.server_type;
227         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.pdc_dns_name);
228         ads->config.realm              = SMB_STRDUP(cldap_reply.dns_domain);
229         strupper_m(ads->config.realm);
230         ads->config.bind_path          = ads_build_dn(ads->config.realm);
231         if (*cldap_reply.server_site) {
232                 ads->config.server_site_name =
233                         SMB_STRDUP(cldap_reply.server_site);
234         }
235         if (*cldap_reply.client_site) {
236                 ads->config.client_site_name =
237                         SMB_STRDUP(cldap_reply.client_site);
238         }
239         ads->server.workgroup          = SMB_STRDUP(cldap_reply.domain);
240
241         ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
242         if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
243                 DEBUG(1,("ads_try_connect: unable to convert %s "
244                         "to an address\n",
245                         srv));
246                 ret = false;
247                 goto out;
248         }
249
250         /* Store our site name. */
251         sitename_store( cldap_reply.domain, cldap_reply.client_site);
252         sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
253
254         ret = true;
255  out:
256         SAFE_FREE(srv);
257         TALLOC_FREE(mem_ctx);
258
259         return ret;
260 }
261
262 /**********************************************************************
263  Try to find an AD dc using our internal name resolution routines
264  Try the realm first and then then workgroup name if netbios is not 
265  disabled
266 **********************************************************************/
267
268 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
269 {
270         const char *c_realm;
271         int count, i=0;
272         struct ip_service *ip_list;
273         const char *realm;
274         bool got_realm = False;
275         bool use_own_domain = False;
276         char *sitename;
277         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
278
279         /* if the realm and workgroup are both empty, assume they are ours */
280
281         /* realm */
282         c_realm = ads->server.realm;
283
284         if ( !c_realm || !*c_realm ) {
285                 /* special case where no realm and no workgroup means our own */
286                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
287                         use_own_domain = True;
288                         c_realm = lp_realm();
289                 }
290         }
291
292         if (c_realm && *c_realm)
293                 got_realm = True;
294
295         /* we need to try once with the realm name and fallback to the
296            netbios domain name if we fail (if netbios has not been disabled */
297
298         if ( !got_realm && !lp_disable_netbios() ) {
299                 c_realm = ads->server.workgroup;
300                 if (!c_realm || !*c_realm) {
301                         if ( use_own_domain )
302                                 c_realm = lp_workgroup();
303                 }
304
305                 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 = NULL;
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 (!(search_attrs = str_list_copy(talloc_tos(), 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 (!(search_attrs = str_list_copy(talloc_tos(), 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, GUID_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                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2832                         goto done;
2833                 }
2834                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2835                 status = ads_connect( ads_s );
2836                 if ( !ADS_ERR_OK(status))
2837                         goto done;
2838         }
2839
2840         /* If the attribute does not exist assume it is a Windows 2000 
2841            functional domain */
2842            
2843         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2844         if (!ADS_ERR_OK(status)) {
2845                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2846                         status = ADS_SUCCESS;
2847                 }
2848                 goto done;
2849         }
2850
2851         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2852                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2853         }
2854         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2855
2856         
2857         ads_msgfree(ads, res);
2858
2859 done:
2860         /* free any temporary ads connections */
2861         if ( ads_s != ads ) {
2862                 ads_destroy( &ads_s );
2863         }
2864
2865         return status;
2866 }
2867
2868 /**
2869  * find the domain sid for our domain
2870  * @param ads connection to ads server
2871  * @param sid Pointer to domain sid
2872  * @return status of search
2873  **/
2874 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2875 {
2876         const char *attrs[] = {"objectSid", NULL};
2877         LDAPMessage *res;
2878         ADS_STATUS rc;
2879
2880         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2881                            attrs, &res);
2882         if (!ADS_ERR_OK(rc)) return rc;
2883         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2884                 ads_msgfree(ads, res);
2885                 return ADS_ERROR_SYSTEM(ENOENT);
2886         }
2887         ads_msgfree(ads, res);
2888         
2889         return ADS_SUCCESS;
2890 }
2891
2892 /**
2893  * find our site name 
2894  * @param ads connection to ads server
2895  * @param mem_ctx Pointer to talloc context
2896  * @param site_name Pointer to the sitename
2897  * @return status of search
2898  **/
2899 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2900 {
2901         ADS_STATUS status;
2902         LDAPMessage *res;
2903         const char *dn, *service_name;
2904         const char *attrs[] = { "dsServiceName", NULL };
2905
2906         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2907         if (!ADS_ERR_OK(status)) {
2908                 return status;
2909         }
2910
2911         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2912         if (service_name == NULL) {
2913                 ads_msgfree(ads, res);
2914                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2915         }
2916
2917         ads_msgfree(ads, res);
2918
2919         /* go up three levels */
2920         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2921         if (dn == NULL) {
2922                 return ADS_ERROR(LDAP_NO_MEMORY);
2923         }
2924
2925         *site_name = talloc_strdup(mem_ctx, dn);
2926         if (*site_name == NULL) {
2927                 return ADS_ERROR(LDAP_NO_MEMORY);
2928         }
2929
2930         return status;
2931         /*
2932         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2933         */                                               
2934 }
2935
2936 /**
2937  * find the site dn where a machine resides
2938  * @param ads connection to ads server
2939  * @param mem_ctx Pointer to talloc context
2940  * @param computer_name name of the machine
2941  * @param site_name Pointer to the sitename
2942  * @return status of search
2943  **/
2944 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2945 {
2946         ADS_STATUS status;
2947         LDAPMessage *res;
2948         const char *parent, *filter;
2949         char *config_context = NULL;
2950         char *dn;
2951
2952         /* shortcut a query */
2953         if (strequal(computer_name, ads->config.ldap_server_name)) {
2954                 return ads_site_dn(ads, mem_ctx, site_dn);
2955         }
2956
2957         status = ads_config_path(ads, mem_ctx, &config_context);
2958         if (!ADS_ERR_OK(status)) {
2959                 return status;
2960         }
2961
2962         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2963         if (filter == NULL) {
2964                 return ADS_ERROR(LDAP_NO_MEMORY);
2965         }
2966
2967         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
2968                                filter, NULL, &res);
2969         if (!ADS_ERR_OK(status)) {
2970                 return status;
2971         }
2972
2973         if (ads_count_replies(ads, res) != 1) {
2974                 ads_msgfree(ads, res);
2975                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2976         }
2977
2978         dn = ads_get_dn(ads, res);
2979         if (dn == NULL) {
2980                 ads_msgfree(ads, res);
2981                 return ADS_ERROR(LDAP_NO_MEMORY);
2982         }
2983
2984         /* go up three levels */
2985         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2986         if (parent == NULL) {
2987                 ads_msgfree(ads, res);
2988                 ads_memfree(ads, dn);
2989                 return ADS_ERROR(LDAP_NO_MEMORY);
2990         }
2991
2992         *site_dn = talloc_strdup(mem_ctx, parent);
2993         if (*site_dn == NULL) {
2994                 ads_msgfree(ads, res);
2995                 ads_memfree(ads, dn);
2996                 return ADS_ERROR(LDAP_NO_MEMORY);
2997         }
2998
2999         ads_memfree(ads, dn);
3000         ads_msgfree(ads, res);
3001
3002         return status;
3003 }
3004
3005 /**
3006  * get the upn suffixes for a domain
3007  * @param ads connection to ads server
3008  * @param mem_ctx Pointer to talloc context
3009  * @param suffixes Pointer to an array of suffixes
3010  * @param num_suffixes Pointer to the number of suffixes
3011  * @return status of search
3012  **/
3013 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3014 {
3015         ADS_STATUS status;
3016         LDAPMessage *res;
3017         const char *base;
3018         char *config_context = NULL;
3019         const char *attrs[] = { "uPNSuffixes", NULL };
3020
3021         status = ads_config_path(ads, mem_ctx, &config_context);
3022         if (!ADS_ERR_OK(status)) {
3023                 return status;
3024         }
3025
3026         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3027         if (base == NULL) {
3028                 return ADS_ERROR(LDAP_NO_MEMORY);
3029         }
3030
3031         status = ads_search_dn(ads, &res, base, attrs);
3032         if (!ADS_ERR_OK(status)) {
3033                 return status;
3034         }
3035
3036         if (ads_count_replies(ads, res) != 1) {
3037                 ads_msgfree(ads, res);
3038                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3039         }
3040
3041         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3042         if ((*suffixes) == NULL) {
3043                 ads_msgfree(ads, res);
3044                 return ADS_ERROR(LDAP_NO_MEMORY);
3045         }
3046
3047         ads_msgfree(ads, res);
3048
3049         return status;
3050 }
3051
3052 /**
3053  * get the joinable ous for a domain
3054  * @param ads connection to ads server
3055  * @param mem_ctx Pointer to talloc context
3056  * @param ous Pointer to an array of ous
3057  * @param num_ous Pointer to the number of ous
3058  * @return status of search
3059  **/
3060 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3061                                 TALLOC_CTX *mem_ctx,
3062                                 char ***ous,
3063                                 size_t *num_ous)
3064 {
3065         ADS_STATUS status;
3066         LDAPMessage *res = NULL;
3067         LDAPMessage *msg = NULL;
3068         const char *attrs[] = { "dn", NULL };
3069         int count = 0;
3070
3071         status = ads_search(ads, &res,
3072                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3073                             attrs);
3074         if (!ADS_ERR_OK(status)) {
3075                 return status;
3076         }
3077
3078         count = ads_count_replies(ads, res);
3079         if (count < 1) {
3080                 ads_msgfree(ads, res);
3081                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3082         }
3083
3084         for (msg = ads_first_entry(ads, res); msg;
3085              msg = ads_next_entry(ads, msg)) {
3086
3087                 char *dn = NULL;
3088
3089                 dn = ads_get_dn(ads, msg);
3090                 if (!dn) {
3091                         ads_msgfree(ads, res);
3092                         return ADS_ERROR(LDAP_NO_MEMORY);
3093                 }
3094
3095                 if (!add_string_to_array(mem_ctx, dn,
3096                                          (const char ***)ous,
3097                                          (int *)num_ous)) {
3098                         ads_memfree(ads, dn);
3099                         ads_msgfree(ads, res);
3100                         return ADS_ERROR(LDAP_NO_MEMORY);
3101                 }
3102
3103                 ads_memfree(ads, dn);
3104         }
3105
3106         ads_msgfree(ads, res);
3107
3108         return status;
3109 }
3110
3111
3112 /**
3113  * pull a DOM_SID from an extended dn string
3114  * @param mem_ctx TALLOC_CTX 
3115  * @param extended_dn string
3116  * @param flags string type of extended_dn
3117  * @param sid pointer to a DOM_SID
3118  * @return boolean inidicating success
3119  **/
3120 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
3121                                   const char *extended_dn, 
3122                                   enum ads_extended_dn_flags flags, 
3123                                   DOM_SID *sid)
3124 {
3125         char *p, *q, *dn;
3126
3127         if (!extended_dn) {
3128                 return False;
3129         }
3130
3131         /* otherwise extended_dn gets stripped off */
3132         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3133                 return False;
3134         }
3135         /* 
3136          * ADS_EXTENDED_DN_HEX_STRING:
3137          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3138          *
3139          * ADS_EXTENDED_DN_STRING (only with w2k3):
3140         <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
3141          */
3142
3143         p = strchr(dn, ';');
3144         if (!p) {
3145                 return False;
3146         }
3147
3148         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3149                 return False;
3150         }
3151
3152         p += strlen(";<SID=");
3153
3154         q = strchr(p, '>');
3155         if (!q) {
3156                 return False;
3157         }
3158         
3159         *q = '\0';
3160
3161         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3162
3163         switch (flags) {
3164         
3165         case ADS_EXTENDED_DN_STRING:
3166                 if (!string_to_sid(sid, p)) {
3167                         return False;
3168                 }
3169                 break;
3170         case ADS_EXTENDED_DN_HEX_STRING: {
3171                 fstring buf;
3172                 size_t buf_len;
3173
3174                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3175                 if (buf_len == 0) {
3176                         return False;
3177                 }
3178
3179                 if (!sid_parse(buf, buf_len, sid)) {
3180                         DEBUG(10,("failed to parse sid\n"));
3181                         return False;
3182                 }
3183                 break;
3184                 }
3185         default:
3186                 DEBUG(10,("unknown extended dn format\n"));
3187                 return False;
3188         }
3189
3190         return True;
3191 }
3192
3193 /**
3194  * pull an array of DOM_SIDs from a ADS result
3195  * @param ads connection to ads server
3196  * @param mem_ctx TALLOC_CTX for allocating sid array
3197  * @param msg Results of search
3198  * @param field Attribute to retrieve
3199  * @param flags string type of extended_dn
3200  * @param sids pointer to sid array to allocate
3201  * @return the count of SIDs pulled
3202  **/
3203  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
3204                                    TALLOC_CTX *mem_ctx, 
3205                                    LDAPMessage *msg, 
3206                                    const char *field,
3207                                    enum ads_extended_dn_flags flags,
3208                                    DOM_SID **sids)
3209 {
3210         int i;
3211         size_t dn_count;
3212         char **dn_strings;
3213
3214         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
3215                                            &dn_count)) == NULL) {
3216                 return 0;
3217         }
3218
3219         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3220         if (!(*sids)) {
3221                 TALLOC_FREE(dn_strings);
3222                 return 0;
3223         }
3224
3225         for (i=0; i<dn_count; i++) {
3226
3227                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
3228                                                   flags, &(*sids)[i])) {
3229                         TALLOC_FREE(*sids);
3230                         TALLOC_FREE(dn_strings);
3231                         return 0;
3232                 }
3233         }
3234
3235         TALLOC_FREE(dn_strings);
3236
3237         return dn_count;
3238 }
3239
3240 /********************************************************************
3241 ********************************************************************/
3242
3243 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3244 {
3245         LDAPMessage *res = NULL;
3246         ADS_STATUS status;
3247         int count = 0;
3248         char *name = NULL;
3249         
3250         status = ads_find_machine_acct(ads, &res, global_myname());
3251         if (!ADS_ERR_OK(status)) {
3252                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3253                         global_myname()));
3254                 goto out;
3255         }
3256                 
3257         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3258                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3259                 goto out;
3260         }
3261                 
3262         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3263                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3264         }
3265
3266 out:
3267         ads_msgfree(ads, res);
3268         
3269         return name;
3270 }
3271
3272 /********************************************************************
3273 ********************************************************************/
3274
3275 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3276 {
3277         LDAPMessage *res = NULL;
3278         ADS_STATUS status;
3279         int count = 0;
3280         char *name = NULL;
3281
3282         status = ads_find_machine_acct(ads, &res, machine_name);
3283         if (!ADS_ERR_OK(status)) {
3284                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3285                         global_myname()));
3286                 goto out;
3287         }
3288
3289         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3290                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3291                 goto out;
3292         }
3293
3294         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3295                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3296         }
3297
3298 out:
3299         ads_msgfree(ads, res);
3300
3301         return name;
3302 }
3303
3304 /********************************************************************
3305 ********************************************************************/
3306
3307 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3308 {
3309         LDAPMessage *res = NULL;
3310         ADS_STATUS status;
3311         int count = 0;
3312         char *name = NULL;
3313         
3314         status = ads_find_machine_acct(ads, &res, global_myname());
3315         if (!ADS_ERR_OK(status)) {
3316                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3317                         global_myname()));
3318                 goto out;
3319         }
3320                 
3321         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3322                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3323                 goto out;
3324         }
3325                 
3326         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3327                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3328         }
3329
3330 out:
3331         ads_msgfree(ads, res);
3332         
3333         return name;
3334 }
3335
3336 #if 0
3337
3338    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3339
3340 /**
3341  * Join a machine to a realm
3342  *  Creates the machine account and sets the machine password
3343  * @param ads connection to ads server
3344  * @param machine name of host to add
3345  * @param org_unit Organizational unit to place machine in
3346  * @return status of join
3347  **/
3348 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3349                         uint32 account_type, const char *org_unit)
3350 {
3351         ADS_STATUS status;
3352         LDAPMessage *res = NULL;
3353         char *machine;
3354
3355         /* machine name must be lowercase */
3356         machine = SMB_STRDUP(machine_name);
3357         strlower_m(machine);
3358
3359         /*
3360         status = ads_find_machine_acct(ads, (void **)&res, machine);
3361         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3362                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3363                 status = ads_leave_realm(ads, machine);
3364                 if (!ADS_ERR_OK(status)) {
3365                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3366                                 machine, ads->config.realm));
3367                         return status;
3368                 }
3369         }
3370         */
3371         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3372         if (!ADS_ERR_OK(status)) {
3373                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3374                 SAFE_FREE(machine);
3375                 return status;
3376         }
3377
3378         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3379         if (!ADS_ERR_OK(status)) {
3380                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3381                 SAFE_FREE(machine);
3382                 return status;
3383         }
3384
3385         SAFE_FREE(machine);
3386         ads_msgfree(ads, res);
3387
3388         return status;
3389 }
3390 #endif
3391
3392 /**
3393  * Delete a machine from the realm
3394  * @param ads connection to ads server
3395  * @param hostname Machine to remove
3396  * @return status of delete
3397  **/
3398 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3399 {
3400         ADS_STATUS status;
3401         void *msg;
3402         LDAPMessage *res;
3403         char *hostnameDN, *host;
3404         int rc;
3405         LDAPControl ldap_control;
3406         LDAPControl  * pldap_control[2] = {NULL, NULL};
3407
3408         pldap_control[0] = &ldap_control;
3409         memset(&ldap_control, 0, sizeof(LDAPControl));
3410         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3411
3412         /* hostname must be lowercase */
3413         host = SMB_STRDUP(hostname);
3414         strlower_m(host);
3415
3416         status = ads_find_machine_acct(ads, &res, host);
3417         if (!ADS_ERR_OK(status)) {
3418                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3419                 SAFE_FREE(host);
3420                 return status;
3421         }
3422
3423         msg = ads_first_entry(ads, res);
3424         if (!msg) {
3425                 SAFE_FREE(host);
3426                 return ADS_ERROR_SYSTEM(ENOENT);
3427         }
3428
3429         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3430
3431         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3432         if (rc) {
3433                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3434         }else {
3435                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3436         }
3437
3438         if (rc != LDAP_SUCCESS) {
3439                 const char *attrs[] = { "cn", NULL };
3440                 LDAPMessage *msg_sub;
3441
3442                 /* we only search with scope ONE, we do not expect any further
3443                  * objects to be created deeper */
3444
3445                 status = ads_do_search_retry(ads, hostnameDN,
3446                                              LDAP_SCOPE_ONELEVEL,
3447                                              "(objectclass=*)", attrs, &res);
3448
3449                 if (!ADS_ERR_OK(status)) {
3450                         SAFE_FREE(host);
3451                         ads_memfree(ads, hostnameDN);
3452                         return status;
3453                 }
3454
3455                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3456                         msg_sub = ads_next_entry(ads, msg_sub)) {
3457
3458                         char *dn = NULL;
3459
3460                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3461                                 SAFE_FREE(host);
3462                                 ads_memfree(ads, hostnameDN);
3463                                 return ADS_ERROR(LDAP_NO_MEMORY);
3464                         }
3465
3466                         status = ads_del_dn(ads, dn);
3467                         if (!ADS_ERR_OK(status)) {
3468                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3469                                 SAFE_FREE(host);
3470                                 ads_memfree(ads, dn);
3471                                 ads_memfree(ads, hostnameDN);
3472                                 return status;
3473                         }
3474
3475                         ads_memfree(ads, dn);
3476                 }
3477
3478                 /* there should be no subordinate objects anymore */
3479                 status = ads_do_search_retry(ads, hostnameDN,
3480                                              LDAP_SCOPE_ONELEVEL,
3481                                              "(objectclass=*)", attrs, &res);
3482
3483                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3484                         SAFE_FREE(host);
3485                         ads_memfree(ads, hostnameDN);
3486                         return status;
3487                 }
3488
3489                 /* delete hostnameDN now */
3490                 status = ads_del_dn(ads, hostnameDN);
3491                 if (!ADS_ERR_OK(status)) {
3492                         SAFE_FREE(host);
3493                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3494                         ads_memfree(ads, hostnameDN);
3495                         return status;
3496                 }
3497         }
3498
3499         ads_memfree(ads, hostnameDN);
3500
3501         status = ads_find_machine_acct(ads, &res, host);
3502         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3503                 DEBUG(3, ("Failed to remove host account.\n"));
3504                 SAFE_FREE(host);
3505                 return status;
3506         }
3507
3508         SAFE_FREE(host);
3509         return status;
3510 }
3511
3512 /**
3513  * pull all token-sids from an LDAP dn
3514  * @param ads connection to ads server
3515  * @param mem_ctx TALLOC_CTX for allocating sid array
3516  * @param dn of LDAP object
3517  * @param user_sid pointer to DOM_SID (objectSid)
3518  * @param primary_group_sid pointer to DOM_SID (self composed)
3519  * @param sids pointer to sid array to allocate
3520  * @param num_sids counter of SIDs pulled
3521  * @return status of token query
3522  **/
3523  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3524                               TALLOC_CTX *mem_ctx,
3525                               const char *dn,
3526                               DOM_SID *user_sid,
3527                               DOM_SID *primary_group_sid,
3528                               DOM_SID **sids,
3529                               size_t *num_sids)
3530 {
3531         ADS_STATUS status;
3532         LDAPMessage *res = NULL;
3533         int count = 0;
3534         size_t tmp_num_sids;
3535         DOM_SID *tmp_sids;
3536         DOM_SID tmp_user_sid;
3537         DOM_SID tmp_primary_group_sid;
3538         uint32 pgid;
3539         const char *attrs[] = {
3540                 "objectSid",
3541                 "tokenGroups",
3542                 "primaryGroupID",
3543                 NULL
3544         };
3545
3546         status = ads_search_retry_dn(ads, &res, dn, attrs);
3547         if (!ADS_ERR_OK(status)) {
3548                 return status;
3549         }
3550
3551         count = ads_count_replies(ads, res);
3552         if (count != 1) {
3553                 ads_msgfree(ads, res);
3554                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3555         }
3556
3557         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3558                 ads_msgfree(ads, res);
3559                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3560         }
3561
3562         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3563                 ads_msgfree(ads, res);
3564                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3565         }
3566
3567         {
3568                 /* hack to compose the primary group sid without knowing the
3569                  * domsid */
3570
3571                 DOM_SID domsid;
3572                 uint32 dummy_rid;
3573
3574                 sid_copy(&domsid, &tmp_user_sid);
3575
3576                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3577                         ads_msgfree(ads, res);
3578                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3579                 }
3580
3581                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3582                         ads_msgfree(ads, res);
3583                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3584                 }
3585         }
3586
3587         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3588
3589         if (tmp_num_sids == 0 || !tmp_sids) {
3590                 ads_msgfree(ads, res);
3591                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3592         }
3593
3594         if (num_sids) {
3595                 *num_sids = tmp_num_sids;
3596         }
3597
3598         if (sids) {
3599                 *sids = tmp_sids;
3600         }
3601
3602         if (user_sid) {
3603                 *user_sid = tmp_user_sid;
3604         }
3605
3606         if (primary_group_sid) {
3607                 *primary_group_sid = tmp_primary_group_sid;
3608         }
3609
3610         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3611
3612         ads_msgfree(ads, res);
3613         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3614 }
3615
3616 /**
3617  * Find a sAMAccoutName in LDAP
3618  * @param ads connection to ads server
3619  * @param mem_ctx TALLOC_CTX for allocating sid array
3620  * @param samaccountname to search
3621  * @param uac_ret uint32 pointer userAccountControl attribute value
3622  * @param dn_ret pointer to dn
3623  * @return status of token query
3624  **/
3625 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3626                                TALLOC_CTX *mem_ctx,
3627                                const char *samaccountname,
3628                                uint32 *uac_ret,
3629                                const char **dn_ret)
3630 {
3631         ADS_STATUS status;
3632         const char *attrs[] = { "userAccountControl", NULL };
3633         const char *filter;
3634         LDAPMessage *res = NULL;
3635         char *dn = NULL;
3636         uint32 uac = 0;
3637
3638         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3639                 samaccountname);
3640         if (filter == NULL) {
3641                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3642                 goto out;
3643         }
3644
3645         status = ads_do_search_all(ads, ads->config.bind_path,
3646                                    LDAP_SCOPE_SUBTREE,
3647                                    filter, attrs, &res);
3648         
3649         if (!ADS_ERR_OK(status)) {
3650                 goto out;
3651         }
3652
3653         if (ads_count_replies(ads, res) != 1) {
3654                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3655                 goto out;
3656         }
3657
3658         dn = ads_get_dn(ads, res);
3659         if (dn == NULL) {
3660                 status = ADS_ERROR(LDAP_NO_MEMORY);
3661                 goto out;
3662         }
3663
3664         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3665                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3666                 goto out;
3667         }
3668
3669         if (uac_ret) {
3670                 *uac_ret = uac;
3671         }
3672
3673         if (dn_ret) {
3674                 *dn_ret = talloc_strdup(mem_ctx, dn);
3675                 if (!*dn_ret) {
3676                         status = ADS_ERROR(LDAP_NO_MEMORY);
3677                         goto out;
3678                 }
3679         }
3680  out:
3681         ads_memfree(ads, dn);
3682         ads_msgfree(ads, res);
3683
3684         return status;
3685 }
3686
3687 /**
3688  * find our configuration path 
3689  * @param ads connection to ads server
3690  * @param mem_ctx Pointer to talloc context
3691  * @param config_path Pointer to the config path
3692  * @return status of search
3693  **/
3694 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3695                            TALLOC_CTX *mem_ctx, 
3696                            char **config_path)
3697 {
3698         ADS_STATUS status;
3699         LDAPMessage *res = NULL;
3700         const char *config_context = NULL;
3701         const char *attrs[] = { "configurationNamingContext", NULL };
3702
3703         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3704                                "(objectclass=*)", attrs, &res);
3705         if (!ADS_ERR_OK(status)) {
3706                 return status;
3707         }
3708
3709         config_context = ads_pull_string(ads, mem_ctx, res, 
3710                                          "configurationNamingContext");
3711         ads_msgfree(ads, res);
3712         if (!config_context) {
3713                 return ADS_ERROR(LDAP_NO_MEMORY);
3714         }
3715
3716         if (config_path) {
3717                 *config_path = talloc_strdup(mem_ctx, config_context);
3718                 if (!*config_path) {
3719                         return ADS_ERROR(LDAP_NO_MEMORY);
3720                 }
3721         }
3722
3723         return ADS_ERROR(LDAP_SUCCESS);
3724 }
3725
3726 /**
3727  * find the displayName of an extended right 
3728  * @param ads connection to ads server
3729  * @param config_path The config path
3730  * @param mem_ctx Pointer to talloc context
3731  * @param GUID struct of the rightsGUID
3732  * @return status of search
3733  **/
3734 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3735                                                 const char *config_path, 
3736                                                 TALLOC_CTX *mem_ctx, 
3737                                                 const struct GUID *rights_guid)
3738 {
3739         ADS_STATUS rc;
3740         LDAPMessage *res = NULL;
3741         char *expr = NULL;
3742         const char *attrs[] = { "displayName", NULL };
3743         const char *result = NULL;
3744         const char *path;
3745
3746         if (!ads || !mem_ctx || !rights_guid) {
3747                 goto done;
3748         }
3749
3750         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3751                                GUID_string(mem_ctx, rights_guid));
3752         if (!expr) {
3753                 goto done;
3754         }
3755
3756         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3757         if (!path) {
3758                 goto done;
3759         }
3760
3761         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3762                                  expr, attrs, &res);
3763         if (!ADS_ERR_OK(rc)) {
3764                 goto done;
3765         }
3766
3767         if (ads_count_replies(ads, res) != 1) {
3768                 goto done;
3769         }
3770
3771         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3772
3773  done:
3774         ads_msgfree(ads, res);
3775         return result;
3776         
3777 }
3778
3779 /**
3780  * verify or build and verify an account ou
3781  * @param mem_ctx Pointer to talloc context
3782  * @param ads connection to ads server
3783  * @param account_ou
3784  * @return status of search
3785  **/
3786
3787 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3788                            ADS_STRUCT *ads,
3789                            const char **account_ou)
3790 {
3791         struct ldb_dn *name_dn = NULL;
3792         const char *name = NULL;
3793         char *ou_string = NULL;
3794
3795         name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3796         if (name_dn) {
3797                 return ADS_SUCCESS;
3798         }
3799
3800         ou_string = ads_ou_string(ads, *account_ou);
3801         if (!ou_string) {
3802                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3803         }
3804
3805         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3806                                ads->config.bind_path);
3807         SAFE_FREE(ou_string);
3808         if (!name) {
3809                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3810         }
3811
3812         name_dn = ldb_dn_explode(mem_ctx, name);
3813         if (!name_dn) {
3814                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3815         }
3816
3817         *account_ou = talloc_strdup(mem_ctx, name);
3818         if (!*account_ou) {
3819                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3820         }
3821
3822         return ADS_SUCCESS;
3823 }
3824
3825 #endif