99df4ed8a331c398dc2648ec902db09e9fcc63cb
[samba.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7    Copyright (C) Guenther Deschner 2005
8    Copyright (C) Gerald Carter 2006
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "lib/ldb/include/includes.h"
26
27 #ifdef HAVE_LDAP
28
29 /**
30  * @file ldap.c
31  * @brief basic ldap client-side routines for ads server communications
32  *
33  * The routines contained here should do the necessary ldap calls for
34  * ads setups.
35  * 
36  * Important note: attribute names passed into ads_ routines must
37  * already be in UTF-8 format.  We do not convert them because in almost
38  * all cases, they are just ascii (which is represented with the same
39  * codepoints in UTF-8).  This may have to change at some point
40  **/
41
42
43 #define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
44
45 static SIG_ATOMIC_T gotalarm;
46
47 /***************************************************************
48  Signal function to tell us we timed out.
49 ****************************************************************/
50
51 static void gotalarm_sig(void)
52 {
53         gotalarm = 1;
54 }
55
56  LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 {
58         LDAP *ldp = NULL;
59
60
61         DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62                    "%u seconds\n", server, port, to));
63
64         /* Setup timeout */
65         gotalarm = 0;
66         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67         alarm(to);
68         /* End setup timeout. */
69
70         ldp = ldap_open(server, port);
71
72         if (ldp == NULL) {
73                 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74                          server, port, strerror(errno)));
75         } else {
76                 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
77         }
78
79         /* Teardown timeout. */
80         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81         alarm(0);
82
83         return ldp;
84 }
85
86 static int ldap_search_with_timeout(LDAP *ld,
87                                     LDAP_CONST char *base,
88                                     int scope,
89                                     LDAP_CONST char *filter,
90                                     char **attrs,
91                                     int attrsonly,
92                                     LDAPControl **sctrls,
93                                     LDAPControl **cctrls,
94                                     int sizelimit,
95                                     LDAPMessage **res )
96 {
97         struct timeval timeout;
98         int result;
99
100         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101         timeout.tv_sec = lp_ldap_timeout();
102         timeout.tv_usec = 0;
103
104         /* Setup alarm timeout.... Do we need both of these ? JRA. */
105         gotalarm = 0;
106         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107         alarm(lp_ldap_timeout());
108         /* End setup timeout. */
109
110         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111                                    attrsonly, sctrls, cctrls, &timeout,
112                                    sizelimit, res);
113
114         /* Teardown timeout. */
115         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116         alarm(0);
117
118         if (gotalarm != 0)
119                 return LDAP_TIMELIMIT_EXCEEDED;
120
121         return result;
122 }
123
124 /**********************************************
125  Do client and server sitename match ?
126 **********************************************/
127
128 bool ads_sitename_match(ADS_STRUCT *ads)
129 {
130         if (ads->config.server_site_name == NULL &&
131             ads->config.client_site_name == NULL ) {
132                 DEBUG(10,("ads_sitename_match: both null\n"));
133                 return True;
134         }
135         if (ads->config.server_site_name &&
136             ads->config.client_site_name &&
137             strequal(ads->config.server_site_name,
138                      ads->config.client_site_name)) {
139                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140                 return True;
141         }
142         DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145         return False;
146 }
147
148 /**********************************************
149  Is this the closest DC ?
150 **********************************************/
151
152 bool ads_closest_dc(ADS_STRUCT *ads)
153 {
154         if (ads->config.flags & ADS_CLOSEST) {
155                 DEBUG(10,("ads_closest_dc: ADS_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: ADS_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(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 & ADS_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_entry(ads, res2); msg; msg = next) {
874                         next = ads_next_entry(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 a single string from a ADS result
2095  * @param ads connection to ads server
2096  * @param mem_ctx TALLOC_CTX to use for allocating result string
2097  * @param msg Results of search
2098  * @param field Attribute to retrieve
2099  * @return Result string in talloc context
2100  **/
2101  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2102                        const char *field)
2103 {
2104         char **values;
2105         char *ret = NULL;
2106         char *ux_string;
2107         size_t rc;
2108
2109         values = ldap_get_values(ads->ldap.ld, msg, field);
2110         if (!values)
2111                 return NULL;
2112         
2113         if (values[0]) {
2114                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
2115                                       values[0]);
2116                 if (rc != (size_t)-1)
2117                         ret = ux_string;
2118                 
2119         }
2120         ldap_value_free(values);
2121         return ret;
2122 }
2123
2124 /**
2125  * pull an array of strings from a ADS result
2126  * @param ads connection to ads server
2127  * @param mem_ctx TALLOC_CTX to use for allocating result string
2128  * @param msg Results of search
2129  * @param field Attribute to retrieve
2130  * @return Result strings in talloc context
2131  **/
2132  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2133                          LDAPMessage *msg, const char *field,
2134                          size_t *num_values)
2135 {
2136         char **values;
2137         char **ret = NULL;
2138         int i;
2139
2140         values = ldap_get_values(ads->ldap.ld, msg, field);
2141         if (!values)
2142                 return NULL;
2143
2144         *num_values = ldap_count_values(values);
2145
2146         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2147         if (!ret) {
2148                 ldap_value_free(values);
2149                 return NULL;
2150         }
2151
2152         for (i=0;i<*num_values;i++) {
2153                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2154                         ldap_value_free(values);
2155                         return NULL;
2156                 }
2157         }
2158         ret[i] = NULL;
2159
2160         ldap_value_free(values);
2161         return ret;
2162 }
2163
2164 /**
2165  * pull an array of strings from a ADS result 
2166  *  (handle large multivalue attributes with range retrieval)
2167  * @param ads connection to ads server
2168  * @param mem_ctx TALLOC_CTX to use for allocating result string
2169  * @param msg Results of search
2170  * @param field Attribute to retrieve
2171  * @param current_strings strings returned by a previous call to this function
2172  * @param next_attribute The next query should ask for this attribute
2173  * @param num_values How many values did we get this time?
2174  * @param more_values Are there more values to get?
2175  * @return Result strings in talloc context
2176  **/
2177  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2178                                TALLOC_CTX *mem_ctx,
2179                                LDAPMessage *msg, const char *field,
2180                                char **current_strings,
2181                                const char **next_attribute,
2182                                size_t *num_strings,
2183                                bool *more_strings)
2184 {
2185         char *attr;
2186         char *expected_range_attrib, *range_attr;
2187         BerElement *ptr = NULL;
2188         char **strings;
2189         char **new_strings;
2190         size_t num_new_strings;
2191         unsigned long int range_start;
2192         unsigned long int range_end;
2193         
2194         /* we might have been given the whole lot anyway */
2195         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2196                 *more_strings = False;
2197                 return strings;
2198         }
2199
2200         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2201
2202         /* look for Range result */
2203         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2204              attr; 
2205              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2206                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2207                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2208                         range_attr = attr;
2209                         break;
2210                 }
2211                 ldap_memfree(attr);
2212         }
2213         if (!attr) {
2214                 ber_free(ptr, 0);
2215                 /* nothing here - this field is just empty */
2216                 *more_strings = False;
2217                 return NULL;
2218         }
2219         
2220         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2221                    &range_start, &range_end) == 2) {
2222                 *more_strings = True;
2223         } else {
2224                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2225                            &range_start) == 1) {
2226                         *more_strings = False;
2227                 } else {
2228                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2229                                   range_attr));
2230                         ldap_memfree(range_attr);
2231                         *more_strings = False;
2232                         return NULL;
2233                 }
2234         }
2235
2236         if ((*num_strings) != range_start) {
2237                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2238                           " - aborting range retreival\n",
2239                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2240                 ldap_memfree(range_attr);
2241                 *more_strings = False;
2242                 return NULL;
2243         }
2244
2245         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2246         
2247         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2248                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2249                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2250                           range_attr, (unsigned long int)range_end - range_start + 1, 
2251                           (unsigned long int)num_new_strings));
2252                 ldap_memfree(range_attr);
2253                 *more_strings = False;
2254                 return NULL;
2255         }
2256
2257         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2258                                  *num_strings + num_new_strings);
2259         
2260         if (strings == NULL) {
2261                 ldap_memfree(range_attr);
2262                 *more_strings = False;
2263                 return NULL;
2264         }
2265         
2266         if (new_strings && num_new_strings) {
2267                 memcpy(&strings[*num_strings], new_strings,
2268                        sizeof(*new_strings) * num_new_strings);
2269         }
2270
2271         (*num_strings) += num_new_strings;
2272
2273         if (*more_strings) {
2274                 *next_attribute = talloc_asprintf(mem_ctx,
2275                                                   "%s;range=%d-*", 
2276                                                   field,
2277                                                   (int)*num_strings);
2278                 
2279                 if (!*next_attribute) {
2280                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2281                         ldap_memfree(range_attr);
2282                         *more_strings = False;
2283                         return NULL;
2284                 }
2285         }
2286
2287         ldap_memfree(range_attr);
2288
2289         return strings;
2290 }
2291
2292 /**
2293  * pull a single uint32 from a ADS result
2294  * @param ads connection to ads server
2295  * @param msg Results of search
2296  * @param field Attribute to retrieve
2297  * @param v Pointer to int to store result
2298  * @return boolean inidicating success
2299 */
2300  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2301                       uint32 *v)
2302 {
2303         char **values;
2304
2305         values = ldap_get_values(ads->ldap.ld, msg, field);
2306         if (!values)
2307                 return False;
2308         if (!values[0]) {
2309                 ldap_value_free(values);
2310                 return False;
2311         }
2312
2313         *v = atoi(values[0]);
2314         ldap_value_free(values);
2315         return True;
2316 }
2317
2318 /**
2319  * pull a single objectGUID from an ADS result
2320  * @param ads connection to ADS server
2321  * @param msg results of search
2322  * @param guid 37-byte area to receive text guid
2323  * @return boolean indicating success
2324  **/
2325  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2326 {
2327         char **values;
2328         UUID_FLAT flat_guid;
2329
2330         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2331         if (!values)
2332                 return False;
2333         
2334         if (values[0]) {
2335                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2336                 smb_uuid_unpack(flat_guid, guid);
2337                 ldap_value_free(values);
2338                 return True;
2339         }
2340         ldap_value_free(values);
2341         return False;
2342
2343 }
2344
2345
2346 /**
2347  * pull a single DOM_SID from a ADS result
2348  * @param ads connection to ads server
2349  * @param msg Results of search
2350  * @param field Attribute to retrieve
2351  * @param sid Pointer to sid to store result
2352  * @return boolean inidicating success
2353 */
2354  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2355                    DOM_SID *sid)
2356 {
2357         struct berval **values;
2358         bool ret = False;
2359
2360         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2361
2362         if (!values)
2363                 return False;
2364
2365         if (values[0])
2366                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2367         
2368         ldap_value_free_len(values);
2369         return ret;
2370 }
2371
2372 /**
2373  * pull an array of DOM_SIDs from a ADS result
2374  * @param ads connection to ads server
2375  * @param mem_ctx TALLOC_CTX for allocating sid array
2376  * @param msg Results of search
2377  * @param field Attribute to retrieve
2378  * @param sids pointer to sid array to allocate
2379  * @return the count of SIDs pulled
2380  **/
2381  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2382                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2383 {
2384         struct berval **values;
2385         bool ret;
2386         int count, i;
2387
2388         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2389
2390         if (!values)
2391                 return 0;
2392
2393         for (i=0; values[i]; i++)
2394                 /* nop */ ;
2395
2396         if (i) {
2397                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2398                 if (!(*sids)) {
2399                         ldap_value_free_len(values);
2400                         return 0;
2401                 }
2402         } else {
2403                 (*sids) = NULL;
2404         }
2405
2406         count = 0;
2407         for (i=0; values[i]; i++) {
2408                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2409                 if (ret) {
2410                         DEBUG(10, ("pulling SID: %s\n",
2411                                    sid_string_dbg(&(*sids)[count])));
2412                         count++;
2413                 }
2414         }
2415         
2416         ldap_value_free_len(values);
2417         return count;
2418 }
2419
2420 /**
2421  * pull a SEC_DESC from a ADS result
2422  * @param ads connection to ads server
2423  * @param mem_ctx TALLOC_CTX for allocating sid array
2424  * @param msg Results of search
2425  * @param field Attribute to retrieve
2426  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2427  * @return boolean inidicating success
2428 */
2429  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2430                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2431 {
2432         struct berval **values;
2433         bool ret = true;
2434
2435         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2436
2437         if (!values) return false;
2438
2439         if (values[0]) {
2440                 NTSTATUS status;
2441                 status = unmarshall_sec_desc(mem_ctx,
2442                                              (uint8 *)values[0]->bv_val,
2443                                              values[0]->bv_len, sd);
2444                 if (!NT_STATUS_IS_OK(status)) {
2445                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2446                                   nt_errstr(status)));
2447                         ret = false;
2448                 }
2449         }
2450         
2451         ldap_value_free_len(values);
2452         return ret;
2453 }
2454
2455 /* 
2456  * in order to support usernames longer than 21 characters we need to 
2457  * use both the sAMAccountName and the userPrincipalName attributes 
2458  * It seems that not all users have the userPrincipalName attribute set
2459  *
2460  * @param ads connection to ads server
2461  * @param mem_ctx TALLOC_CTX for allocating sid array
2462  * @param msg Results of search
2463  * @return the username
2464  */
2465  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2466                          LDAPMessage *msg)
2467 {
2468 #if 0   /* JERRY */
2469         char *ret, *p;
2470
2471         /* lookup_name() only works on the sAMAccountName to 
2472            returning the username portion of userPrincipalName
2473            breaks winbindd_getpwnam() */
2474
2475         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2476         if (ret && (p = strchr_m(ret, '@'))) {
2477                 *p = 0;
2478                 return ret;
2479         }
2480 #endif
2481         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2482 }
2483
2484
2485 /**
2486  * find the update serial number - this is the core of the ldap cache
2487  * @param ads connection to ads server
2488  * @param ads connection to ADS server
2489  * @param usn Pointer to retrieved update serial number
2490  * @return status of search
2491  **/
2492 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2493 {
2494         const char *attrs[] = {"highestCommittedUSN", NULL};
2495         ADS_STATUS status;
2496         LDAPMessage *res;
2497
2498         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2499         if (!ADS_ERR_OK(status)) 
2500                 return status;
2501
2502         if (ads_count_replies(ads, res) != 1) {
2503                 ads_msgfree(ads, res);
2504                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2505         }
2506
2507         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2508                 ads_msgfree(ads, res);
2509                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2510         }
2511
2512         ads_msgfree(ads, res);
2513         return ADS_SUCCESS;
2514 }
2515
2516 /* parse a ADS timestring - typical string is
2517    '20020917091222.0Z0' which means 09:12.22 17th September
2518    2002, timezone 0 */
2519 static time_t ads_parse_time(const char *str)
2520 {
2521         struct tm tm;
2522
2523         ZERO_STRUCT(tm);
2524
2525         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2526                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2527                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2528                 return 0;
2529         }
2530         tm.tm_year -= 1900;
2531         tm.tm_mon -= 1;
2532
2533         return timegm(&tm);
2534 }
2535
2536 /********************************************************************
2537 ********************************************************************/
2538
2539 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2540 {
2541         const char *attrs[] = {"currentTime", NULL};
2542         ADS_STATUS status;
2543         LDAPMessage *res;
2544         char *timestr;
2545         TALLOC_CTX *ctx;
2546         ADS_STRUCT *ads_s = ads;
2547
2548         if (!(ctx = talloc_init("ads_current_time"))) {
2549                 return ADS_ERROR(LDAP_NO_MEMORY);
2550         }
2551
2552         /* establish a new ldap tcp session if necessary */
2553
2554         if ( !ads->ldap.ld ) {
2555                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2556                         ads->server.ldap_server )) == NULL )
2557                 {
2558                         goto done;
2559                 }
2560                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2561                 status = ads_connect( ads_s );
2562                 if ( !ADS_ERR_OK(status))
2563                         goto done;
2564         }
2565
2566         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2567         if (!ADS_ERR_OK(status)) {
2568                 goto done;
2569         }
2570
2571         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2572         if (!timestr) {
2573                 ads_msgfree(ads_s, res);
2574                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2575                 goto done;
2576         }
2577
2578         /* but save the time and offset in the original ADS_STRUCT */   
2579         
2580         ads->config.current_time = ads_parse_time(timestr);
2581
2582         if (ads->config.current_time != 0) {
2583                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2584                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2585         }
2586
2587         ads_msgfree(ads, res);
2588
2589         status = ADS_SUCCESS;
2590
2591 done:
2592         /* free any temporary ads connections */
2593         if ( ads_s != ads ) {
2594                 ads_destroy( &ads_s );
2595         }
2596         talloc_destroy(ctx);
2597
2598         return status;
2599 }
2600
2601 /********************************************************************
2602 ********************************************************************/
2603
2604 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2605 {
2606         const char *attrs[] = {"domainFunctionality", NULL};
2607         ADS_STATUS status;
2608         LDAPMessage *res;
2609         ADS_STRUCT *ads_s = ads;
2610         
2611         *val = DS_DOMAIN_FUNCTION_2000;
2612
2613         /* establish a new ldap tcp session if necessary */
2614
2615         if ( !ads->ldap.ld ) {
2616                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2617                         ads->server.ldap_server )) == NULL )
2618                 {
2619                         goto done;
2620                 }
2621                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2622                 status = ads_connect( ads_s );
2623                 if ( !ADS_ERR_OK(status))
2624                         goto done;
2625         }
2626
2627         /* If the attribute does not exist assume it is a Windows 2000 
2628            functional domain */
2629            
2630         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2631         if (!ADS_ERR_OK(status)) {
2632                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2633                         status = ADS_SUCCESS;
2634                 }
2635                 goto done;
2636         }
2637
2638         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2639                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2640         }
2641         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2642
2643         
2644         ads_msgfree(ads, res);
2645
2646 done:
2647         /* free any temporary ads connections */
2648         if ( ads_s != ads ) {
2649                 ads_destroy( &ads_s );
2650         }
2651
2652         return status;
2653 }
2654
2655 /**
2656  * find the domain sid for our domain
2657  * @param ads connection to ads server
2658  * @param sid Pointer to domain sid
2659  * @return status of search
2660  **/
2661 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2662 {
2663         const char *attrs[] = {"objectSid", NULL};
2664         LDAPMessage *res;
2665         ADS_STATUS rc;
2666
2667         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2668                            attrs, &res);
2669         if (!ADS_ERR_OK(rc)) return rc;
2670         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2671                 ads_msgfree(ads, res);
2672                 return ADS_ERROR_SYSTEM(ENOENT);
2673         }
2674         ads_msgfree(ads, res);
2675         
2676         return ADS_SUCCESS;
2677 }
2678
2679 /**
2680  * find our site name 
2681  * @param ads connection to ads server
2682  * @param mem_ctx Pointer to talloc context
2683  * @param site_name Pointer to the sitename
2684  * @return status of search
2685  **/
2686 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2687 {
2688         ADS_STATUS status;
2689         LDAPMessage *res;
2690         const char *dn, *service_name;
2691         const char *attrs[] = { "dsServiceName", NULL };
2692
2693         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2694         if (!ADS_ERR_OK(status)) {
2695                 return status;
2696         }
2697
2698         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2699         if (service_name == NULL) {
2700                 ads_msgfree(ads, res);
2701                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2702         }
2703
2704         ads_msgfree(ads, res);
2705
2706         /* go up three levels */
2707         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2708         if (dn == NULL) {
2709                 return ADS_ERROR(LDAP_NO_MEMORY);
2710         }
2711
2712         *site_name = talloc_strdup(mem_ctx, dn);
2713         if (*site_name == NULL) {
2714                 return ADS_ERROR(LDAP_NO_MEMORY);
2715         }
2716
2717         return status;
2718         /*
2719         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2720         */                                               
2721 }
2722
2723 /**
2724  * find the site dn where a machine resides
2725  * @param ads connection to ads server
2726  * @param mem_ctx Pointer to talloc context
2727  * @param computer_name name of the machine
2728  * @param site_name Pointer to the sitename
2729  * @return status of search
2730  **/
2731 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2732 {
2733         ADS_STATUS status;
2734         LDAPMessage *res;
2735         const char *parent, *filter;
2736         char *config_context = NULL;
2737         char *dn;
2738
2739         /* shortcut a query */
2740         if (strequal(computer_name, ads->config.ldap_server_name)) {
2741                 return ads_site_dn(ads, mem_ctx, site_dn);
2742         }
2743
2744         status = ads_config_path(ads, mem_ctx, &config_context);
2745         if (!ADS_ERR_OK(status)) {
2746                 return status;
2747         }
2748
2749         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2750         if (filter == NULL) {
2751                 return ADS_ERROR(LDAP_NO_MEMORY);
2752         }
2753
2754         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
2755                                filter, NULL, &res);
2756         if (!ADS_ERR_OK(status)) {
2757                 return status;
2758         }
2759
2760         if (ads_count_replies(ads, res) != 1) {
2761                 ads_msgfree(ads, res);
2762                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2763         }
2764
2765         dn = ads_get_dn(ads, res);
2766         if (dn == NULL) {
2767                 ads_msgfree(ads, res);
2768                 return ADS_ERROR(LDAP_NO_MEMORY);
2769         }
2770
2771         /* go up three levels */
2772         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2773         if (parent == NULL) {
2774                 ads_msgfree(ads, res);
2775                 ads_memfree(ads, dn);
2776                 return ADS_ERROR(LDAP_NO_MEMORY);
2777         }
2778
2779         *site_dn = talloc_strdup(mem_ctx, parent);
2780         if (*site_dn == NULL) {
2781                 ads_msgfree(ads, res);
2782                 ads_memfree(ads, dn);
2783                 return ADS_ERROR(LDAP_NO_MEMORY);
2784         }
2785
2786         ads_memfree(ads, dn);
2787         ads_msgfree(ads, res);
2788
2789         return status;
2790 }
2791
2792 /**
2793  * get the upn suffixes for a domain
2794  * @param ads connection to ads server
2795  * @param mem_ctx Pointer to talloc context
2796  * @param suffixes Pointer to an array of suffixes
2797  * @param num_suffixes Pointer to the number of suffixes
2798  * @return status of search
2799  **/
2800 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2801 {
2802         ADS_STATUS status;
2803         LDAPMessage *res;
2804         const char *base;
2805         char *config_context = NULL;
2806         const char *attrs[] = { "uPNSuffixes", NULL };
2807
2808         status = ads_config_path(ads, mem_ctx, &config_context);
2809         if (!ADS_ERR_OK(status)) {
2810                 return status;
2811         }
2812
2813         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2814         if (base == NULL) {
2815                 return ADS_ERROR(LDAP_NO_MEMORY);
2816         }
2817
2818         status = ads_search_dn(ads, &res, base, attrs);
2819         if (!ADS_ERR_OK(status)) {
2820                 return status;
2821         }
2822
2823         if (ads_count_replies(ads, res) != 1) {
2824                 ads_msgfree(ads, res);
2825                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2826         }
2827
2828         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2829         if ((*suffixes) == NULL) {
2830                 ads_msgfree(ads, res);
2831                 return ADS_ERROR(LDAP_NO_MEMORY);
2832         }
2833
2834         ads_msgfree(ads, res);
2835
2836         return status;
2837 }
2838
2839 /**
2840  * get the joinable ous for a domain
2841  * @param ads connection to ads server
2842  * @param mem_ctx Pointer to talloc context
2843  * @param ous Pointer to an array of ous
2844  * @param num_ous Pointer to the number of ous
2845  * @return status of search
2846  **/
2847 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2848                                 TALLOC_CTX *mem_ctx,
2849                                 char ***ous,
2850                                 size_t *num_ous)
2851 {
2852         ADS_STATUS status;
2853         LDAPMessage *res = NULL;
2854         LDAPMessage *msg = NULL;
2855         const char *attrs[] = { "dn", NULL };
2856         int count = 0;
2857
2858         status = ads_search(ads, &res,
2859                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
2860                             attrs);
2861         if (!ADS_ERR_OK(status)) {
2862                 return status;
2863         }
2864
2865         count = ads_count_replies(ads, res);
2866         if (count < 1) {
2867                 ads_msgfree(ads, res);
2868                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2869         }
2870
2871         for (msg = ads_first_entry(ads, res); msg;
2872              msg = ads_next_entry(ads, msg)) {
2873
2874                 char *dn = NULL;
2875
2876                 dn = ads_get_dn(ads, msg);
2877                 if (!dn) {
2878                         ads_msgfree(ads, res);
2879                         return ADS_ERROR(LDAP_NO_MEMORY);
2880                 }
2881
2882                 if (!add_string_to_array(mem_ctx, dn,
2883                                          (const char ***)ous,
2884                                          (int *)num_ous)) {
2885                         ads_memfree(ads, dn);
2886                         ads_msgfree(ads, res);
2887                         return ADS_ERROR(LDAP_NO_MEMORY);
2888                 }
2889
2890                 ads_memfree(ads, dn);
2891         }
2892
2893         ads_msgfree(ads, res);
2894
2895         return status;
2896 }
2897
2898
2899 /**
2900  * pull a DOM_SID from an extended dn string
2901  * @param mem_ctx TALLOC_CTX 
2902  * @param extended_dn string
2903  * @param flags string type of extended_dn
2904  * @param sid pointer to a DOM_SID
2905  * @return boolean inidicating success
2906  **/
2907 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2908                                   const char *extended_dn, 
2909                                   enum ads_extended_dn_flags flags, 
2910                                   DOM_SID *sid)
2911 {
2912         char *p, *q, *dn;
2913
2914         if (!extended_dn) {
2915                 return False;
2916         }
2917
2918         /* otherwise extended_dn gets stripped off */
2919         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2920                 return False;
2921         }
2922         /* 
2923          * ADS_EXTENDED_DN_HEX_STRING:
2924          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2925          *
2926          * ADS_EXTENDED_DN_STRING (only with w2k3):
2927         <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
2928          */
2929
2930         p = strchr(dn, ';');
2931         if (!p) {
2932                 return False;
2933         }
2934
2935         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2936                 return False;
2937         }
2938
2939         p += strlen(";<SID=");
2940
2941         q = strchr(p, '>');
2942         if (!q) {
2943                 return False;
2944         }
2945         
2946         *q = '\0';
2947
2948         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2949
2950         switch (flags) {
2951         
2952         case ADS_EXTENDED_DN_STRING:
2953                 if (!string_to_sid(sid, p)) {
2954                         return False;
2955                 }
2956                 break;
2957         case ADS_EXTENDED_DN_HEX_STRING: {
2958                 fstring buf;
2959                 size_t buf_len;
2960
2961                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2962                 if (buf_len == 0) {
2963                         return False;
2964                 }
2965
2966                 if (!sid_parse(buf, buf_len, sid)) {
2967                         DEBUG(10,("failed to parse sid\n"));
2968                         return False;
2969                 }
2970                 break;
2971                 }
2972         default:
2973                 DEBUG(10,("unknown extended dn format\n"));
2974                 return False;
2975         }
2976
2977         return True;
2978 }
2979
2980 /**
2981  * pull an array of DOM_SIDs from a ADS result
2982  * @param ads connection to ads server
2983  * @param mem_ctx TALLOC_CTX for allocating sid array
2984  * @param msg Results of search
2985  * @param field Attribute to retrieve
2986  * @param flags string type of extended_dn
2987  * @param sids pointer to sid array to allocate
2988  * @return the count of SIDs pulled
2989  **/
2990  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2991                                    TALLOC_CTX *mem_ctx, 
2992                                    LDAPMessage *msg, 
2993                                    const char *field,
2994                                    enum ads_extended_dn_flags flags,
2995                                    DOM_SID **sids)
2996 {
2997         int i;
2998         size_t dn_count;
2999         char **dn_strings;
3000
3001         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
3002                                            &dn_count)) == NULL) {
3003                 return 0;
3004         }
3005
3006         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3007         if (!(*sids)) {
3008                 TALLOC_FREE(dn_strings);
3009                 return 0;
3010         }
3011
3012         for (i=0; i<dn_count; i++) {
3013
3014                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
3015                                                   flags, &(*sids)[i])) {
3016                         TALLOC_FREE(*sids);
3017                         TALLOC_FREE(dn_strings);
3018                         return 0;
3019                 }
3020         }
3021
3022         TALLOC_FREE(dn_strings);
3023
3024         return dn_count;
3025 }
3026
3027 /********************************************************************
3028 ********************************************************************/
3029
3030 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3031 {
3032         LDAPMessage *res = NULL;
3033         ADS_STATUS status;
3034         int count = 0;
3035         char *name = NULL;
3036         
3037         status = ads_find_machine_acct(ads, &res, global_myname());
3038         if (!ADS_ERR_OK(status)) {
3039                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3040                         global_myname()));
3041                 goto out;
3042         }
3043                 
3044         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3045                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3046                 goto out;
3047         }
3048                 
3049         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3050                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3051         }
3052
3053 out:
3054         ads_msgfree(ads, res);
3055         
3056         return name;
3057 }
3058
3059 /********************************************************************
3060 ********************************************************************/
3061
3062 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3063 {
3064         LDAPMessage *res = NULL;
3065         ADS_STATUS status;
3066         int count = 0;
3067         char *name = NULL;
3068
3069         status = ads_find_machine_acct(ads, &res, machine_name);
3070         if (!ADS_ERR_OK(status)) {
3071                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3072                         global_myname()));
3073                 goto out;
3074         }
3075
3076         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3077                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3078                 goto out;
3079         }
3080
3081         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3082                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3083         }
3084
3085 out:
3086         ads_msgfree(ads, res);
3087
3088         return name;
3089 }
3090
3091 /********************************************************************
3092 ********************************************************************/
3093
3094 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3095 {
3096         LDAPMessage *res = NULL;
3097         ADS_STATUS status;
3098         int count = 0;
3099         char *name = NULL;
3100         
3101         status = ads_find_machine_acct(ads, &res, global_myname());
3102         if (!ADS_ERR_OK(status)) {
3103                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3104                         global_myname()));
3105                 goto out;
3106         }
3107                 
3108         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3109                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3110                 goto out;
3111         }
3112                 
3113         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3114                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3115         }
3116
3117 out:
3118         ads_msgfree(ads, res);
3119         
3120         return name;
3121 }
3122
3123 #if 0
3124
3125    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3126
3127 /**
3128  * Join a machine to a realm
3129  *  Creates the machine account and sets the machine password
3130  * @param ads connection to ads server
3131  * @param machine name of host to add
3132  * @param org_unit Organizational unit to place machine in
3133  * @return status of join
3134  **/
3135 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3136                         uint32 account_type, const char *org_unit)
3137 {
3138         ADS_STATUS status;
3139         LDAPMessage *res = NULL;
3140         char *machine;
3141
3142         /* machine name must be lowercase */
3143         machine = SMB_STRDUP(machine_name);
3144         strlower_m(machine);
3145
3146         /*
3147         status = ads_find_machine_acct(ads, (void **)&res, machine);
3148         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3149                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3150                 status = ads_leave_realm(ads, machine);
3151                 if (!ADS_ERR_OK(status)) {
3152                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3153                                 machine, ads->config.realm));
3154                         return status;
3155                 }
3156         }
3157         */
3158         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3159         if (!ADS_ERR_OK(status)) {
3160                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3161                 SAFE_FREE(machine);
3162                 return status;
3163         }
3164
3165         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3166         if (!ADS_ERR_OK(status)) {
3167                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3168                 SAFE_FREE(machine);
3169                 return status;
3170         }
3171
3172         SAFE_FREE(machine);
3173         ads_msgfree(ads, res);
3174
3175         return status;
3176 }
3177 #endif
3178
3179 /**
3180  * Delete a machine from the realm
3181  * @param ads connection to ads server
3182  * @param hostname Machine to remove
3183  * @return status of delete
3184  **/
3185 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3186 {
3187         ADS_STATUS status;
3188         void *msg;
3189         LDAPMessage *res;
3190         char *hostnameDN, *host;
3191         int rc;
3192         LDAPControl ldap_control;
3193         LDAPControl  * pldap_control[2] = {NULL, NULL};
3194
3195         pldap_control[0] = &ldap_control;
3196         memset(&ldap_control, 0, sizeof(LDAPControl));
3197         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3198
3199         /* hostname must be lowercase */
3200         host = SMB_STRDUP(hostname);
3201         strlower_m(host);
3202
3203         status = ads_find_machine_acct(ads, &res, host);
3204         if (!ADS_ERR_OK(status)) {
3205                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3206                 SAFE_FREE(host);
3207                 return status;
3208         }
3209
3210         msg = ads_first_entry(ads, res);
3211         if (!msg) {
3212                 SAFE_FREE(host);
3213                 return ADS_ERROR_SYSTEM(ENOENT);
3214         }
3215
3216         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3217
3218         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3219         if (rc) {
3220                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3221         }else {
3222                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3223         }
3224
3225         if (rc != LDAP_SUCCESS) {
3226                 const char *attrs[] = { "cn", NULL };
3227                 LDAPMessage *msg_sub;
3228
3229                 /* we only search with scope ONE, we do not expect any further
3230                  * objects to be created deeper */
3231
3232                 status = ads_do_search_retry(ads, hostnameDN,
3233                                              LDAP_SCOPE_ONELEVEL,
3234                                              "(objectclass=*)", attrs, &res);
3235
3236                 if (!ADS_ERR_OK(status)) {
3237                         SAFE_FREE(host);
3238                         ads_memfree(ads, hostnameDN);
3239                         return status;
3240                 }
3241
3242                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3243                         msg_sub = ads_next_entry(ads, msg_sub)) {
3244
3245                         char *dn = NULL;
3246
3247                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3248                                 SAFE_FREE(host);
3249                                 ads_memfree(ads, hostnameDN);
3250                                 return ADS_ERROR(LDAP_NO_MEMORY);
3251                         }
3252
3253                         status = ads_del_dn(ads, dn);
3254                         if (!ADS_ERR_OK(status)) {
3255                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3256                                 SAFE_FREE(host);
3257                                 ads_memfree(ads, dn);
3258                                 ads_memfree(ads, hostnameDN);
3259                                 return status;
3260                         }
3261
3262                         ads_memfree(ads, dn);
3263                 }
3264
3265                 /* there should be no subordinate objects anymore */
3266                 status = ads_do_search_retry(ads, hostnameDN,
3267                                              LDAP_SCOPE_ONELEVEL,
3268                                              "(objectclass=*)", attrs, &res);
3269
3270                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3271                         SAFE_FREE(host);
3272                         ads_memfree(ads, hostnameDN);
3273                         return status;
3274                 }
3275
3276                 /* delete hostnameDN now */
3277                 status = ads_del_dn(ads, hostnameDN);
3278                 if (!ADS_ERR_OK(status)) {
3279                         SAFE_FREE(host);
3280                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3281                         ads_memfree(ads, hostnameDN);
3282                         return status;
3283                 }
3284         }
3285
3286         ads_memfree(ads, hostnameDN);
3287
3288         status = ads_find_machine_acct(ads, &res, host);
3289         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3290                 DEBUG(3, ("Failed to remove host account.\n"));
3291                 SAFE_FREE(host);
3292                 return status;
3293         }
3294
3295         SAFE_FREE(host);
3296         return status;
3297 }
3298
3299 /**
3300  * pull all token-sids from an LDAP dn
3301  * @param ads connection to ads server
3302  * @param mem_ctx TALLOC_CTX for allocating sid array
3303  * @param dn of LDAP object
3304  * @param user_sid pointer to DOM_SID (objectSid)
3305  * @param primary_group_sid pointer to DOM_SID (self composed)
3306  * @param sids pointer to sid array to allocate
3307  * @param num_sids counter of SIDs pulled
3308  * @return status of token query
3309  **/
3310  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3311                               TALLOC_CTX *mem_ctx,
3312                               const char *dn,
3313                               DOM_SID *user_sid,
3314                               DOM_SID *primary_group_sid,
3315                               DOM_SID **sids,
3316                               size_t *num_sids)
3317 {
3318         ADS_STATUS status;
3319         LDAPMessage *res = NULL;
3320         int count = 0;
3321         size_t tmp_num_sids;
3322         DOM_SID *tmp_sids;
3323         DOM_SID tmp_user_sid;
3324         DOM_SID tmp_primary_group_sid;
3325         uint32 pgid;
3326         const char *attrs[] = {
3327                 "objectSid",
3328                 "tokenGroups",
3329                 "primaryGroupID",
3330                 NULL
3331         };
3332
3333         status = ads_search_retry_dn(ads, &res, dn, attrs);
3334         if (!ADS_ERR_OK(status)) {
3335                 return status;
3336         }
3337
3338         count = ads_count_replies(ads, res);
3339         if (count != 1) {
3340                 ads_msgfree(ads, res);
3341                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3342         }
3343
3344         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3345                 ads_msgfree(ads, res);
3346                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3347         }
3348
3349         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3350                 ads_msgfree(ads, res);
3351                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3352         }
3353
3354         {
3355                 /* hack to compose the primary group sid without knowing the
3356                  * domsid */
3357
3358                 DOM_SID domsid;
3359                 uint32 dummy_rid;
3360
3361                 sid_copy(&domsid, &tmp_user_sid);
3362
3363                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3364                         ads_msgfree(ads, res);
3365                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3366                 }
3367
3368                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3369                         ads_msgfree(ads, res);
3370                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3371                 }
3372         }
3373
3374         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3375
3376         if (tmp_num_sids == 0 || !tmp_sids) {
3377                 ads_msgfree(ads, res);
3378                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3379         }
3380
3381         if (num_sids) {
3382                 *num_sids = tmp_num_sids;
3383         }
3384
3385         if (sids) {
3386                 *sids = tmp_sids;
3387         }
3388
3389         if (user_sid) {
3390                 *user_sid = tmp_user_sid;
3391         }
3392
3393         if (primary_group_sid) {
3394                 *primary_group_sid = tmp_primary_group_sid;
3395         }
3396
3397         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3398
3399         ads_msgfree(ads, res);
3400         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3401 }
3402
3403 /**
3404  * Find a sAMAccoutName in LDAP
3405  * @param ads connection to ads server
3406  * @param mem_ctx TALLOC_CTX for allocating sid array
3407  * @param samaccountname to search
3408  * @param uac_ret uint32 pointer userAccountControl attribute value
3409  * @param dn_ret pointer to dn
3410  * @return status of token query
3411  **/
3412 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3413                                TALLOC_CTX *mem_ctx,
3414                                const char *samaccountname,
3415                                uint32 *uac_ret,
3416                                const char **dn_ret)
3417 {
3418         ADS_STATUS status;
3419         const char *attrs[] = { "userAccountControl", NULL };
3420         const char *filter;
3421         LDAPMessage *res = NULL;
3422         char *dn = NULL;
3423         uint32 uac = 0;
3424
3425         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3426                 samaccountname);
3427         if (filter == NULL) {
3428                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3429                 goto out;
3430         }
3431
3432         status = ads_do_search_all(ads, ads->config.bind_path,
3433                                    LDAP_SCOPE_SUBTREE,
3434                                    filter, attrs, &res);
3435         
3436         if (!ADS_ERR_OK(status)) {
3437                 goto out;
3438         }
3439
3440         if (ads_count_replies(ads, res) != 1) {
3441                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3442                 goto out;
3443         }
3444
3445         dn = ads_get_dn(ads, res);
3446         if (dn == NULL) {
3447                 status = ADS_ERROR(LDAP_NO_MEMORY);
3448                 goto out;
3449         }
3450
3451         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3452                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3453                 goto out;
3454         }
3455
3456         if (uac_ret) {
3457                 *uac_ret = uac;
3458         }
3459
3460         if (dn_ret) {
3461                 *dn_ret = talloc_strdup(mem_ctx, dn);
3462                 if (!*dn_ret) {
3463                         status = ADS_ERROR(LDAP_NO_MEMORY);
3464                         goto out;
3465                 }
3466         }
3467  out:
3468         ads_memfree(ads, dn);
3469         ads_msgfree(ads, res);
3470
3471         return status;
3472 }
3473
3474 /**
3475  * find our configuration path 
3476  * @param ads connection to ads server
3477  * @param mem_ctx Pointer to talloc context
3478  * @param config_path Pointer to the config path
3479  * @return status of search
3480  **/
3481 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3482                            TALLOC_CTX *mem_ctx, 
3483                            char **config_path)
3484 {
3485         ADS_STATUS status;
3486         LDAPMessage *res = NULL;
3487         const char *config_context = NULL;
3488         const char *attrs[] = { "configurationNamingContext", NULL };
3489
3490         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3491                                "(objectclass=*)", attrs, &res);
3492         if (!ADS_ERR_OK(status)) {
3493                 return status;
3494         }
3495
3496         config_context = ads_pull_string(ads, mem_ctx, res, 
3497                                          "configurationNamingContext");
3498         ads_msgfree(ads, res);
3499         if (!config_context) {
3500                 return ADS_ERROR(LDAP_NO_MEMORY);
3501         }
3502
3503         if (config_path) {
3504                 *config_path = talloc_strdup(mem_ctx, config_context);
3505                 if (!*config_path) {
3506                         return ADS_ERROR(LDAP_NO_MEMORY);
3507                 }
3508         }
3509
3510         return ADS_ERROR(LDAP_SUCCESS);
3511 }
3512
3513 /**
3514  * find the displayName of an extended right 
3515  * @param ads connection to ads server
3516  * @param config_path The config path
3517  * @param mem_ctx Pointer to talloc context
3518  * @param GUID struct of the rightsGUID
3519  * @return status of search
3520  **/
3521 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3522                                                 const char *config_path, 
3523                                                 TALLOC_CTX *mem_ctx, 
3524                                                 const struct GUID *rights_guid)
3525 {
3526         ADS_STATUS rc;
3527         LDAPMessage *res = NULL;
3528         char *expr = NULL;
3529         const char *attrs[] = { "displayName", NULL };
3530         const char *result = NULL;
3531         const char *path;
3532
3533         if (!ads || !mem_ctx || !rights_guid) {
3534                 goto done;
3535         }
3536
3537         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3538                                smb_uuid_string(mem_ctx, *rights_guid));
3539         if (!expr) {
3540                 goto done;
3541         }
3542
3543         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3544         if (!path) {
3545                 goto done;
3546         }
3547
3548         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3549                                  expr, attrs, &res);
3550         if (!ADS_ERR_OK(rc)) {
3551                 goto done;
3552         }
3553
3554         if (ads_count_replies(ads, res) != 1) {
3555                 goto done;
3556         }
3557
3558         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3559
3560  done:
3561         ads_msgfree(ads, res);
3562         return result;
3563         
3564 }
3565
3566 /**
3567  * verify or build and verify an account ou
3568  * @param mem_ctx Pointer to talloc context
3569  * @param ads connection to ads server
3570  * @param account_ou
3571  * @return status of search
3572  **/
3573
3574 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3575                            ADS_STRUCT *ads,
3576                            const char *account_ou)
3577 {
3578         struct ldb_dn *name_dn = NULL;
3579         const char *name = NULL;
3580         char *ou_string = NULL;
3581
3582         name_dn = ldb_dn_explode(mem_ctx, account_ou);
3583         if (name_dn) {
3584                 return ADS_SUCCESS;
3585         }
3586
3587         ou_string = ads_ou_string(ads, account_ou);
3588         if (!ou_string) {
3589                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3590         }
3591
3592         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3593                                ads->config.bind_path);
3594         SAFE_FREE(ou_string);
3595         if (!name) {
3596                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3597         }
3598
3599         name_dn = ldb_dn_explode(mem_ctx, name);
3600         if (!name_dn) {
3601                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3602         }
3603
3604         account_ou = talloc_strdup(mem_ctx, name);
3605         if (!account_ou) {
3606                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3607         }
3608
3609         return ADS_SUCCESS;
3610 }
3611
3612 #endif