libads: add ads_get_machine_kvno() to make ads_get_kvno() a bit more generic.
[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 an account's current KVNO via an LDAP lookup
1520  * @param ads An initialized ADS_STRUCT
1521  * @param account_name the NT samaccountname.
1522  * @return the kvno for the account, or -1 in case of a failure.
1523  **/
1524
1525 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_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 account %s\n", account_name));
1535         if (asprintf(&filter, "(samAccountName=%s)", account_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) != 1)) {
1541                 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_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  * Determines the computer account's current KVNO via an LDAP lookup
1578  * @param ads An initialized ADS_STRUCT
1579  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1580  * @return the kvno for the computer account, or -1 in case of a failure.
1581  **/
1582
1583 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1584 {
1585         char *computer_account = NULL;
1586         uint32_t kvno = -1;
1587
1588         if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1589                 return kvno;
1590         }
1591
1592         kvno = ads_get_kvno(ads, computer_account);
1593         free(computer_account);
1594
1595         return kvno;
1596 }
1597
1598 /**
1599  * This clears out all registered spn's for a given hostname
1600  * @param ads An initilaized ADS_STRUCT
1601  * @param machine_name the NetBIOS name of the computer.
1602  * @return 0 upon success, non-zero otherwise.
1603  **/
1604
1605 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1606 {
1607         TALLOC_CTX *ctx;
1608         LDAPMessage *res = NULL;
1609         ADS_MODLIST mods;
1610         const char *servicePrincipalName[1] = {NULL};
1611         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1612         char *dn_string = NULL;
1613
1614         ret = ads_find_machine_acct(ads, &res, machine_name);
1615         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1616                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1617                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1618                 ads_msgfree(ads, res);
1619                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1620         }
1621
1622         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1623         ctx = talloc_init("ads_clear_service_principal_names");
1624         if (!ctx) {
1625                 ads_msgfree(ads, res);
1626                 return ADS_ERROR(LDAP_NO_MEMORY);
1627         }
1628
1629         if (!(mods = ads_init_mods(ctx))) {
1630                 talloc_destroy(ctx);
1631                 ads_msgfree(ads, res);
1632                 return ADS_ERROR(LDAP_NO_MEMORY);
1633         }
1634         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1635         if (!ADS_ERR_OK(ret)) {
1636                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1637                 ads_msgfree(ads, res);
1638                 talloc_destroy(ctx);
1639                 return ret;
1640         }
1641         dn_string = ads_get_dn(ads, res);
1642         if (!dn_string) {
1643                 talloc_destroy(ctx);
1644                 ads_msgfree(ads, res);
1645                 return ADS_ERROR(LDAP_NO_MEMORY);
1646         }
1647         ret = ads_gen_mod(ads, dn_string, mods);
1648         ads_memfree(ads,dn_string);
1649         if (!ADS_ERR_OK(ret)) {
1650                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1651                         machine_name));
1652                 ads_msgfree(ads, res);
1653                 talloc_destroy(ctx);
1654                 return ret;
1655         }
1656
1657         ads_msgfree(ads, res);
1658         talloc_destroy(ctx);
1659         return ret;
1660 }
1661
1662 /**
1663  * This adds a service principal name to an existing computer account
1664  * (found by hostname) in AD.
1665  * @param ads An initialized ADS_STRUCT
1666  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1667  * @param my_fqdn The fully qualified DNS name of the machine
1668  * @param spn A string of the service principal to add, i.e. 'host'
1669  * @return 0 upon sucess, or non-zero if a failure occurs
1670  **/
1671
1672 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1673                                           const char *my_fqdn, const char *spn)
1674 {
1675         ADS_STATUS ret;
1676         TALLOC_CTX *ctx;
1677         LDAPMessage *res = NULL;
1678         char *psp1, *psp2;
1679         ADS_MODLIST mods;
1680         char *dn_string = NULL;
1681         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1682
1683         ret = ads_find_machine_acct(ads, &res, machine_name);
1684         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1685                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1686                         machine_name));
1687                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1688                         spn, machine_name, ads->config.realm));
1689                 ads_msgfree(ads, res);
1690                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1691         }
1692
1693         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1694         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1695                 ads_msgfree(ads, res);
1696                 return ADS_ERROR(LDAP_NO_MEMORY);
1697         }
1698
1699         /* add short name spn */
1700         
1701         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1702                 talloc_destroy(ctx);
1703                 ads_msgfree(ads, res);
1704                 return ADS_ERROR(LDAP_NO_MEMORY);
1705         }
1706         strupper_m(psp1);
1707         strlower_m(&psp1[strlen(spn)]);
1708         servicePrincipalName[0] = psp1;
1709         
1710         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1711                 psp1, machine_name));
1712
1713
1714         /* add fully qualified spn */
1715         
1716         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1717                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1718                 goto out;
1719         }
1720         strupper_m(psp2);
1721         strlower_m(&psp2[strlen(spn)]);
1722         servicePrincipalName[1] = psp2;
1723
1724         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1725                 psp2, machine_name));
1726
1727         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1728                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1729                 goto out;
1730         }
1731         
1732         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1733         if (!ADS_ERR_OK(ret)) {
1734                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1735                 goto out;
1736         }
1737         
1738         if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1739                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1740                 goto out;
1741         }
1742         
1743         ret = ads_gen_mod(ads, dn_string, mods);
1744         ads_memfree(ads,dn_string);
1745         if (!ADS_ERR_OK(ret)) {
1746                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1747                 goto out;
1748         }
1749
1750  out:
1751         TALLOC_FREE( ctx );
1752         ads_msgfree(ads, res);
1753         return ret;
1754 }
1755
1756 /**
1757  * adds a machine account to the ADS server
1758  * @param ads An intialized ADS_STRUCT
1759  * @param machine_name - the NetBIOS machine name of this account.
1760  * @param account_type A number indicating the type of account to create
1761  * @param org_unit The LDAP path in which to place this account
1762  * @return 0 upon success, or non-zero otherwise
1763 **/
1764
1765 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1766                                    const char *org_unit)
1767 {
1768         ADS_STATUS ret;
1769         char *samAccountName, *controlstr;
1770         TALLOC_CTX *ctx;
1771         ADS_MODLIST mods;
1772         char *machine_escaped = NULL;
1773         char *new_dn;
1774         const char *objectClass[] = {"top", "person", "organizationalPerson",
1775                                      "user", "computer", NULL};
1776         LDAPMessage *res = NULL;
1777         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1778                                 UF_DONT_EXPIRE_PASSWD |\
1779                                 UF_ACCOUNTDISABLE );
1780                               
1781         if (!(ctx = talloc_init("ads_add_machine_acct")))
1782                 return ADS_ERROR(LDAP_NO_MEMORY);
1783
1784         ret = ADS_ERROR(LDAP_NO_MEMORY);
1785
1786         machine_escaped = escape_rdn_val_string_alloc(machine_name);
1787         if (!machine_escaped) {
1788                 goto done;
1789         }
1790
1791         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1792         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1793
1794         if ( !new_dn || !samAccountName ) {
1795                 goto done;
1796         }
1797         
1798 #ifndef ENCTYPE_ARCFOUR_HMAC
1799         acct_control |= UF_USE_DES_KEY_ONLY;
1800 #endif
1801
1802         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1803                 goto done;
1804         }
1805
1806         if (!(mods = ads_init_mods(ctx))) {
1807                 goto done;
1808         }
1809         
1810         ads_mod_str(ctx, &mods, "cn", machine_name);
1811         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1812         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1813         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1814
1815         ret = ads_gen_add(ads, new_dn, mods);
1816
1817 done:
1818         SAFE_FREE(machine_escaped);
1819         ads_msgfree(ads, res);
1820         talloc_destroy(ctx);
1821         
1822         return ret;
1823 }
1824
1825 /**
1826  * move a machine account to another OU on the ADS server
1827  * @param ads - An intialized ADS_STRUCT
1828  * @param machine_name - the NetBIOS machine name of this account.
1829  * @param org_unit - The LDAP path in which to place this account
1830  * @param moved - whether we moved the machine account (optional)
1831  * @return 0 upon success, or non-zero otherwise
1832 **/
1833
1834 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1835                                  const char *org_unit, bool *moved)
1836 {
1837         ADS_STATUS rc;
1838         int ldap_status;
1839         LDAPMessage *res = NULL;
1840         char *filter = NULL;
1841         char *computer_dn = NULL;
1842         char *parent_dn;
1843         char *computer_rdn = NULL;
1844         bool need_move = False;
1845
1846         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1847                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1848                 goto done;
1849         }
1850
1851         /* Find pre-existing machine */
1852         rc = ads_search(ads, &res, filter, NULL);
1853         if (!ADS_ERR_OK(rc)) {
1854                 goto done;
1855         }
1856
1857         computer_dn = ads_get_dn(ads, res);
1858         if (!computer_dn) {
1859                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1860                 goto done;
1861         }
1862
1863         parent_dn = ads_parent_dn(computer_dn);
1864         if (strequal(parent_dn, org_unit)) {
1865                 goto done;
1866         }
1867
1868         need_move = True;
1869
1870         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1871                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1872                 goto done;
1873         }
1874
1875         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
1876                                     org_unit, 1, NULL, NULL);
1877         rc = ADS_ERROR(ldap_status);
1878
1879 done:
1880         ads_msgfree(ads, res);
1881         SAFE_FREE(filter);
1882         SAFE_FREE(computer_dn);
1883         SAFE_FREE(computer_rdn);
1884
1885         if (!ADS_ERR_OK(rc)) {
1886                 need_move = False;
1887         }
1888
1889         if (moved) {
1890                 *moved = need_move;
1891         }
1892
1893         return rc;
1894 }
1895
1896 /*
1897   dump a binary result from ldap
1898 */
1899 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1900 {
1901         int i, j;
1902         for (i=0; values[i]; i++) {
1903                 printf("%s: ", field);
1904                 for (j=0; j<values[i]->bv_len; j++) {
1905                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1906                 }
1907                 printf("\n");
1908         }
1909 }
1910
1911 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1912 {
1913         int i;
1914         for (i=0; values[i]; i++) {
1915
1916                 UUID_FLAT guid;
1917                 struct GUID tmp;
1918
1919                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1920                 smb_uuid_unpack(guid, &tmp);
1921                 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1922         }
1923 }
1924
1925 /*
1926   dump a sid result from ldap
1927 */
1928 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1929 {
1930         int i;
1931         for (i=0; values[i]; i++) {
1932                 DOM_SID sid;
1933                 fstring tmp;
1934                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1935                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1936         }
1937 }
1938
1939 /*
1940   dump ntSecurityDescriptor
1941 */
1942 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1943 {
1944         TALLOC_CTX *frame = talloc_stackframe();
1945         struct security_descriptor *psd;
1946         NTSTATUS status;
1947
1948         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1949                                      values[0]->bv_len, &psd);
1950         if (!NT_STATUS_IS_OK(status)) {
1951                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1952                           nt_errstr(status)));
1953                 TALLOC_FREE(frame);
1954                 return;
1955         }
1956
1957         if (psd) {
1958                 ads_disp_sd(ads, talloc_tos(), psd);
1959         }
1960
1961         TALLOC_FREE(frame);
1962 }
1963
1964 /*
1965   dump a string result from ldap
1966 */
1967 static void dump_string(const char *field, char **values)
1968 {
1969         int i;
1970         for (i=0; values[i]; i++) {
1971                 printf("%s: %s\n", field, values[i]);
1972         }
1973 }
1974
1975 /*
1976   dump a field from LDAP on stdout
1977   used for debugging
1978 */
1979
1980 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1981 {
1982         const struct {
1983                 const char *name;
1984                 bool string;
1985                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1986         } handlers[] = {
1987                 {"objectGUID", False, dump_guid},
1988                 {"netbootGUID", False, dump_guid},
1989                 {"nTSecurityDescriptor", False, dump_sd},
1990                 {"dnsRecord", False, dump_binary},
1991                 {"objectSid", False, dump_sid},
1992                 {"tokenGroups", False, dump_sid},
1993                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1994                 {"tokengroupsGlobalandUniversal", False, dump_sid},
1995                 {"mS-DS-CreatorSID", False, dump_sid},
1996                 {"msExchMailboxGuid", False, dump_guid},
1997                 {NULL, True, NULL}
1998         };
1999         int i;
2000
2001         if (!field) { /* must be end of an entry */
2002                 printf("\n");
2003                 return False;
2004         }
2005
2006         for (i=0; handlers[i].name; i++) {
2007                 if (StrCaseCmp(handlers[i].name, field) == 0) {
2008                         if (!values) /* first time, indicate string or not */
2009                                 return handlers[i].string;
2010                         handlers[i].handler(ads, field, (struct berval **) values);
2011                         break;
2012                 }
2013         }
2014         if (!handlers[i].name) {
2015                 if (!values) /* first time, indicate string conversion */
2016                         return True;
2017                 dump_string(field, (char **)values);
2018         }
2019         return False;
2020 }
2021
2022 /**
2023  * Dump a result from LDAP on stdout
2024  *  used for debugging
2025  * @param ads connection to ads server
2026  * @param res Results to dump
2027  **/
2028
2029  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2030 {
2031         ads_process_results(ads, res, ads_dump_field, NULL);
2032 }
2033
2034 /**
2035  * Walk through results, calling a function for each entry found.
2036  *  The function receives a field name, a berval * array of values,
2037  *  and a data area passed through from the start.  The function is
2038  *  called once with null for field and values at the end of each
2039  *  entry.
2040  * @param ads connection to ads server
2041  * @param res Results to process
2042  * @param fn Function for processing each result
2043  * @param data_area user-defined area to pass to function
2044  **/
2045  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2046                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2047                           void *data_area)
2048 {
2049         LDAPMessage *msg;
2050         TALLOC_CTX *ctx;
2051         size_t converted_size;
2052
2053         if (!(ctx = talloc_init("ads_process_results")))
2054                 return;
2055
2056         for (msg = ads_first_entry(ads, res); msg; 
2057              msg = ads_next_entry(ads, msg)) {
2058                 char *utf8_field;
2059                 BerElement *b;
2060         
2061                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2062                                                      (LDAPMessage *)msg,&b); 
2063                      utf8_field;
2064                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2065                                                     (LDAPMessage *)msg,b)) {
2066                         struct berval **ber_vals;
2067                         char **str_vals, **utf8_vals;
2068                         char *field;
2069                         bool string; 
2070
2071                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2072                                               &converted_size))
2073                         {
2074                                 DEBUG(0,("ads_process_results: "
2075                                          "pull_utf8_talloc failed: %s",
2076                                          strerror(errno)));
2077                         }
2078
2079                         string = fn(ads, field, NULL, data_area);
2080
2081                         if (string) {
2082                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2083                                                  (LDAPMessage *)msg, field);
2084                                 str_vals = ads_pull_strvals(ctx, 
2085                                                   (const char **) utf8_vals);
2086                                 fn(ads, field, (void **) str_vals, data_area);
2087                                 ldap_value_free(utf8_vals);
2088                         } else {
2089                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2090                                                  (LDAPMessage *)msg, field);
2091                                 fn(ads, field, (void **) ber_vals, data_area);
2092
2093                                 ldap_value_free_len(ber_vals);
2094                         }
2095                         ldap_memfree(utf8_field);
2096                 }
2097                 ber_free(b, 0);
2098                 talloc_free_children(ctx);
2099                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2100
2101         }
2102         talloc_destroy(ctx);
2103 }
2104
2105 /**
2106  * count how many replies are in a LDAPMessage
2107  * @param ads connection to ads server
2108  * @param res Results to count
2109  * @return number of replies
2110  **/
2111 int ads_count_replies(ADS_STRUCT *ads, void *res)
2112 {
2113         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2114 }
2115
2116 /**
2117  * pull the first entry from a ADS result
2118  * @param ads connection to ads server
2119  * @param res Results of search
2120  * @return first entry from result
2121  **/
2122  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2123 {
2124         return ldap_first_entry(ads->ldap.ld, res);
2125 }
2126
2127 /**
2128  * pull the next entry from a ADS result
2129  * @param ads connection to ads server
2130  * @param res Results of search
2131  * @return next entry from result
2132  **/
2133  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2134 {
2135         return ldap_next_entry(ads->ldap.ld, res);
2136 }
2137
2138 /**
2139  * pull the first message from a ADS result
2140  * @param ads connection to ads server
2141  * @param res Results of search
2142  * @return first message from result
2143  **/
2144  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2145 {
2146         return ldap_first_message(ads->ldap.ld, res);
2147 }
2148
2149 /**
2150  * pull the next message from a ADS result
2151  * @param ads connection to ads server
2152  * @param res Results of search
2153  * @return next message from result
2154  **/
2155  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2156 {
2157         return ldap_next_message(ads->ldap.ld, res);
2158 }
2159
2160 /**
2161  * pull a single string from a ADS result
2162  * @param ads connection to ads server
2163  * @param mem_ctx TALLOC_CTX to use for allocating result string
2164  * @param msg Results of search
2165  * @param field Attribute to retrieve
2166  * @return Result string in talloc context
2167  **/
2168  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2169                        const char *field)
2170 {
2171         char **values;
2172         char *ret = NULL;
2173         char *ux_string;
2174         size_t converted_size;
2175
2176         values = ldap_get_values(ads->ldap.ld, msg, field);
2177         if (!values)
2178                 return NULL;
2179         
2180         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2181                                           &converted_size))
2182         {
2183                 ret = ux_string;
2184         }
2185         ldap_value_free(values);
2186         return ret;
2187 }
2188
2189 /**
2190  * pull an array of strings from a ADS result
2191  * @param ads connection to ads server
2192  * @param mem_ctx TALLOC_CTX to use for allocating result string
2193  * @param msg Results of search
2194  * @param field Attribute to retrieve
2195  * @return Result strings in talloc context
2196  **/
2197  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2198                          LDAPMessage *msg, const char *field,
2199                          size_t *num_values)
2200 {
2201         char **values;
2202         char **ret = NULL;
2203         int i;
2204         size_t converted_size;
2205
2206         values = ldap_get_values(ads->ldap.ld, msg, field);
2207         if (!values)
2208                 return NULL;
2209
2210         *num_values = ldap_count_values(values);
2211
2212         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2213         if (!ret) {
2214                 ldap_value_free(values);
2215                 return NULL;
2216         }
2217
2218         for (i=0;i<*num_values;i++) {
2219                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2220                                       &converted_size))
2221                 {
2222                         ldap_value_free(values);
2223                         return NULL;
2224                 }
2225         }
2226         ret[i] = NULL;
2227
2228         ldap_value_free(values);
2229         return ret;
2230 }
2231
2232 /**
2233  * pull an array of strings from a ADS result 
2234  *  (handle large multivalue attributes with range retrieval)
2235  * @param ads connection to ads server
2236  * @param mem_ctx TALLOC_CTX to use for allocating result string
2237  * @param msg Results of search
2238  * @param field Attribute to retrieve
2239  * @param current_strings strings returned by a previous call to this function
2240  * @param next_attribute The next query should ask for this attribute
2241  * @param num_values How many values did we get this time?
2242  * @param more_values Are there more values to get?
2243  * @return Result strings in talloc context
2244  **/
2245  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2246                                TALLOC_CTX *mem_ctx,
2247                                LDAPMessage *msg, const char *field,
2248                                char **current_strings,
2249                                const char **next_attribute,
2250                                size_t *num_strings,
2251                                bool *more_strings)
2252 {
2253         char *attr;
2254         char *expected_range_attrib, *range_attr;
2255         BerElement *ptr = NULL;
2256         char **strings;
2257         char **new_strings;
2258         size_t num_new_strings;
2259         unsigned long int range_start;
2260         unsigned long int range_end;
2261         
2262         /* we might have been given the whole lot anyway */
2263         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2264                 *more_strings = False;
2265                 return strings;
2266         }
2267
2268         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2269
2270         /* look for Range result */
2271         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2272              attr; 
2273              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2274                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2275                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2276                         range_attr = attr;
2277                         break;
2278                 }
2279                 ldap_memfree(attr);
2280         }
2281         if (!attr) {
2282                 ber_free(ptr, 0);
2283                 /* nothing here - this field is just empty */
2284                 *more_strings = False;
2285                 return NULL;
2286         }
2287         
2288         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2289                    &range_start, &range_end) == 2) {
2290                 *more_strings = True;
2291         } else {
2292                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2293                            &range_start) == 1) {
2294                         *more_strings = False;
2295                 } else {
2296                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2297                                   range_attr));
2298                         ldap_memfree(range_attr);
2299                         *more_strings = False;
2300                         return NULL;
2301                 }
2302         }
2303
2304         if ((*num_strings) != range_start) {
2305                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2306                           " - aborting range retreival\n",
2307                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2308                 ldap_memfree(range_attr);
2309                 *more_strings = False;
2310                 return NULL;
2311         }
2312
2313         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2314         
2315         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2316                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2317                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2318                           range_attr, (unsigned long int)range_end - range_start + 1, 
2319                           (unsigned long int)num_new_strings));
2320                 ldap_memfree(range_attr);
2321                 *more_strings = False;
2322                 return NULL;
2323         }
2324
2325         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2326                                  *num_strings + num_new_strings);
2327         
2328         if (strings == NULL) {
2329                 ldap_memfree(range_attr);
2330                 *more_strings = False;
2331                 return NULL;
2332         }
2333         
2334         if (new_strings && num_new_strings) {
2335                 memcpy(&strings[*num_strings], new_strings,
2336                        sizeof(*new_strings) * num_new_strings);
2337         }
2338
2339         (*num_strings) += num_new_strings;
2340
2341         if (*more_strings) {
2342                 *next_attribute = talloc_asprintf(mem_ctx,
2343                                                   "%s;range=%d-*", 
2344                                                   field,
2345                                                   (int)*num_strings);
2346                 
2347                 if (!*next_attribute) {
2348                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2349                         ldap_memfree(range_attr);
2350                         *more_strings = False;
2351                         return NULL;
2352                 }
2353         }
2354
2355         ldap_memfree(range_attr);
2356
2357         return strings;
2358 }
2359
2360 /**
2361  * pull a single uint32 from a ADS result
2362  * @param ads connection to ads server
2363  * @param msg Results of search
2364  * @param field Attribute to retrieve
2365  * @param v Pointer to int to store result
2366  * @return boolean inidicating success
2367 */
2368  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2369                       uint32 *v)
2370 {
2371         char **values;
2372
2373         values = ldap_get_values(ads->ldap.ld, msg, field);
2374         if (!values)
2375                 return False;
2376         if (!values[0]) {
2377                 ldap_value_free(values);
2378                 return False;
2379         }
2380
2381         *v = atoi(values[0]);
2382         ldap_value_free(values);
2383         return True;
2384 }
2385
2386 /**
2387  * pull a single objectGUID from an ADS result
2388  * @param ads connection to ADS server
2389  * @param msg results of search
2390  * @param guid 37-byte area to receive text guid
2391  * @return boolean indicating success
2392  **/
2393  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2394 {
2395         char **values;
2396         UUID_FLAT flat_guid;
2397
2398         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2399         if (!values)
2400                 return False;
2401         
2402         if (values[0]) {
2403                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2404                 smb_uuid_unpack(flat_guid, guid);
2405                 ldap_value_free(values);
2406                 return True;
2407         }
2408         ldap_value_free(values);
2409         return False;
2410
2411 }
2412
2413
2414 /**
2415  * pull a single DOM_SID from a ADS result
2416  * @param ads connection to ads server
2417  * @param msg Results of search
2418  * @param field Attribute to retrieve
2419  * @param sid Pointer to sid to store result
2420  * @return boolean inidicating success
2421 */
2422  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2423                    DOM_SID *sid)
2424 {
2425         struct berval **values;
2426         bool ret = False;
2427
2428         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2429
2430         if (!values)
2431                 return False;
2432
2433         if (values[0])
2434                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2435         
2436         ldap_value_free_len(values);
2437         return ret;
2438 }
2439
2440 /**
2441  * pull an array of DOM_SIDs from a ADS result
2442  * @param ads connection to ads server
2443  * @param mem_ctx TALLOC_CTX for allocating sid array
2444  * @param msg Results of search
2445  * @param field Attribute to retrieve
2446  * @param sids pointer to sid array to allocate
2447  * @return the count of SIDs pulled
2448  **/
2449  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2450                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2451 {
2452         struct berval **values;
2453         bool ret;
2454         int count, i;
2455
2456         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2457
2458         if (!values)
2459                 return 0;
2460
2461         for (i=0; values[i]; i++)
2462                 /* nop */ ;
2463
2464         if (i) {
2465                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2466                 if (!(*sids)) {
2467                         ldap_value_free_len(values);
2468                         return 0;
2469                 }
2470         } else {
2471                 (*sids) = NULL;
2472         }
2473
2474         count = 0;
2475         for (i=0; values[i]; i++) {
2476                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2477                 if (ret) {
2478                         DEBUG(10, ("pulling SID: %s\n",
2479                                    sid_string_dbg(&(*sids)[count])));
2480                         count++;
2481                 }
2482         }
2483         
2484         ldap_value_free_len(values);
2485         return count;
2486 }
2487
2488 /**
2489  * pull a SEC_DESC from a ADS result
2490  * @param ads connection to ads server
2491  * @param mem_ctx TALLOC_CTX for allocating sid array
2492  * @param msg Results of search
2493  * @param field Attribute to retrieve
2494  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2495  * @return boolean inidicating success
2496 */
2497  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2498                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2499 {
2500         struct berval **values;
2501         bool ret = true;
2502
2503         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2504
2505         if (!values) return false;
2506
2507         if (values[0]) {
2508                 NTSTATUS status;
2509                 status = unmarshall_sec_desc(mem_ctx,
2510                                              (uint8 *)values[0]->bv_val,
2511                                              values[0]->bv_len, sd);
2512                 if (!NT_STATUS_IS_OK(status)) {
2513                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2514                                   nt_errstr(status)));
2515                         ret = false;
2516                 }
2517         }
2518         
2519         ldap_value_free_len(values);
2520         return ret;
2521 }
2522
2523 /* 
2524  * in order to support usernames longer than 21 characters we need to 
2525  * use both the sAMAccountName and the userPrincipalName attributes 
2526  * It seems that not all users have the userPrincipalName attribute set
2527  *
2528  * @param ads connection to ads server
2529  * @param mem_ctx TALLOC_CTX for allocating sid array
2530  * @param msg Results of search
2531  * @return the username
2532  */
2533  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2534                          LDAPMessage *msg)
2535 {
2536 #if 0   /* JERRY */
2537         char *ret, *p;
2538
2539         /* lookup_name() only works on the sAMAccountName to 
2540            returning the username portion of userPrincipalName
2541            breaks winbindd_getpwnam() */
2542
2543         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2544         if (ret && (p = strchr_m(ret, '@'))) {
2545                 *p = 0;
2546                 return ret;
2547         }
2548 #endif
2549         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2550 }
2551
2552
2553 /**
2554  * find the update serial number - this is the core of the ldap cache
2555  * @param ads connection to ads server
2556  * @param ads connection to ADS server
2557  * @param usn Pointer to retrieved update serial number
2558  * @return status of search
2559  **/
2560 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2561 {
2562         const char *attrs[] = {"highestCommittedUSN", NULL};
2563         ADS_STATUS status;
2564         LDAPMessage *res;
2565
2566         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2567         if (!ADS_ERR_OK(status)) 
2568                 return status;
2569
2570         if (ads_count_replies(ads, res) != 1) {
2571                 ads_msgfree(ads, res);
2572                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2573         }
2574
2575         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2576                 ads_msgfree(ads, res);
2577                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2578         }
2579
2580         ads_msgfree(ads, res);
2581         return ADS_SUCCESS;
2582 }
2583
2584 /* parse a ADS timestring - typical string is
2585    '20020917091222.0Z0' which means 09:12.22 17th September
2586    2002, timezone 0 */
2587 static time_t ads_parse_time(const char *str)
2588 {
2589         struct tm tm;
2590
2591         ZERO_STRUCT(tm);
2592
2593         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2594                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2595                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2596                 return 0;
2597         }
2598         tm.tm_year -= 1900;
2599         tm.tm_mon -= 1;
2600
2601         return timegm(&tm);
2602 }
2603
2604 /********************************************************************
2605 ********************************************************************/
2606
2607 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2608 {
2609         const char *attrs[] = {"currentTime", NULL};
2610         ADS_STATUS status;
2611         LDAPMessage *res;
2612         char *timestr;
2613         TALLOC_CTX *ctx;
2614         ADS_STRUCT *ads_s = ads;
2615
2616         if (!(ctx = talloc_init("ads_current_time"))) {
2617                 return ADS_ERROR(LDAP_NO_MEMORY);
2618         }
2619
2620         /* establish a new ldap tcp session if necessary */
2621
2622         if ( !ads->ldap.ld ) {
2623                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2624                         ads->server.ldap_server )) == NULL )
2625                 {
2626                         goto done;
2627                 }
2628                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2629                 status = ads_connect( ads_s );
2630                 if ( !ADS_ERR_OK(status))
2631                         goto done;
2632         }
2633
2634         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2635         if (!ADS_ERR_OK(status)) {
2636                 goto done;
2637         }
2638
2639         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2640         if (!timestr) {
2641                 ads_msgfree(ads_s, res);
2642                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2643                 goto done;
2644         }
2645
2646         /* but save the time and offset in the original ADS_STRUCT */   
2647         
2648         ads->config.current_time = ads_parse_time(timestr);
2649
2650         if (ads->config.current_time != 0) {
2651                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2652                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2653         }
2654
2655         ads_msgfree(ads, res);
2656
2657         status = ADS_SUCCESS;
2658
2659 done:
2660         /* free any temporary ads connections */
2661         if ( ads_s != ads ) {
2662                 ads_destroy( &ads_s );
2663         }
2664         talloc_destroy(ctx);
2665
2666         return status;
2667 }
2668
2669 /********************************************************************
2670 ********************************************************************/
2671
2672 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2673 {
2674         const char *attrs[] = {"domainFunctionality", NULL};
2675         ADS_STATUS status;
2676         LDAPMessage *res;
2677         ADS_STRUCT *ads_s = ads;
2678         
2679         *val = DS_DOMAIN_FUNCTION_2000;
2680
2681         /* establish a new ldap tcp session if necessary */
2682
2683         if ( !ads->ldap.ld ) {
2684                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2685                         ads->server.ldap_server )) == NULL )
2686                 {
2687                         goto done;
2688                 }
2689                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2690                 status = ads_connect( ads_s );
2691                 if ( !ADS_ERR_OK(status))
2692                         goto done;
2693         }
2694
2695         /* If the attribute does not exist assume it is a Windows 2000 
2696            functional domain */
2697            
2698         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2699         if (!ADS_ERR_OK(status)) {
2700                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2701                         status = ADS_SUCCESS;
2702                 }
2703                 goto done;
2704         }
2705
2706         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2707                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2708         }
2709         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2710
2711         
2712         ads_msgfree(ads, res);
2713
2714 done:
2715         /* free any temporary ads connections */
2716         if ( ads_s != ads ) {
2717                 ads_destroy( &ads_s );
2718         }
2719
2720         return status;
2721 }
2722
2723 /**
2724  * find the domain sid for our domain
2725  * @param ads connection to ads server
2726  * @param sid Pointer to domain sid
2727  * @return status of search
2728  **/
2729 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2730 {
2731         const char *attrs[] = {"objectSid", NULL};
2732         LDAPMessage *res;
2733         ADS_STATUS rc;
2734
2735         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2736                            attrs, &res);
2737         if (!ADS_ERR_OK(rc)) return rc;
2738         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2739                 ads_msgfree(ads, res);
2740                 return ADS_ERROR_SYSTEM(ENOENT);
2741         }
2742         ads_msgfree(ads, res);
2743         
2744         return ADS_SUCCESS;
2745 }
2746
2747 /**
2748  * find our site name 
2749  * @param ads connection to ads server
2750  * @param mem_ctx Pointer to talloc context
2751  * @param site_name Pointer to the sitename
2752  * @return status of search
2753  **/
2754 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2755 {
2756         ADS_STATUS status;
2757         LDAPMessage *res;
2758         const char *dn, *service_name;
2759         const char *attrs[] = { "dsServiceName", NULL };
2760
2761         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2762         if (!ADS_ERR_OK(status)) {
2763                 return status;
2764         }
2765
2766         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2767         if (service_name == NULL) {
2768                 ads_msgfree(ads, res);
2769                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2770         }
2771
2772         ads_msgfree(ads, res);
2773
2774         /* go up three levels */
2775         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2776         if (dn == NULL) {
2777                 return ADS_ERROR(LDAP_NO_MEMORY);
2778         }
2779
2780         *site_name = talloc_strdup(mem_ctx, dn);
2781         if (*site_name == NULL) {
2782                 return ADS_ERROR(LDAP_NO_MEMORY);
2783         }
2784
2785         return status;
2786         /*
2787         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2788         */                                               
2789 }
2790
2791 /**
2792  * find the site dn where a machine resides
2793  * @param ads connection to ads server
2794  * @param mem_ctx Pointer to talloc context
2795  * @param computer_name name of the machine
2796  * @param site_name Pointer to the sitename
2797  * @return status of search
2798  **/
2799 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2800 {
2801         ADS_STATUS status;
2802         LDAPMessage *res;
2803         const char *parent, *filter;
2804         char *config_context = NULL;
2805         char *dn;
2806
2807         /* shortcut a query */
2808         if (strequal(computer_name, ads->config.ldap_server_name)) {
2809                 return ads_site_dn(ads, mem_ctx, site_dn);
2810         }
2811
2812         status = ads_config_path(ads, mem_ctx, &config_context);
2813         if (!ADS_ERR_OK(status)) {
2814                 return status;
2815         }
2816
2817         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2818         if (filter == NULL) {
2819                 return ADS_ERROR(LDAP_NO_MEMORY);
2820         }
2821
2822         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
2823                                filter, NULL, &res);
2824         if (!ADS_ERR_OK(status)) {
2825                 return status;
2826         }
2827
2828         if (ads_count_replies(ads, res) != 1) {
2829                 ads_msgfree(ads, res);
2830                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2831         }
2832
2833         dn = ads_get_dn(ads, res);
2834         if (dn == NULL) {
2835                 ads_msgfree(ads, res);
2836                 return ADS_ERROR(LDAP_NO_MEMORY);
2837         }
2838
2839         /* go up three levels */
2840         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2841         if (parent == NULL) {
2842                 ads_msgfree(ads, res);
2843                 ads_memfree(ads, dn);
2844                 return ADS_ERROR(LDAP_NO_MEMORY);
2845         }
2846
2847         *site_dn = talloc_strdup(mem_ctx, parent);
2848         if (*site_dn == NULL) {
2849                 ads_msgfree(ads, res);
2850                 ads_memfree(ads, dn);
2851                 return ADS_ERROR(LDAP_NO_MEMORY);
2852         }
2853
2854         ads_memfree(ads, dn);
2855         ads_msgfree(ads, res);
2856
2857         return status;
2858 }
2859
2860 /**
2861  * get the upn suffixes for a domain
2862  * @param ads connection to ads server
2863  * @param mem_ctx Pointer to talloc context
2864  * @param suffixes Pointer to an array of suffixes
2865  * @param num_suffixes Pointer to the number of suffixes
2866  * @return status of search
2867  **/
2868 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2869 {
2870         ADS_STATUS status;
2871         LDAPMessage *res;
2872         const char *base;
2873         char *config_context = NULL;
2874         const char *attrs[] = { "uPNSuffixes", NULL };
2875
2876         status = ads_config_path(ads, mem_ctx, &config_context);
2877         if (!ADS_ERR_OK(status)) {
2878                 return status;
2879         }
2880
2881         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2882         if (base == NULL) {
2883                 return ADS_ERROR(LDAP_NO_MEMORY);
2884         }
2885
2886         status = ads_search_dn(ads, &res, base, attrs);
2887         if (!ADS_ERR_OK(status)) {
2888                 return status;
2889         }
2890
2891         if (ads_count_replies(ads, res) != 1) {
2892                 ads_msgfree(ads, res);
2893                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2894         }
2895
2896         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2897         if ((*suffixes) == NULL) {
2898                 ads_msgfree(ads, res);
2899                 return ADS_ERROR(LDAP_NO_MEMORY);
2900         }
2901
2902         ads_msgfree(ads, res);
2903
2904         return status;
2905 }
2906
2907 /**
2908  * get the joinable ous for a domain
2909  * @param ads connection to ads server
2910  * @param mem_ctx Pointer to talloc context
2911  * @param ous Pointer to an array of ous
2912  * @param num_ous Pointer to the number of ous
2913  * @return status of search
2914  **/
2915 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2916                                 TALLOC_CTX *mem_ctx,
2917                                 char ***ous,
2918                                 size_t *num_ous)
2919 {
2920         ADS_STATUS status;
2921         LDAPMessage *res = NULL;
2922         LDAPMessage *msg = NULL;
2923         const char *attrs[] = { "dn", NULL };
2924         int count = 0;
2925
2926         status = ads_search(ads, &res,
2927                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
2928                             attrs);
2929         if (!ADS_ERR_OK(status)) {
2930                 return status;
2931         }
2932
2933         count = ads_count_replies(ads, res);
2934         if (count < 1) {
2935                 ads_msgfree(ads, res);
2936                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2937         }
2938
2939         for (msg = ads_first_entry(ads, res); msg;
2940              msg = ads_next_entry(ads, msg)) {
2941
2942                 char *dn = NULL;
2943
2944                 dn = ads_get_dn(ads, msg);
2945                 if (!dn) {
2946                         ads_msgfree(ads, res);
2947                         return ADS_ERROR(LDAP_NO_MEMORY);
2948                 }
2949
2950                 if (!add_string_to_array(mem_ctx, dn,
2951                                          (const char ***)ous,
2952                                          (int *)num_ous)) {
2953                         ads_memfree(ads, dn);
2954                         ads_msgfree(ads, res);
2955                         return ADS_ERROR(LDAP_NO_MEMORY);
2956                 }
2957
2958                 ads_memfree(ads, dn);
2959         }
2960
2961         ads_msgfree(ads, res);
2962
2963         return status;
2964 }
2965
2966
2967 /**
2968  * pull a DOM_SID from an extended dn string
2969  * @param mem_ctx TALLOC_CTX 
2970  * @param extended_dn string
2971  * @param flags string type of extended_dn
2972  * @param sid pointer to a DOM_SID
2973  * @return boolean inidicating success
2974  **/
2975 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2976                                   const char *extended_dn, 
2977                                   enum ads_extended_dn_flags flags, 
2978                                   DOM_SID *sid)
2979 {
2980         char *p, *q, *dn;
2981
2982         if (!extended_dn) {
2983                 return False;
2984         }
2985
2986         /* otherwise extended_dn gets stripped off */
2987         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2988                 return False;
2989         }
2990         /* 
2991          * ADS_EXTENDED_DN_HEX_STRING:
2992          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2993          *
2994          * ADS_EXTENDED_DN_STRING (only with w2k3):
2995         <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
2996          */
2997
2998         p = strchr(dn, ';');
2999         if (!p) {
3000                 return False;
3001         }
3002
3003         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3004                 return False;
3005         }
3006
3007         p += strlen(";<SID=");
3008
3009         q = strchr(p, '>');
3010         if (!q) {
3011                 return False;
3012         }
3013         
3014         *q = '\0';
3015
3016         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3017
3018         switch (flags) {
3019         
3020         case ADS_EXTENDED_DN_STRING:
3021                 if (!string_to_sid(sid, p)) {
3022                         return False;
3023                 }
3024                 break;
3025         case ADS_EXTENDED_DN_HEX_STRING: {
3026                 fstring buf;
3027                 size_t buf_len;
3028
3029                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3030                 if (buf_len == 0) {
3031                         return False;
3032                 }
3033
3034                 if (!sid_parse(buf, buf_len, sid)) {
3035                         DEBUG(10,("failed to parse sid\n"));
3036                         return False;
3037                 }
3038                 break;
3039                 }
3040         default:
3041                 DEBUG(10,("unknown extended dn format\n"));
3042                 return False;
3043         }
3044
3045         return True;
3046 }
3047
3048 /**
3049  * pull an array of DOM_SIDs from a ADS result
3050  * @param ads connection to ads server
3051  * @param mem_ctx TALLOC_CTX for allocating sid array
3052  * @param msg Results of search
3053  * @param field Attribute to retrieve
3054  * @param flags string type of extended_dn
3055  * @param sids pointer to sid array to allocate
3056  * @return the count of SIDs pulled
3057  **/
3058  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
3059                                    TALLOC_CTX *mem_ctx, 
3060                                    LDAPMessage *msg, 
3061                                    const char *field,
3062                                    enum ads_extended_dn_flags flags,
3063                                    DOM_SID **sids)
3064 {
3065         int i;
3066         size_t dn_count;
3067         char **dn_strings;
3068
3069         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
3070                                            &dn_count)) == NULL) {
3071                 return 0;
3072         }
3073
3074         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3075         if (!(*sids)) {
3076                 TALLOC_FREE(dn_strings);
3077                 return 0;
3078         }
3079
3080         for (i=0; i<dn_count; i++) {
3081
3082                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
3083                                                   flags, &(*sids)[i])) {
3084                         TALLOC_FREE(*sids);
3085                         TALLOC_FREE(dn_strings);
3086                         return 0;
3087                 }
3088         }
3089
3090         TALLOC_FREE(dn_strings);
3091
3092         return dn_count;
3093 }
3094
3095 /********************************************************************
3096 ********************************************************************/
3097
3098 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3099 {
3100         LDAPMessage *res = NULL;
3101         ADS_STATUS status;
3102         int count = 0;
3103         char *name = NULL;
3104         
3105         status = ads_find_machine_acct(ads, &res, global_myname());
3106         if (!ADS_ERR_OK(status)) {
3107                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3108                         global_myname()));
3109                 goto out;
3110         }
3111                 
3112         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3113                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3114                 goto out;
3115         }
3116                 
3117         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3118                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3119         }
3120
3121 out:
3122         ads_msgfree(ads, res);
3123         
3124         return name;
3125 }
3126
3127 /********************************************************************
3128 ********************************************************************/
3129
3130 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3131 {
3132         LDAPMessage *res = NULL;
3133         ADS_STATUS status;
3134         int count = 0;
3135         char *name = NULL;
3136
3137         status = ads_find_machine_acct(ads, &res, machine_name);
3138         if (!ADS_ERR_OK(status)) {
3139                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3140                         global_myname()));
3141                 goto out;
3142         }
3143
3144         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3145                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3146                 goto out;
3147         }
3148
3149         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3150                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3151         }
3152
3153 out:
3154         ads_msgfree(ads, res);
3155
3156         return name;
3157 }
3158
3159 /********************************************************************
3160 ********************************************************************/
3161
3162 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3163 {
3164         LDAPMessage *res = NULL;
3165         ADS_STATUS status;
3166         int count = 0;
3167         char *name = NULL;
3168         
3169         status = ads_find_machine_acct(ads, &res, global_myname());
3170         if (!ADS_ERR_OK(status)) {
3171                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3172                         global_myname()));
3173                 goto out;
3174         }
3175                 
3176         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3177                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3178                 goto out;
3179         }
3180                 
3181         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3182                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3183         }
3184
3185 out:
3186         ads_msgfree(ads, res);
3187         
3188         return name;
3189 }
3190
3191 #if 0
3192
3193    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3194
3195 /**
3196  * Join a machine to a realm
3197  *  Creates the machine account and sets the machine password
3198  * @param ads connection to ads server
3199  * @param machine name of host to add
3200  * @param org_unit Organizational unit to place machine in
3201  * @return status of join
3202  **/
3203 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3204                         uint32 account_type, const char *org_unit)
3205 {
3206         ADS_STATUS status;
3207         LDAPMessage *res = NULL;
3208         char *machine;
3209
3210         /* machine name must be lowercase */
3211         machine = SMB_STRDUP(machine_name);
3212         strlower_m(machine);
3213
3214         /*
3215         status = ads_find_machine_acct(ads, (void **)&res, machine);
3216         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3217                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3218                 status = ads_leave_realm(ads, machine);
3219                 if (!ADS_ERR_OK(status)) {
3220                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3221                                 machine, ads->config.realm));
3222                         return status;
3223                 }
3224         }
3225         */
3226         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3227         if (!ADS_ERR_OK(status)) {
3228                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3229                 SAFE_FREE(machine);
3230                 return status;
3231         }
3232
3233         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3234         if (!ADS_ERR_OK(status)) {
3235                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3236                 SAFE_FREE(machine);
3237                 return status;
3238         }
3239
3240         SAFE_FREE(machine);
3241         ads_msgfree(ads, res);
3242
3243         return status;
3244 }
3245 #endif
3246
3247 /**
3248  * Delete a machine from the realm
3249  * @param ads connection to ads server
3250  * @param hostname Machine to remove
3251  * @return status of delete
3252  **/
3253 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3254 {
3255         ADS_STATUS status;
3256         void *msg;
3257         LDAPMessage *res;
3258         char *hostnameDN, *host;
3259         int rc;
3260         LDAPControl ldap_control;
3261         LDAPControl  * pldap_control[2] = {NULL, NULL};
3262
3263         pldap_control[0] = &ldap_control;
3264         memset(&ldap_control, 0, sizeof(LDAPControl));
3265         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3266
3267         /* hostname must be lowercase */
3268         host = SMB_STRDUP(hostname);
3269         strlower_m(host);
3270
3271         status = ads_find_machine_acct(ads, &res, host);
3272         if (!ADS_ERR_OK(status)) {
3273                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3274                 SAFE_FREE(host);
3275                 return status;
3276         }
3277
3278         msg = ads_first_entry(ads, res);
3279         if (!msg) {
3280                 SAFE_FREE(host);
3281                 return ADS_ERROR_SYSTEM(ENOENT);
3282         }
3283
3284         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3285
3286         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3287         if (rc) {
3288                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3289         }else {
3290                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3291         }
3292
3293         if (rc != LDAP_SUCCESS) {
3294                 const char *attrs[] = { "cn", NULL };
3295                 LDAPMessage *msg_sub;
3296
3297                 /* we only search with scope ONE, we do not expect any further
3298                  * objects to be created deeper */
3299
3300                 status = ads_do_search_retry(ads, hostnameDN,
3301                                              LDAP_SCOPE_ONELEVEL,
3302                                              "(objectclass=*)", attrs, &res);
3303
3304                 if (!ADS_ERR_OK(status)) {
3305                         SAFE_FREE(host);
3306                         ads_memfree(ads, hostnameDN);
3307                         return status;
3308                 }
3309
3310                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3311                         msg_sub = ads_next_entry(ads, msg_sub)) {
3312
3313                         char *dn = NULL;
3314
3315                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3316                                 SAFE_FREE(host);
3317                                 ads_memfree(ads, hostnameDN);
3318                                 return ADS_ERROR(LDAP_NO_MEMORY);
3319                         }
3320
3321                         status = ads_del_dn(ads, dn);
3322                         if (!ADS_ERR_OK(status)) {
3323                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3324                                 SAFE_FREE(host);
3325                                 ads_memfree(ads, dn);
3326                                 ads_memfree(ads, hostnameDN);
3327                                 return status;
3328                         }
3329
3330                         ads_memfree(ads, dn);
3331                 }
3332
3333                 /* there should be no subordinate objects anymore */
3334                 status = ads_do_search_retry(ads, hostnameDN,
3335                                              LDAP_SCOPE_ONELEVEL,
3336                                              "(objectclass=*)", attrs, &res);
3337
3338                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3339                         SAFE_FREE(host);
3340                         ads_memfree(ads, hostnameDN);
3341                         return status;
3342                 }
3343
3344                 /* delete hostnameDN now */
3345                 status = ads_del_dn(ads, hostnameDN);
3346                 if (!ADS_ERR_OK(status)) {
3347                         SAFE_FREE(host);
3348                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3349                         ads_memfree(ads, hostnameDN);
3350                         return status;
3351                 }
3352         }
3353
3354         ads_memfree(ads, hostnameDN);
3355
3356         status = ads_find_machine_acct(ads, &res, host);
3357         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3358                 DEBUG(3, ("Failed to remove host account.\n"));
3359                 SAFE_FREE(host);
3360                 return status;
3361         }
3362
3363         SAFE_FREE(host);
3364         return status;
3365 }
3366
3367 /**
3368  * pull all token-sids from an LDAP dn
3369  * @param ads connection to ads server
3370  * @param mem_ctx TALLOC_CTX for allocating sid array
3371  * @param dn of LDAP object
3372  * @param user_sid pointer to DOM_SID (objectSid)
3373  * @param primary_group_sid pointer to DOM_SID (self composed)
3374  * @param sids pointer to sid array to allocate
3375  * @param num_sids counter of SIDs pulled
3376  * @return status of token query
3377  **/
3378  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3379                               TALLOC_CTX *mem_ctx,
3380                               const char *dn,
3381                               DOM_SID *user_sid,
3382                               DOM_SID *primary_group_sid,
3383                               DOM_SID **sids,
3384                               size_t *num_sids)
3385 {
3386         ADS_STATUS status;
3387         LDAPMessage *res = NULL;
3388         int count = 0;
3389         size_t tmp_num_sids;
3390         DOM_SID *tmp_sids;
3391         DOM_SID tmp_user_sid;
3392         DOM_SID tmp_primary_group_sid;
3393         uint32 pgid;
3394         const char *attrs[] = {
3395                 "objectSid",
3396                 "tokenGroups",
3397                 "primaryGroupID",
3398                 NULL
3399         };
3400
3401         status = ads_search_retry_dn(ads, &res, dn, attrs);
3402         if (!ADS_ERR_OK(status)) {
3403                 return status;
3404         }
3405
3406         count = ads_count_replies(ads, res);
3407         if (count != 1) {
3408                 ads_msgfree(ads, res);
3409                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3410         }
3411
3412         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3413                 ads_msgfree(ads, res);
3414                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3415         }
3416
3417         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3418                 ads_msgfree(ads, res);
3419                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3420         }
3421
3422         {
3423                 /* hack to compose the primary group sid without knowing the
3424                  * domsid */
3425
3426                 DOM_SID domsid;
3427                 uint32 dummy_rid;
3428
3429                 sid_copy(&domsid, &tmp_user_sid);
3430
3431                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3432                         ads_msgfree(ads, res);
3433                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3434                 }
3435
3436                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3437                         ads_msgfree(ads, res);
3438                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3439                 }
3440         }
3441
3442         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3443
3444         if (tmp_num_sids == 0 || !tmp_sids) {
3445                 ads_msgfree(ads, res);
3446                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3447         }
3448
3449         if (num_sids) {
3450                 *num_sids = tmp_num_sids;
3451         }
3452
3453         if (sids) {
3454                 *sids = tmp_sids;
3455         }
3456
3457         if (user_sid) {
3458                 *user_sid = tmp_user_sid;
3459         }
3460
3461         if (primary_group_sid) {
3462                 *primary_group_sid = tmp_primary_group_sid;
3463         }
3464
3465         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3466
3467         ads_msgfree(ads, res);
3468         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3469 }
3470
3471 /**
3472  * Find a sAMAccoutName in LDAP
3473  * @param ads connection to ads server
3474  * @param mem_ctx TALLOC_CTX for allocating sid array
3475  * @param samaccountname to search
3476  * @param uac_ret uint32 pointer userAccountControl attribute value
3477  * @param dn_ret pointer to dn
3478  * @return status of token query
3479  **/
3480 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3481                                TALLOC_CTX *mem_ctx,
3482                                const char *samaccountname,
3483                                uint32 *uac_ret,
3484                                const char **dn_ret)
3485 {
3486         ADS_STATUS status;
3487         const char *attrs[] = { "userAccountControl", NULL };
3488         const char *filter;
3489         LDAPMessage *res = NULL;
3490         char *dn = NULL;
3491         uint32 uac = 0;
3492
3493         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3494                 samaccountname);
3495         if (filter == NULL) {
3496                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3497                 goto out;
3498         }
3499
3500         status = ads_do_search_all(ads, ads->config.bind_path,
3501                                    LDAP_SCOPE_SUBTREE,
3502                                    filter, attrs, &res);
3503         
3504         if (!ADS_ERR_OK(status)) {
3505                 goto out;
3506         }
3507
3508         if (ads_count_replies(ads, res) != 1) {
3509                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3510                 goto out;
3511         }
3512
3513         dn = ads_get_dn(ads, res);
3514         if (dn == NULL) {
3515                 status = ADS_ERROR(LDAP_NO_MEMORY);
3516                 goto out;
3517         }
3518
3519         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3520                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3521                 goto out;
3522         }
3523
3524         if (uac_ret) {
3525                 *uac_ret = uac;
3526         }
3527
3528         if (dn_ret) {
3529                 *dn_ret = talloc_strdup(mem_ctx, dn);
3530                 if (!*dn_ret) {
3531                         status = ADS_ERROR(LDAP_NO_MEMORY);
3532                         goto out;
3533                 }
3534         }
3535  out:
3536         ads_memfree(ads, dn);
3537         ads_msgfree(ads, res);
3538
3539         return status;
3540 }
3541
3542 /**
3543  * find our configuration path 
3544  * @param ads connection to ads server
3545  * @param mem_ctx Pointer to talloc context
3546  * @param config_path Pointer to the config path
3547  * @return status of search
3548  **/
3549 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3550                            TALLOC_CTX *mem_ctx, 
3551                            char **config_path)
3552 {
3553         ADS_STATUS status;
3554         LDAPMessage *res = NULL;
3555         const char *config_context = NULL;
3556         const char *attrs[] = { "configurationNamingContext", NULL };
3557
3558         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3559                                "(objectclass=*)", attrs, &res);
3560         if (!ADS_ERR_OK(status)) {
3561                 return status;
3562         }
3563
3564         config_context = ads_pull_string(ads, mem_ctx, res, 
3565                                          "configurationNamingContext");
3566         ads_msgfree(ads, res);
3567         if (!config_context) {
3568                 return ADS_ERROR(LDAP_NO_MEMORY);
3569         }
3570
3571         if (config_path) {
3572                 *config_path = talloc_strdup(mem_ctx, config_context);
3573                 if (!*config_path) {
3574                         return ADS_ERROR(LDAP_NO_MEMORY);
3575                 }
3576         }
3577
3578         return ADS_ERROR(LDAP_SUCCESS);
3579 }
3580
3581 /**
3582  * find the displayName of an extended right 
3583  * @param ads connection to ads server
3584  * @param config_path The config path
3585  * @param mem_ctx Pointer to talloc context
3586  * @param GUID struct of the rightsGUID
3587  * @return status of search
3588  **/
3589 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3590                                                 const char *config_path, 
3591                                                 TALLOC_CTX *mem_ctx, 
3592                                                 const struct GUID *rights_guid)
3593 {
3594         ADS_STATUS rc;
3595         LDAPMessage *res = NULL;
3596         char *expr = NULL;
3597         const char *attrs[] = { "displayName", NULL };
3598         const char *result = NULL;
3599         const char *path;
3600
3601         if (!ads || !mem_ctx || !rights_guid) {
3602                 goto done;
3603         }
3604
3605         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3606                                smb_uuid_string(mem_ctx, *rights_guid));
3607         if (!expr) {
3608                 goto done;
3609         }
3610
3611         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3612         if (!path) {
3613                 goto done;
3614         }
3615
3616         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3617                                  expr, attrs, &res);
3618         if (!ADS_ERR_OK(rc)) {
3619                 goto done;
3620         }
3621
3622         if (ads_count_replies(ads, res) != 1) {
3623                 goto done;
3624         }
3625
3626         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3627
3628  done:
3629         ads_msgfree(ads, res);
3630         return result;
3631         
3632 }
3633
3634 /**
3635  * verify or build and verify an account ou
3636  * @param mem_ctx Pointer to talloc context
3637  * @param ads connection to ads server
3638  * @param account_ou
3639  * @return status of search
3640  **/
3641
3642 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3643                            ADS_STRUCT *ads,
3644                            const char **account_ou)
3645 {
3646         struct ldb_dn *name_dn = NULL;
3647         const char *name = NULL;
3648         char *ou_string = NULL;
3649
3650         name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3651         if (name_dn) {
3652                 return ADS_SUCCESS;
3653         }
3654
3655         ou_string = ads_ou_string(ads, *account_ou);
3656         if (!ou_string) {
3657                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3658         }
3659
3660         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3661                                ads->config.bind_path);
3662         SAFE_FREE(ou_string);
3663         if (!name) {
3664                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3665         }
3666
3667         name_dn = ldb_dn_explode(mem_ctx, name);
3668         if (!name_dn) {
3669                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3670         }
3671
3672         *account_ou = talloc_strdup(mem_ctx, name);
3673         if (!*account_ou) {
3674                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3675         }
3676
3677         return ADS_SUCCESS;
3678 }
3679
3680 #endif