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