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