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