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