r18663: Fix one more uuid -> GUID.
[sfrench/samba-autobuild/.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 2 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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #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         /* Setup timeout */
61         gotalarm = 0;
62         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63         alarm(to);
64         /* End setup timeout. */
65
66         ldp = ldap_open(server, port);
67
68         if (ldp == NULL) {
69                 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70                          server, port, strerror(errno)));
71         }
72
73         /* Teardown timeout. */
74         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
75         alarm(0);
76
77         return ldp;
78 }
79
80 static int ldap_search_with_timeout(LDAP *ld,
81                                     LDAP_CONST char *base,
82                                     int scope,
83                                     LDAP_CONST char *filter,
84                                     char **attrs,
85                                     int attrsonly,
86                                     LDAPControl **sctrls,
87                                     LDAPControl **cctrls,
88                                     int sizelimit,
89                                     LDAPMessage **res )
90 {
91         struct timeval timeout;
92         int result;
93
94         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95         timeout.tv_sec = lp_ldap_timeout();
96         timeout.tv_usec = 0;
97
98         /* Setup alarm timeout.... Do we need both of these ? JRA. */
99         gotalarm = 0;
100         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101         alarm(lp_ldap_timeout());
102         /* End setup timeout. */
103
104         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105                                    attrsonly, sctrls, cctrls, &timeout,
106                                    sizelimit, res);
107
108         /* Teardown timeout. */
109         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
110         alarm(0);
111
112         if (gotalarm != 0)
113                 return LDAP_TIMELIMIT_EXCEEDED;
114
115         return result;
116 }
117
118 /**********************************************
119  Do client and server sitename match ?
120 **********************************************/
121
122 BOOL ads_sitename_match(ADS_STRUCT *ads)
123 {
124         if (ads->config.server_site_name == NULL &&
125             ads->config.client_site_name == NULL ) {
126                 DEBUG(10,("ads_sitename_match: both null\n"));
127                 return True;
128         }
129         if (ads->config.server_site_name &&
130             ads->config.client_site_name &&
131             strequal(ads->config.server_site_name,
132                      ads->config.client_site_name)) {
133                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
134                 return True;
135         }
136         DEBUG(10,("ads_sitename_match: no match %s %s\n",
137                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
138                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
139         return False;
140 }
141
142 /*
143   try a connection to a given ldap server, returning True and setting the servers IP
144   in the ads struct if successful
145  */
146 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
147 {
148         char *srv;
149         struct cldap_netlogon_reply cldap_reply;
150
151         if (!server || !*server) {
152                 return False;
153         }
154         
155         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
156                 server, ads->server.realm));
157
158         /* this copes with inet_ntoa brokenness */
159         
160         srv = SMB_STRDUP(server);
161
162         ZERO_STRUCT( cldap_reply );
163
164         if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
165                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
166                 SAFE_FREE( srv );
167                 return False;
168         }
169
170         /* Check the CLDAP reply flags */
171
172         if ( !(cldap_reply.flags & ADS_LDAP) ) {
173                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
174                         srv));
175                 SAFE_FREE( srv );
176                 return False;
177         }
178
179         /* Fill in the ads->config values */
180
181         SAFE_FREE(ads->config.realm);
182         SAFE_FREE(ads->config.bind_path);
183         SAFE_FREE(ads->config.ldap_server_name);
184         SAFE_FREE(ads->config.server_site_name);
185         SAFE_FREE(ads->config.client_site_name);
186         SAFE_FREE(ads->server.workgroup);
187
188         ads->config.flags              = cldap_reply.flags;
189         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.hostname);
190         strupper_m(cldap_reply.domain);
191         ads->config.realm              = SMB_STRDUP(cldap_reply.domain);
192         ads->config.bind_path          = ads_build_dn(ads->config.realm);
193         if (*cldap_reply.server_site_name) {
194                 ads->config.server_site_name =
195                         SMB_STRDUP(cldap_reply.server_site_name);
196         }
197         if (*cldap_reply.client_site_name) {
198                 ads->config.client_site_name =
199                         SMB_STRDUP(cldap_reply.client_site_name);
200         }
201                 
202         ads->server.workgroup          = SMB_STRDUP(cldap_reply.netbios_domain);
203
204         ads->ldap_port = LDAP_PORT;
205         ads->ldap_ip = *interpret_addr2(srv);
206         SAFE_FREE(srv);
207         
208         /* Store our site name. */
209         sitename_store( cldap_reply.client_site_name );
210
211         return True;
212 }
213
214 /**********************************************************************
215  Try to find an AD dc using our internal name resolution routines
216  Try the realm first and then then workgroup name if netbios is not 
217  disabled
218 **********************************************************************/
219
220 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
221 {
222         const char *c_realm;
223         int count, i=0;
224         struct ip_service *ip_list;
225         pstring realm;
226         BOOL got_realm = False;
227         BOOL use_own_domain = False;
228         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
229
230         /* if the realm and workgroup are both empty, assume they are ours */
231
232         /* realm */
233         c_realm = ads->server.realm;
234         
235         if ( !c_realm || !*c_realm ) {
236                 /* special case where no realm and no workgroup means our own */
237                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
238                         use_own_domain = True;
239                         c_realm = lp_realm();
240                 }
241         }
242         
243         if (c_realm && *c_realm) 
244                 got_realm = True;
245                    
246 again:
247         /* we need to try once with the realm name and fallback to the 
248            netbios domain name if we fail (if netbios has not been disabled */
249            
250         if ( !got_realm && !lp_disable_netbios() ) {
251                 c_realm = ads->server.workgroup;
252                 if (!c_realm || !*c_realm) {
253                         if ( use_own_domain )
254                                 c_realm = lp_workgroup();
255                 }
256                 
257                 if ( !c_realm || !*c_realm ) {
258                         DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
259                         return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
260                 }
261         }
262         
263         pstrcpy( realm, c_realm );
264
265         DEBUG(6,("ads_find_dc: looking for %s '%s'\n", 
266                 (got_realm ? "realm" : "domain"), realm));
267
268         status = get_sorted_dc_list(realm, &ip_list, &count, got_realm);
269         if (!NT_STATUS_IS_OK(status)) {
270                 /* fall back to netbios if we can */
271                 if ( got_realm && !lp_disable_netbios() ) {
272                         got_realm = False;
273                         goto again;
274                 }
275                 
276                 return status;
277         }
278
279         /* if we fail this loop, then giveup since all the IP addresses returned were dead */
280         for ( i=0; i<count; i++ ) {
281                 fstring server;
282                 
283                 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
284                 
285                 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
286                         continue;
287
288                 if (!got_realm) {
289                         /* realm in this case is a workgroup name. We need
290                            to ignore any IP addresses in the negative connection
291                            cache that match ip addresses returned in the ad realm
292                            case. It sucks that I have to reproduce the logic above... */
293                         c_realm = ads->server.realm;
294                         if ( !c_realm || !*c_realm ) {
295                                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
296                                         c_realm = lp_realm();
297                                 }
298                         }
299                         if (c_realm && *c_realm &&
300                                         !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
301                                 /* Ensure we add the workgroup name for this
302                                    IP address as negative too. */
303                                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
304                                 continue;
305                         }
306                 }
307                         
308                 if ( ads_try_connect(ads, server) ) {
309                         SAFE_FREE(ip_list);
310                         return NT_STATUS_OK;
311                 }
312                 
313                 /* keep track of failures */
314                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
315         }
316
317         SAFE_FREE(ip_list);
318         
319         return NT_STATUS_NO_LOGON_SERVERS;
320 }
321
322
323 /**
324  * Connect to the LDAP server
325  * @param ads Pointer to an existing ADS_STRUCT
326  * @return status of connection
327  **/
328 ADS_STATUS ads_connect(ADS_STRUCT *ads)
329 {
330         int version = LDAP_VERSION3;
331         ADS_STATUS status;
332         NTSTATUS ntstatus;
333
334         ads->last_attempt = time(NULL);
335         ads->ld = NULL;
336
337         /* try with a user specified server */
338
339         if (ads->server.ldap_server && 
340             ads_try_connect(ads, ads->server.ldap_server)) {
341                 goto got_connection;
342         }
343
344         ntstatus = ads_find_dc(ads);
345         if (NT_STATUS_IS_OK(ntstatus)) {
346                 goto got_connection;
347         }
348
349         return ADS_ERROR_NT(ntstatus);
350
351 got_connection:
352         DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
353
354         if (!ads->auth.user_name) {
355                 /* Must use the userPrincipalName value here or sAMAccountName
356                    and not servicePrincipalName; found by Guenther Deschner */
357
358                 asprintf(&ads->auth.user_name, "%s$", global_myname() );
359         }
360
361         if (!ads->auth.realm) {
362                 ads->auth.realm = SMB_STRDUP(ads->config.realm);
363         }
364
365         if (!ads->auth.kdc_server) {
366                 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
367         }
368
369 #if KRB5_DNS_HACK
370         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
371            to MIT kerberos to work (tridge) */
372         {
373                 char *env;
374                 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
375                 setenv(env, ads->auth.kdc_server, 1);
376                 free(env);
377         }
378 #endif
379
380         /* If the caller() requested no LDAP bind, then we are done */
381         
382         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
383                 return ADS_SUCCESS;
384         }
385         
386         /* Otherwise setup the TCP LDAP session */
387
388         if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name, 
389                 LDAP_PORT, lp_ldap_timeout())) == NULL )
390         {
391                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
392         }
393
394         /* cache the successful connection for workgroup and realm */
395         if (ads_sitename_match(ads)) {
396                 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
397                 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
398         }
399
400         ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
401
402         status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
403         if (!ADS_ERR_OK(status)) {
404                 return status;
405         }
406
407         /* fill in the current time and offsets */
408         
409         status = ads_current_time( ads );
410         if ( !ADS_ERR_OK(status) ) {
411                 return status;
412         }
413
414         /* Now do the bind */
415         
416         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
417                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
418         }
419
420         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
421                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
422         }
423
424         return ads_sasl_bind(ads);
425 }
426
427 /*
428   Duplicate a struct berval into talloc'ed memory
429  */
430 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
431 {
432         struct berval *value;
433
434         if (!in_val) return NULL;
435
436         value = TALLOC_ZERO_P(ctx, struct berval);
437         if (value == NULL)
438                 return NULL;
439         if (in_val->bv_len == 0) return value;
440
441         value->bv_len = in_val->bv_len;
442         value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
443                                               in_val->bv_len);
444         return value;
445 }
446
447 /*
448   Make a values list out of an array of (struct berval *)
449  */
450 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
451                                       const struct berval **in_vals)
452 {
453         struct berval **values;
454         int i;
455        
456         if (!in_vals) return NULL;
457         for (i=0; in_vals[i]; i++)
458                 ; /* count values */
459         values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
460         if (!values) return NULL;
461
462         for (i=0; in_vals[i]; i++) {
463                 values[i] = dup_berval(ctx, in_vals[i]);
464         }
465         return values;
466 }
467
468 /*
469   UTF8-encode a values list out of an array of (char *)
470  */
471 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
472 {
473         char **values;
474         int i;
475        
476         if (!in_vals) return NULL;
477         for (i=0; in_vals[i]; i++)
478                 ; /* count values */
479         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
480         if (!values) return NULL;
481
482         for (i=0; in_vals[i]; i++) {
483                 push_utf8_talloc(ctx, &values[i], in_vals[i]);
484         }
485         return values;
486 }
487
488 /*
489   Pull a (char *) array out of a UTF8-encoded values list
490  */
491 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
492 {
493         char **values;
494         int i;
495        
496         if (!in_vals) return NULL;
497         for (i=0; in_vals[i]; i++)
498                 ; /* count values */
499         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
500         if (!values) return NULL;
501
502         for (i=0; in_vals[i]; i++) {
503                 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
504         }
505         return values;
506 }
507
508 /**
509  * Do a search with paged results.  cookie must be null on the first
510  *  call, and then returned on each subsequent call.  It will be null
511  *  again when the entire search is complete 
512  * @param ads connection to ads server 
513  * @param bind_path Base dn for the search
514  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
515  * @param expr Search expression - specified in local charset
516  * @param attrs Attributes to retrieve - specified in utf8 or ascii
517  * @param res ** which will contain results - free res* with ads_msgfree()
518  * @param count Number of entries retrieved on this page
519  * @param cookie The paged results cookie to be returned on subsequent calls
520  * @return status of search
521  **/
522 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
523                                            const char *bind_path,
524                                            int scope, const char *expr,
525                                            const char **attrs, void *args,
526                                            LDAPMessage **res, 
527                                            int *count, struct berval **cookie)
528 {
529         int rc, i, version;
530         char *utf8_expr, *utf8_path, **search_attrs;
531         LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
532         BerElement *cookie_be = NULL;
533         struct berval *cookie_bv= NULL;
534         BerElement *extdn_be = NULL;
535         struct berval *extdn_bv= NULL;
536
537         TALLOC_CTX *ctx;
538         ads_control *external_control = (ads_control *) args;
539
540         *res = NULL;
541
542         if (!(ctx = talloc_init("ads_do_paged_search_args")))
543                 return ADS_ERROR(LDAP_NO_MEMORY);
544
545         /* 0 means the conversion worked but the result was empty 
546            so we only fail if it's -1.  In any case, it always 
547            at least nulls out the dest */
548         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
549             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
550                 rc = LDAP_NO_MEMORY;
551                 goto done;
552         }
553
554         if (!attrs || !(*attrs))
555                 search_attrs = NULL;
556         else {
557                 /* This would be the utf8-encoded version...*/
558                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
559                 if (!(str_list_copy(&search_attrs, attrs))) {
560                         rc = LDAP_NO_MEMORY;
561                         goto done;
562                 }
563         }
564                 
565                 
566         /* Paged results only available on ldap v3 or later */
567         ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
568         if (version < LDAP_VERSION3) {
569                 rc =  LDAP_NOT_SUPPORTED;
570                 goto done;
571         }
572
573         cookie_be = ber_alloc_t(LBER_USE_DER);
574         if (*cookie) {
575                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
576                 ber_bvfree(*cookie); /* don't need it from last time */
577                 *cookie = NULL;
578         } else {
579                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
580         }
581         ber_flatten(cookie_be, &cookie_bv);
582         PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
583         PagedResults.ldctl_iscritical = (char) 1;
584         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
585         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
586
587         NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
588         NoReferrals.ldctl_iscritical = (char) 0;
589         NoReferrals.ldctl_value.bv_len = 0;
590         NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
591
592         if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
593
594                 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
595                 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
596
597                 /* win2k does not accept a ldctl_value beeing passed in */
598
599                 if (external_control->val != 0) {
600
601                         if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
602                                 rc = LDAP_NO_MEMORY;
603                                 goto done;
604                         }
605
606                         if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
607                                 rc = LDAP_NO_MEMORY;
608                                 goto done;
609                         }
610                         if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
611                                 rc = LDAP_NO_MEMORY;
612                                 goto done;
613                         }
614
615                         ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
616                         ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
617
618                 } else {
619                         ExtendedDn.ldctl_value.bv_len = 0;
620                         ExtendedDn.ldctl_value.bv_val = NULL;
621                 }
622
623                 controls[0] = &NoReferrals;
624                 controls[1] = &PagedResults;
625                 controls[2] = &ExtendedDn;
626                 controls[3] = NULL;
627
628         } else {
629                 controls[0] = &NoReferrals;
630                 controls[1] = &PagedResults;
631                 controls[2] = NULL;
632         }
633
634         /* we need to disable referrals as the openldap libs don't
635            handle them and paged results at the same time.  Using them
636            together results in the result record containing the server 
637            page control being removed from the result list (tridge/jmcd) 
638         
639            leaving this in despite the control that says don't generate
640            referrals, in case the server doesn't support it (jmcd)
641         */
642         ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
643
644         rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr, 
645                                       search_attrs, 0, controls,
646                                       NULL, LDAP_NO_LIMIT,
647                                       (LDAPMessage **)res);
648
649         ber_free(cookie_be, 1);
650         ber_bvfree(cookie_bv);
651
652         if (rc) {
653                 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
654                          ldap_err2string(rc)));
655                 goto done;
656         }
657
658         rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
659                                         NULL, &rcontrols,  0);
660
661         if (!rcontrols) {
662                 goto done;
663         }
664
665         for (i=0; rcontrols[i]; i++) {
666                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
667                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
668                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
669                                   &cookie_bv);
670                         /* the berval is the cookie, but must be freed when
671                            it is all done */
672                         if (cookie_bv->bv_len) /* still more to do */
673                                 *cookie=ber_bvdup(cookie_bv);
674                         else
675                                 *cookie=NULL;
676                         ber_bvfree(cookie_bv);
677                         ber_free(cookie_be, 1);
678                         break;
679                 }
680         }
681         ldap_controls_free(rcontrols);
682
683 done:
684         talloc_destroy(ctx);
685
686         if (extdn_be) {
687                 ber_free(extdn_be, 1);
688         }
689
690         if (extdn_bv) {
691                 ber_bvfree(extdn_bv);
692         }
693  
694         /* if/when we decide to utf8-encode attrs, take out this next line */
695         str_list_free(&search_attrs);
696
697         return ADS_ERROR(rc);
698 }
699
700 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
701                                       int scope, const char *expr,
702                                       const char **attrs, LDAPMessage **res, 
703                                       int *count, struct berval **cookie)
704 {
705         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
706 }
707
708
709 /**
710  * Get all results for a search.  This uses ads_do_paged_search() to return 
711  * all entries in a large search.
712  * @param ads connection to ads server 
713  * @param bind_path Base dn for the search
714  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
715  * @param expr Search expression
716  * @param attrs Attributes to retrieve
717  * @param res ** which will contain results - free res* with ads_msgfree()
718  * @return status of search
719  **/
720  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
721                                    int scope, const char *expr,
722                                    const char **attrs, void *args,
723                                    LDAPMessage **res)
724 {
725         struct berval *cookie = NULL;
726         int count = 0;
727         ADS_STATUS status;
728
729         *res = NULL;
730         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
731                                      &count, &cookie);
732
733         if (!ADS_ERR_OK(status)) 
734                 return status;
735
736 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
737         while (cookie) {
738                 LDAPMessage *res2 = NULL;
739                 ADS_STATUS status2;
740                 LDAPMessage *msg, *next;
741
742                 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
743                                               attrs, args, &res2, &count, &cookie);
744
745                 if (!ADS_ERR_OK(status2)) break;
746
747                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
748                    that this works on all ldap libs, but I have only tested with openldap */
749                 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
750                         next = ads_next_entry(ads, msg);
751                         ldap_add_result_entry((LDAPMessage **)res, msg);
752                 }
753                 /* note that we do not free res2, as the memory is now
754                    part of the main returned list */
755         }
756 #else
757         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
758         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
759 #endif
760
761         return status;
762 }
763
764  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
765                               int scope, const char *expr,
766                               const char **attrs, LDAPMessage **res)
767 {
768         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
769 }
770
771 /**
772  * Run a function on all results for a search.  Uses ads_do_paged_search() and
773  *  runs the function as each page is returned, using ads_process_results()
774  * @param ads connection to ads server
775  * @param bind_path Base dn for the search
776  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
777  * @param expr Search expression - specified in local charset
778  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
779  * @param fn Function which takes attr name, values list, and data_area
780  * @param data_area Pointer which is passed to function on each call
781  * @return status of search
782  **/
783 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
784                                 int scope, const char *expr, const char **attrs,
785                                 BOOL(*fn)(char *, void **, void *), 
786                                 void *data_area)
787 {
788         struct berval *cookie = NULL;
789         int count = 0;
790         ADS_STATUS status;
791         LDAPMessage *res;
792
793         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
794                                      &count, &cookie);
795
796         if (!ADS_ERR_OK(status)) return status;
797
798         ads_process_results(ads, res, fn, data_area);
799         ads_msgfree(ads, res);
800
801         while (cookie) {
802                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
803                                              &res, &count, &cookie);
804
805                 if (!ADS_ERR_OK(status)) break;
806                 
807                 ads_process_results(ads, res, fn, data_area);
808                 ads_msgfree(ads, res);
809         }
810
811         return status;
812 }
813
814 /**
815  * Do a search with a timeout.
816  * @param ads connection to ads server
817  * @param bind_path Base dn for the search
818  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
819  * @param expr Search expression
820  * @param attrs Attributes to retrieve
821  * @param res ** which will contain results - free res* with ads_msgfree()
822  * @return status of search
823  **/
824  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
825                           const char *expr,
826                           const char **attrs, LDAPMessage **res)
827 {
828         int rc;
829         char *utf8_expr, *utf8_path, **search_attrs = NULL;
830         TALLOC_CTX *ctx;
831
832         *res = NULL;
833         if (!(ctx = talloc_init("ads_do_search"))) {
834                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
835                 return ADS_ERROR(LDAP_NO_MEMORY);
836         }
837
838         /* 0 means the conversion worked but the result was empty 
839            so we only fail if it's negative.  In any case, it always 
840            at least nulls out the dest */
841         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
842             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
843                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
844                 rc = LDAP_NO_MEMORY;
845                 goto done;
846         }
847
848         if (!attrs || !(*attrs))
849                 search_attrs = NULL;
850         else {
851                 /* This would be the utf8-encoded version...*/
852                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
853                 if (!(str_list_copy(&search_attrs, attrs)))
854                 {
855                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
856                         rc = LDAP_NO_MEMORY;
857                         goto done;
858                 }
859         }
860
861         /* see the note in ads_do_paged_search - we *must* disable referrals */
862         ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
863
864         rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
865                                       search_attrs, 0, NULL, NULL, 
866                                       LDAP_NO_LIMIT,
867                                       (LDAPMessage **)res);
868
869         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
870                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
871                 rc = 0;
872         }
873
874  done:
875         talloc_destroy(ctx);
876         /* if/when we decide to utf8-encode attrs, take out this next line */
877         str_list_free(&search_attrs);
878         return ADS_ERROR(rc);
879 }
880 /**
881  * Do a general ADS search
882  * @param ads connection to ads server
883  * @param res ** which will contain results - free res* with ads_msgfree()
884  * @param expr Search expression
885  * @param attrs Attributes to retrieve
886  * @return status of search
887  **/
888  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
889                        const char *expr, const char **attrs)
890 {
891         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
892                              expr, attrs, res);
893 }
894
895 /**
896  * Do a search on a specific DistinguishedName
897  * @param ads connection to ads server
898  * @param res ** which will contain results - free res* with ads_msgfree()
899  * @param dn DistinguishName to search
900  * @param attrs Attributes to retrieve
901  * @return status of search
902  **/
903  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
904                           const char *dn, const char **attrs)
905 {
906         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
907                              attrs, res);
908 }
909
910 /**
911  * Free up memory from a ads_search
912  * @param ads connection to ads server
913  * @param msg Search results to free
914  **/
915  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
916 {
917         if (!msg) return;
918         ldap_msgfree(msg);
919 }
920
921 /**
922  * Free up memory from various ads requests
923  * @param ads connection to ads server
924  * @param mem Area to free
925  **/
926 void ads_memfree(ADS_STRUCT *ads, void *mem)
927 {
928         SAFE_FREE(mem);
929 }
930
931 /**
932  * Get a dn from search results
933  * @param ads connection to ads server
934  * @param msg Search result
935  * @return dn string
936  **/
937  char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
938 {
939         char *utf8_dn, *unix_dn;
940
941         utf8_dn = ldap_get_dn(ads->ld, msg);
942
943         if (!utf8_dn) {
944                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
945                 return NULL;
946         }
947
948         if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
949                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
950                         utf8_dn ));
951                 return NULL;
952         }
953         ldap_memfree(utf8_dn);
954         return unix_dn;
955 }
956
957 /**
958  * Get a canonical dn from search results
959  * @param ads connection to ads server
960  * @param msg Search result
961  * @return dn string
962  **/
963  char *ads_get_dn_canonical(ADS_STRUCT *ads, LDAPMessage *msg)
964 {
965 #ifdef HAVE_LDAP_DN2AD_CANONICAL
966         return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
967 #else
968         return NULL;
969 #endif
970 }
971
972 /**
973  * Get the parent from a dn
974  * @param dn the dn to return the parent from
975  * @return parent dn string
976  **/
977 char *ads_parent_dn(const char *dn)
978 {
979         char *p;
980
981         if (dn == NULL) {
982                 return NULL;
983         }
984
985         p = strchr(dn, ',');
986
987         if (p == NULL) {
988                 return NULL;
989         }
990
991         return p+1;
992 }
993
994 /**
995  * Find a machine account given a hostname
996  * @param ads connection to ads server
997  * @param res ** which will contain results - free res* with ads_msgfree()
998  * @param host Hostname to search for
999  * @return status of search
1000  **/
1001  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1002                                   const char *machine)
1003 {
1004         ADS_STATUS status;
1005         char *expr;
1006         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1007
1008         *res = NULL;
1009
1010         /* the easiest way to find a machine account anywhere in the tree
1011            is to look for hostname$ */
1012         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1013                 DEBUG(1, ("asprintf failed!\n"));
1014                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1015         }
1016         
1017         status = ads_search(ads, res, expr, attrs);
1018         SAFE_FREE(expr);
1019         return status;
1020 }
1021
1022 /**
1023  * Initialize a list of mods to be used in a modify request
1024  * @param ctx An initialized TALLOC_CTX
1025  * @return allocated ADS_MODLIST
1026  **/
1027 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1028 {
1029 #define ADS_MODLIST_ALLOC_SIZE 10
1030         LDAPMod **mods;
1031         
1032         if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1033                 /* -1 is safety to make sure we don't go over the end.
1034                    need to reset it to NULL before doing ldap modify */
1035                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1036         
1037         return (ADS_MODLIST)mods;
1038 }
1039
1040
1041 /*
1042   add an attribute to the list, with values list already constructed
1043 */
1044 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1045                                   int mod_op, const char *name, 
1046                                   const void *_invals)
1047 {
1048         const void **invals = (const void **)_invals;
1049         int curmod;
1050         LDAPMod **modlist = (LDAPMod **) *mods;
1051         struct berval **ber_values = NULL;
1052         char **char_values = NULL;
1053
1054         if (!invals) {
1055                 mod_op = LDAP_MOD_DELETE;
1056         } else {
1057                 if (mod_op & LDAP_MOD_BVALUES)
1058                         ber_values = ads_dup_values(ctx, 
1059                                                 (const struct berval **)invals);
1060                 else
1061                         char_values = ads_push_strvals(ctx, 
1062                                                   (const char **) invals);
1063         }
1064
1065         /* find the first empty slot */
1066         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1067              curmod++);
1068         if (modlist[curmod] == (LDAPMod *) -1) {
1069                 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1070                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1071                         return ADS_ERROR(LDAP_NO_MEMORY);
1072                 memset(&modlist[curmod], 0, 
1073                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1074                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1075                 *mods = (ADS_MODLIST)modlist;
1076         }
1077                 
1078         if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1079                 return ADS_ERROR(LDAP_NO_MEMORY);
1080         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1081         if (mod_op & LDAP_MOD_BVALUES) {
1082                 modlist[curmod]->mod_bvalues = ber_values;
1083         } else if (mod_op & LDAP_MOD_DELETE) {
1084                 modlist[curmod]->mod_values = NULL;
1085         } else {
1086                 modlist[curmod]->mod_values = char_values;
1087         }
1088
1089         modlist[curmod]->mod_op = mod_op;
1090         return ADS_ERROR(LDAP_SUCCESS);
1091 }
1092
1093 /**
1094  * Add a single string value to a mod list
1095  * @param ctx An initialized TALLOC_CTX
1096  * @param mods An initialized ADS_MODLIST
1097  * @param name The attribute name to add
1098  * @param val The value to add - NULL means DELETE
1099  * @return ADS STATUS indicating success of add
1100  **/
1101 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1102                        const char *name, const char *val)
1103 {
1104         const char *values[2];
1105
1106         values[0] = val;
1107         values[1] = NULL;
1108
1109         if (!val)
1110                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1111         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1112 }
1113
1114 /**
1115  * Add an array of string values to a mod list
1116  * @param ctx An initialized TALLOC_CTX
1117  * @param mods An initialized ADS_MODLIST
1118  * @param name The attribute name to add
1119  * @param vals The array of string values to add - NULL means DELETE
1120  * @return ADS STATUS indicating success of add
1121  **/
1122 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1123                            const char *name, const char **vals)
1124 {
1125         if (!vals)
1126                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1127         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1128                                name, (const void **) vals);
1129 }
1130
1131 #if 0
1132 /**
1133  * Add a single ber-encoded value to a mod list
1134  * @param ctx An initialized TALLOC_CTX
1135  * @param mods An initialized ADS_MODLIST
1136  * @param name The attribute name to add
1137  * @param val The value to add - NULL means DELETE
1138  * @return ADS STATUS indicating success of add
1139  **/
1140 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1141                               const char *name, const struct berval *val)
1142 {
1143         const struct berval *values[2];
1144
1145         values[0] = val;
1146         values[1] = NULL;
1147         if (!val)
1148                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1149         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1150                                name, (const void **) values);
1151 }
1152 #endif
1153
1154 /**
1155  * Perform an ldap modify
1156  * @param ads connection to ads server
1157  * @param mod_dn DistinguishedName to modify
1158  * @param mods list of modifications to perform
1159  * @return status of modify
1160  **/
1161 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1162 {
1163         int ret,i;
1164         char *utf8_dn = NULL;
1165         /* 
1166            this control is needed to modify that contains a currently 
1167            non-existent attribute (but allowable for the object) to run
1168         */
1169         LDAPControl PermitModify = {
1170                 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1171                 {0, NULL},
1172                 (char) 1};
1173         LDAPControl *controls[2];
1174
1175         controls[0] = &PermitModify;
1176         controls[1] = NULL;
1177
1178         if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1179                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1180         }
1181
1182         /* find the end of the list, marked by NULL or -1 */
1183         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1184         /* make sure the end of the list is NULL */
1185         mods[i] = NULL;
1186         ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1187                                 (LDAPMod **) mods, controls, NULL);
1188         SAFE_FREE(utf8_dn);
1189         return ADS_ERROR(ret);
1190 }
1191
1192 /**
1193  * Perform an ldap add
1194  * @param ads connection to ads server
1195  * @param new_dn DistinguishedName to add
1196  * @param mods list of attributes and values for DN
1197  * @return status of add
1198  **/
1199 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1200 {
1201         int ret, i;
1202         char *utf8_dn = NULL;
1203
1204         if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1205                 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1206                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1207         }
1208         
1209         /* find the end of the list, marked by NULL or -1 */
1210         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1211         /* make sure the end of the list is NULL */
1212         mods[i] = NULL;
1213
1214         ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1215         SAFE_FREE(utf8_dn);
1216         return ADS_ERROR(ret);
1217 }
1218
1219 /**
1220  * Delete a DistinguishedName
1221  * @param ads connection to ads server
1222  * @param new_dn DistinguishedName to delete
1223  * @return status of delete
1224  **/
1225 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1226 {
1227         int ret;
1228         char *utf8_dn = NULL;
1229         if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1230                 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1231                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1232         }
1233         
1234         ret = ldap_delete_s(ads->ld, utf8_dn);
1235         return ADS_ERROR(ret);
1236 }
1237
1238 /**
1239  * Build an org unit string
1240  *  if org unit is Computers or blank then assume a container, otherwise
1241  *  assume a / separated list of organisational units.
1242  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1243  * @param ads connection to ads server
1244  * @param org_unit Organizational unit
1245  * @return org unit string - caller must free
1246  **/
1247 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1248 {
1249         char *ret = NULL;
1250
1251         if (!org_unit || !*org_unit) {
1252
1253                 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1254
1255                 /* samba4 might not yet respond to a wellknownobject-query */
1256                 return ret ? ret : SMB_STRDUP("cn=Computers");
1257         }
1258         
1259         if (strequal(org_unit, "Computers")) {
1260                 return SMB_STRDUP("cn=Computers");
1261         }
1262
1263         /* jmcd: removed "\\" from the separation chars, because it is
1264            needed as an escape for chars like '#' which are valid in an
1265            OU name */
1266         return ads_build_path(org_unit, "/", "ou=", 1);
1267 }
1268
1269 /**
1270  * Get a org unit string for a well-known GUID
1271  * @param ads connection to ads server
1272  * @param wknguid Well known GUID
1273  * @return org unit string - caller must free
1274  **/
1275 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1276 {
1277         ADS_STATUS status;
1278         LDAPMessage *res;
1279         char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1280         const char *attrs[] = {"distinguishedName", NULL};
1281         int new_ln, wkn_ln, bind_ln, i;
1282
1283         if (wknguid == NULL) {
1284                 return NULL;
1285         }
1286
1287         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1288                 DEBUG(1, ("asprintf failed!\n"));
1289                 return NULL;
1290         }
1291
1292         status = ads_search_dn(ads, &res, base, attrs);
1293         if (!ADS_ERR_OK(status)) {
1294                 DEBUG(1,("Failed while searching for: %s\n", base));
1295                 SAFE_FREE(base);
1296                 return NULL;
1297         }
1298         SAFE_FREE(base);
1299
1300         if (ads_count_replies(ads, res) != 1) {
1301                 return NULL;
1302         }
1303
1304         /* substitute the bind-path from the well-known-guid-search result */
1305         wkn_dn = ads_get_dn(ads, res);
1306         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1307         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1308
1309         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1310                 ;
1311         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1312                 ;
1313
1314         new_ln = wkn_ln - bind_ln;
1315
1316         ret = wkn_dn_exp[0];
1317
1318         for (i=1; i < new_ln; i++) {
1319                 char *s;
1320                 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1321                 ret = SMB_STRDUP(s);
1322                 free(s);
1323         }
1324
1325         ads_memfree(ads, wkn_dn);
1326         ldap_value_free(wkn_dn_exp);
1327         ldap_value_free(bind_dn_exp);
1328
1329         return ret;
1330 }
1331
1332 /**
1333  * Adds (appends) an item to an attribute array, rather then
1334  * replacing the whole list
1335  * @param ctx An initialized TALLOC_CTX
1336  * @param mods An initialized ADS_MODLIST
1337  * @param name name of the ldap attribute to append to
1338  * @param vals an array of values to add
1339  * @return status of addition
1340  **/
1341
1342 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1343                                 const char *name, const char **vals)
1344 {
1345         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1346                                (const void *) vals);
1347 }
1348
1349 /**
1350  * Determines the computer account's current KVNO via an LDAP lookup
1351  * @param ads An initialized ADS_STRUCT
1352  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1353  * @return the kvno for the computer account, or -1 in case of a failure.
1354  **/
1355
1356 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1357 {
1358         LDAPMessage *res = NULL;
1359         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1360         char *filter;
1361         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1362         char *dn_string = NULL;
1363         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1364
1365         DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1366         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1367                 return kvno;
1368         }
1369         ret = ads_search(ads, &res, filter, attrs);
1370         SAFE_FREE(filter);
1371         if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1372                 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1373                 ads_msgfree(ads, res);
1374                 return kvno;
1375         }
1376
1377         dn_string = ads_get_dn(ads, res);
1378         if (!dn_string) {
1379                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1380                 ads_msgfree(ads, res);
1381                 return kvno;
1382         }
1383         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1384         ads_memfree(ads, dn_string);
1385
1386         /* ---------------------------------------------------------
1387          * 0 is returned as a default KVNO from this point on...
1388          * This is done because Windows 2000 does not support key
1389          * version numbers.  Chances are that a failure in the next
1390          * step is simply due to Windows 2000 being used for a
1391          * domain controller. */
1392         kvno = 0;
1393
1394         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1395                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1396                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1397                 ads_msgfree(ads, res);
1398                 return kvno;
1399         }
1400
1401         /* Success */
1402         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1403         ads_msgfree(ads, res);
1404         return kvno;
1405 }
1406
1407 /**
1408  * This clears out all registered spn's for a given hostname
1409  * @param ads An initilaized ADS_STRUCT
1410  * @param machine_name the NetBIOS name of the computer.
1411  * @return 0 upon success, non-zero otherwise.
1412  **/
1413
1414 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1415 {
1416         TALLOC_CTX *ctx;
1417         LDAPMessage *res = NULL;
1418         ADS_MODLIST mods;
1419         const char *servicePrincipalName[1] = {NULL};
1420         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1421         char *dn_string = NULL;
1422
1423         ret = ads_find_machine_acct(ads, &res, machine_name);
1424         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1425                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1426                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1427                 ads_msgfree(ads, res);
1428                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1429         }
1430
1431         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1432         ctx = talloc_init("ads_clear_service_principal_names");
1433         if (!ctx) {
1434                 ads_msgfree(ads, res);
1435                 return ADS_ERROR(LDAP_NO_MEMORY);
1436         }
1437
1438         if (!(mods = ads_init_mods(ctx))) {
1439                 talloc_destroy(ctx);
1440                 ads_msgfree(ads, res);
1441                 return ADS_ERROR(LDAP_NO_MEMORY);
1442         }
1443         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1444         if (!ADS_ERR_OK(ret)) {
1445                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1446                 ads_msgfree(ads, res);
1447                 talloc_destroy(ctx);
1448                 return ret;
1449         }
1450         dn_string = ads_get_dn(ads, res);
1451         if (!dn_string) {
1452                 talloc_destroy(ctx);
1453                 ads_msgfree(ads, res);
1454                 return ADS_ERROR(LDAP_NO_MEMORY);
1455         }
1456         ret = ads_gen_mod(ads, dn_string, mods);
1457         ads_memfree(ads,dn_string);
1458         if (!ADS_ERR_OK(ret)) {
1459                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1460                         machine_name));
1461                 ads_msgfree(ads, res);
1462                 talloc_destroy(ctx);
1463                 return ret;
1464         }
1465
1466         ads_msgfree(ads, res);
1467         talloc_destroy(ctx);
1468         return ret;
1469 }
1470
1471 /**
1472  * This adds a service principal name to an existing computer account
1473  * (found by hostname) in AD.
1474  * @param ads An initialized ADS_STRUCT
1475  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1476  * @param my_fqdn The fully qualified DNS name of the machine
1477  * @param spn A string of the service principal to add, i.e. 'host'
1478  * @return 0 upon sucess, or non-zero if a failure occurs
1479  **/
1480
1481 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1482                                           const char *my_fqdn, const char *spn)
1483 {
1484         ADS_STATUS ret;
1485         TALLOC_CTX *ctx;
1486         LDAPMessage *res = NULL;
1487         char *psp1, *psp2;
1488         ADS_MODLIST mods;
1489         char *dn_string = NULL;
1490         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1491
1492         ret = ads_find_machine_acct(ads, &res, machine_name);
1493         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1494                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1495                         machine_name));
1496                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1497                         spn, machine_name, ads->config.realm));
1498                 ads_msgfree(ads, res);
1499                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1500         }
1501
1502         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1503         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1504                 ads_msgfree(ads, res);
1505                 return ADS_ERROR(LDAP_NO_MEMORY);
1506         }
1507
1508         /* add short name spn */
1509         
1510         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1511                 talloc_destroy(ctx);
1512                 ads_msgfree(ads, res);
1513                 return ADS_ERROR(LDAP_NO_MEMORY);
1514         }
1515         strupper_m(psp1);
1516         strlower_m(&psp1[strlen(spn)]);
1517         servicePrincipalName[0] = psp1;
1518         
1519         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1520                 psp1, machine_name));
1521
1522
1523         /* add fully qualified spn */
1524         
1525         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1526                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1527                 goto out;
1528         }
1529         strupper_m(psp2);
1530         strlower_m(&psp2[strlen(spn)]);
1531         servicePrincipalName[1] = psp2;
1532
1533         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1534                 psp2, machine_name));
1535
1536         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1537                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1538                 goto out;
1539         }
1540         
1541         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1542         if (!ADS_ERR_OK(ret)) {
1543                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1544                 goto out;
1545         }
1546         
1547         if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1548                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1549                 goto out;
1550         }
1551         
1552         ret = ads_gen_mod(ads, dn_string, mods);
1553         ads_memfree(ads,dn_string);
1554         if (!ADS_ERR_OK(ret)) {
1555                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1556                 goto out;
1557         }
1558
1559  out:
1560         TALLOC_FREE( ctx );
1561         ads_msgfree(ads, res);
1562         return ret;
1563 }
1564
1565 /**
1566  * adds a machine account to the ADS server
1567  * @param ads An intialized ADS_STRUCT
1568  * @param machine_name - the NetBIOS machine name of this account.
1569  * @param account_type A number indicating the type of account to create
1570  * @param org_unit The LDAP path in which to place this account
1571  * @return 0 upon success, or non-zero otherwise
1572 **/
1573
1574 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1575                                    const char *org_unit)
1576 {
1577         ADS_STATUS ret;
1578         char *samAccountName, *controlstr;
1579         TALLOC_CTX *ctx;
1580         ADS_MODLIST mods;
1581         char *new_dn;
1582         const char *objectClass[] = {"top", "person", "organizationalPerson",
1583                                      "user", "computer", NULL};
1584         LDAPMessage *res = NULL;
1585         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1586                                 UF_DONT_EXPIRE_PASSWD |\
1587                                 UF_ACCOUNTDISABLE );
1588                               
1589         if (!(ctx = talloc_init("ads_add_machine_acct")))
1590                 return ADS_ERROR(LDAP_NO_MEMORY);
1591
1592         ret = ADS_ERROR(LDAP_NO_MEMORY);
1593                 
1594         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1595         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1596
1597         if ( !new_dn || !samAccountName ) {
1598                 goto done;
1599         }
1600         
1601 #ifndef ENCTYPE_ARCFOUR_HMAC
1602         acct_control |= UF_USE_DES_KEY_ONLY;
1603 #endif
1604
1605         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1606                 goto done;
1607         }
1608
1609         if (!(mods = ads_init_mods(ctx))) {
1610                 goto done;
1611         }
1612         
1613         ads_mod_str(ctx, &mods, "cn", machine_name);
1614         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1615         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1616         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1617
1618         ret = ads_gen_add(ads, new_dn, mods);
1619
1620 done:
1621         ads_msgfree(ads, res);
1622         talloc_destroy(ctx);
1623         
1624         return ret;
1625 }
1626
1627 /*
1628   dump a binary result from ldap
1629 */
1630 static void dump_binary(const char *field, struct berval **values)
1631 {
1632         int i, j;
1633         for (i=0; values[i]; i++) {
1634                 printf("%s: ", field);
1635                 for (j=0; j<values[i]->bv_len; j++) {
1636                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1637                 }
1638                 printf("\n");
1639         }
1640 }
1641
1642 static void dump_guid(const char *field, struct berval **values)
1643 {
1644         int i;
1645         UUID_FLAT guid;
1646         for (i=0; values[i]; i++) {
1647                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1648                 printf("%s: %s\n", field, 
1649                        smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1650         }
1651 }
1652
1653 /*
1654   dump a sid result from ldap
1655 */
1656 static void dump_sid(const char *field, struct berval **values)
1657 {
1658         int i;
1659         for (i=0; values[i]; i++) {
1660                 DOM_SID sid;
1661                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1662                 printf("%s: %s\n", field, sid_string_static(&sid));
1663         }
1664 }
1665
1666 /*
1667   dump ntSecurityDescriptor
1668 */
1669 static void dump_sd(const char *filed, struct berval **values)
1670 {
1671         prs_struct ps;
1672         
1673         SEC_DESC   *psd = 0;
1674         TALLOC_CTX *ctx = 0;
1675
1676         if (!(ctx = talloc_init("sec_io_desc")))
1677                 return;
1678
1679         /* prepare data */
1680         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1681         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1682         prs_set_offset(&ps,0);
1683
1684         /* parse secdesc */
1685         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1686                 prs_mem_free(&ps);
1687                 talloc_destroy(ctx);
1688                 return;
1689         }
1690         if (psd) ads_disp_sd(psd);
1691
1692         prs_mem_free(&ps);
1693         talloc_destroy(ctx);
1694 }
1695
1696 /*
1697   dump a string result from ldap
1698 */
1699 static void dump_string(const char *field, char **values)
1700 {
1701         int i;
1702         for (i=0; values[i]; i++) {
1703                 printf("%s: %s\n", field, values[i]);
1704         }
1705 }
1706
1707 /*
1708   dump a field from LDAP on stdout
1709   used for debugging
1710 */
1711
1712 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1713 {
1714         const struct {
1715                 const char *name;
1716                 BOOL string;
1717                 void (*handler)(const char *, struct berval **);
1718         } handlers[] = {
1719                 {"objectGUID", False, dump_guid},
1720                 {"netbootGUID", False, dump_guid},
1721                 {"nTSecurityDescriptor", False, dump_sd},
1722                 {"dnsRecord", False, dump_binary},
1723                 {"objectSid", False, dump_sid},
1724                 {"tokenGroups", False, dump_sid},
1725                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1726                 {"tokengroupsGlobalandUniversal", False, dump_sid},
1727                 {NULL, True, NULL}
1728         };
1729         int i;
1730
1731         if (!field) { /* must be end of an entry */
1732                 printf("\n");
1733                 return False;
1734         }
1735
1736         for (i=0; handlers[i].name; i++) {
1737                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1738                         if (!values) /* first time, indicate string or not */
1739                                 return handlers[i].string;
1740                         handlers[i].handler(field, (struct berval **) values);
1741                         break;
1742                 }
1743         }
1744         if (!handlers[i].name) {
1745                 if (!values) /* first time, indicate string conversion */
1746                         return True;
1747                 dump_string(field, (char **)values);
1748         }
1749         return False;
1750 }
1751
1752 /**
1753  * Dump a result from LDAP on stdout
1754  *  used for debugging
1755  * @param ads connection to ads server
1756  * @param res Results to dump
1757  **/
1758
1759  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1760 {
1761         ads_process_results(ads, res, ads_dump_field, NULL);
1762 }
1763
1764 /**
1765  * Walk through results, calling a function for each entry found.
1766  *  The function receives a field name, a berval * array of values,
1767  *  and a data area passed through from the start.  The function is
1768  *  called once with null for field and values at the end of each
1769  *  entry.
1770  * @param ads connection to ads server
1771  * @param res Results to process
1772  * @param fn Function for processing each result
1773  * @param data_area user-defined area to pass to function
1774  **/
1775  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1776                           BOOL(*fn)(char *, void **, void *),
1777                           void *data_area)
1778 {
1779         LDAPMessage *msg;
1780         TALLOC_CTX *ctx;
1781
1782         if (!(ctx = talloc_init("ads_process_results")))
1783                 return;
1784
1785         for (msg = ads_first_entry(ads, res); msg; 
1786              msg = ads_next_entry(ads, msg)) {
1787                 char *utf8_field;
1788                 BerElement *b;
1789         
1790                 for (utf8_field=ldap_first_attribute(ads->ld,
1791                                                      (LDAPMessage *)msg,&b); 
1792                      utf8_field;
1793                      utf8_field=ldap_next_attribute(ads->ld,
1794                                                     (LDAPMessage *)msg,b)) {
1795                         struct berval **ber_vals;
1796                         char **str_vals, **utf8_vals;
1797                         char *field;
1798                         BOOL string; 
1799
1800                         pull_utf8_talloc(ctx, &field, utf8_field);
1801                         string = fn(field, NULL, data_area);
1802
1803                         if (string) {
1804                                 utf8_vals = ldap_get_values(ads->ld,
1805                                                  (LDAPMessage *)msg, field);
1806                                 str_vals = ads_pull_strvals(ctx, 
1807                                                   (const char **) utf8_vals);
1808                                 fn(field, (void **) str_vals, data_area);
1809                                 ldap_value_free(utf8_vals);
1810                         } else {
1811                                 ber_vals = ldap_get_values_len(ads->ld, 
1812                                                  (LDAPMessage *)msg, field);
1813                                 fn(field, (void **) ber_vals, data_area);
1814
1815                                 ldap_value_free_len(ber_vals);
1816                         }
1817                         ldap_memfree(utf8_field);
1818                 }
1819                 ber_free(b, 0);
1820                 talloc_free_children(ctx);
1821                 fn(NULL, NULL, data_area); /* completed an entry */
1822
1823         }
1824         talloc_destroy(ctx);
1825 }
1826
1827 /**
1828  * count how many replies are in a LDAPMessage
1829  * @param ads connection to ads server
1830  * @param res Results to count
1831  * @return number of replies
1832  **/
1833 int ads_count_replies(ADS_STRUCT *ads, void *res)
1834 {
1835         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1836 }
1837
1838 /**
1839  * pull the first entry from a ADS result
1840  * @param ads connection to ads server
1841  * @param res Results of search
1842  * @return first entry from result
1843  **/
1844  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1845 {
1846         return ldap_first_entry(ads->ld, res);
1847 }
1848
1849 /**
1850  * pull the next entry from a ADS result
1851  * @param ads connection to ads server
1852  * @param res Results of search
1853  * @return next entry from result
1854  **/
1855  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1856 {
1857         return ldap_next_entry(ads->ld, res);
1858 }
1859
1860 /**
1861  * pull a single string from a ADS result
1862  * @param ads connection to ads server
1863  * @param mem_ctx TALLOC_CTX to use for allocating result string
1864  * @param msg Results of search
1865  * @param field Attribute to retrieve
1866  * @return Result string in talloc context
1867  **/
1868  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
1869                        const char *field)
1870 {
1871         char **values;
1872         char *ret = NULL;
1873         char *ux_string;
1874         size_t rc;
1875
1876         values = ldap_get_values(ads->ld, msg, field);
1877         if (!values)
1878                 return NULL;
1879         
1880         if (values[0]) {
1881                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1882                                       values[0]);
1883                 if (rc != (size_t)-1)
1884                         ret = ux_string;
1885                 
1886         }
1887         ldap_value_free(values);
1888         return ret;
1889 }
1890
1891 /**
1892  * pull an array of strings from a ADS result
1893  * @param ads connection to ads server
1894  * @param mem_ctx TALLOC_CTX to use for allocating result string
1895  * @param msg Results of search
1896  * @param field Attribute to retrieve
1897  * @return Result strings in talloc context
1898  **/
1899  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1900                          LDAPMessage *msg, const char *field,
1901                          size_t *num_values)
1902 {
1903         char **values;
1904         char **ret = NULL;
1905         int i;
1906
1907         values = ldap_get_values(ads->ld, msg, field);
1908         if (!values)
1909                 return NULL;
1910
1911         *num_values = ldap_count_values(values);
1912
1913         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1914         if (!ret) {
1915                 ldap_value_free(values);
1916                 return NULL;
1917         }
1918
1919         for (i=0;i<*num_values;i++) {
1920                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1921                         ldap_value_free(values);
1922                         return NULL;
1923                 }
1924         }
1925         ret[i] = NULL;
1926
1927         ldap_value_free(values);
1928         return ret;
1929 }
1930
1931 /**
1932  * pull an array of strings from a ADS result 
1933  *  (handle large multivalue attributes with range retrieval)
1934  * @param ads connection to ads server
1935  * @param mem_ctx TALLOC_CTX to use for allocating result string
1936  * @param msg Results of search
1937  * @param field Attribute to retrieve
1938  * @param current_strings strings returned by a previous call to this function
1939  * @param next_attribute The next query should ask for this attribute
1940  * @param num_values How many values did we get this time?
1941  * @param more_values Are there more values to get?
1942  * @return Result strings in talloc context
1943  **/
1944  char **ads_pull_strings_range(ADS_STRUCT *ads, 
1945                                TALLOC_CTX *mem_ctx,
1946                                LDAPMessage *msg, const char *field,
1947                                char **current_strings,
1948                                const char **next_attribute,
1949                                size_t *num_strings,
1950                                BOOL *more_strings)
1951 {
1952         char *attr;
1953         char *expected_range_attrib, *range_attr;
1954         BerElement *ptr = NULL;
1955         char **strings;
1956         char **new_strings;
1957         size_t num_new_strings;
1958         unsigned long int range_start;
1959         unsigned long int range_end;
1960         
1961         /* we might have been given the whole lot anyway */
1962         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1963                 *more_strings = False;
1964                 return strings;
1965         }
1966
1967         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1968
1969         /* look for Range result */
1970         for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr); 
1971              attr; 
1972              attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1973                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1974                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1975                         range_attr = attr;
1976                         break;
1977                 }
1978                 ldap_memfree(attr);
1979         }
1980         if (!attr) {
1981                 ber_free(ptr, 0);
1982                 /* nothing here - this field is just empty */
1983                 *more_strings = False;
1984                 return NULL;
1985         }
1986         
1987         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
1988                    &range_start, &range_end) == 2) {
1989                 *more_strings = True;
1990         } else {
1991                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
1992                            &range_start) == 1) {
1993                         *more_strings = False;
1994                 } else {
1995                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
1996                                   range_attr));
1997                         ldap_memfree(range_attr);
1998                         *more_strings = False;
1999                         return NULL;
2000                 }
2001         }
2002
2003         if ((*num_strings) != range_start) {
2004                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2005                           " - aborting range retreival\n",
2006                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2007                 ldap_memfree(range_attr);
2008                 *more_strings = False;
2009                 return NULL;
2010         }
2011
2012         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2013         
2014         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2015                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2016                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2017                           range_attr, (unsigned long int)range_end - range_start + 1, 
2018                           (unsigned long int)num_new_strings));
2019                 ldap_memfree(range_attr);
2020                 *more_strings = False;
2021                 return NULL;
2022         }
2023
2024         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2025                                  *num_strings + num_new_strings);
2026         
2027         if (strings == NULL) {
2028                 ldap_memfree(range_attr);
2029                 *more_strings = False;
2030                 return NULL;
2031         }
2032         
2033         if (new_strings && num_new_strings) {
2034                 memcpy(&strings[*num_strings], new_strings,
2035                        sizeof(*new_strings) * num_new_strings);
2036         }
2037
2038         (*num_strings) += num_new_strings;
2039
2040         if (*more_strings) {
2041                 *next_attribute = talloc_asprintf(mem_ctx,
2042                                                   "%s;range=%d-*", 
2043                                                   field,
2044                                                   (int)*num_strings);
2045                 
2046                 if (!*next_attribute) {
2047                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2048                         ldap_memfree(range_attr);
2049                         *more_strings = False;
2050                         return NULL;
2051                 }
2052         }
2053
2054         ldap_memfree(range_attr);
2055
2056         return strings;
2057 }
2058
2059 /**
2060  * pull a single uint32 from a ADS result
2061  * @param ads connection to ads server
2062  * @param msg Results of search
2063  * @param field Attribute to retrieve
2064  * @param v Pointer to int to store result
2065  * @return boolean inidicating success
2066 */
2067  BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2068                       uint32 *v)
2069 {
2070         char **values;
2071
2072         values = ldap_get_values(ads->ld, msg, field);
2073         if (!values)
2074                 return False;
2075         if (!values[0]) {
2076                 ldap_value_free(values);
2077                 return False;
2078         }
2079
2080         *v = atoi(values[0]);
2081         ldap_value_free(values);
2082         return True;
2083 }
2084
2085 /**
2086  * pull a single objectGUID from an ADS result
2087  * @param ads connection to ADS server
2088  * @param msg results of search
2089  * @param guid 37-byte area to receive text guid
2090  * @return boolean indicating success
2091  **/
2092  BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2093 {
2094         char **values;
2095         UUID_FLAT flat_guid;
2096
2097         values = ldap_get_values(ads->ld, msg, "objectGUID");
2098         if (!values)
2099                 return False;
2100         
2101         if (values[0]) {
2102                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2103                 smb_uuid_unpack(flat_guid, guid);
2104                 ldap_value_free(values);
2105                 return True;
2106         }
2107         ldap_value_free(values);
2108         return False;
2109
2110 }
2111
2112
2113 /**
2114  * pull a single DOM_SID from a ADS result
2115  * @param ads connection to ads server
2116  * @param msg Results of search
2117  * @param field Attribute to retrieve
2118  * @param sid Pointer to sid to store result
2119  * @return boolean inidicating success
2120 */
2121  BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2122                    DOM_SID *sid)
2123 {
2124         struct berval **values;
2125         BOOL ret = False;
2126
2127         values = ldap_get_values_len(ads->ld, msg, field);
2128
2129         if (!values)
2130                 return False;
2131
2132         if (values[0])
2133                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2134         
2135         ldap_value_free_len(values);
2136         return ret;
2137 }
2138
2139 /**
2140  * pull an array of DOM_SIDs from a ADS result
2141  * @param ads connection to ads server
2142  * @param mem_ctx TALLOC_CTX for allocating sid array
2143  * @param msg Results of search
2144  * @param field Attribute to retrieve
2145  * @param sids pointer to sid array to allocate
2146  * @return the count of SIDs pulled
2147  **/
2148  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2149                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2150 {
2151         struct berval **values;
2152         BOOL ret;
2153         int count, i;
2154
2155         values = ldap_get_values_len(ads->ld, msg, field);
2156
2157         if (!values)
2158                 return 0;
2159
2160         for (i=0; values[i]; i++)
2161                 /* nop */ ;
2162
2163         (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2164         if (!(*sids)) {
2165                 ldap_value_free_len(values);
2166                 return 0;
2167         }
2168
2169         count = 0;
2170         for (i=0; values[i]; i++) {
2171                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2172                 if (ret) {
2173                         fstring sid;
2174                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2175                         count++;
2176                 }
2177         }
2178         
2179         ldap_value_free_len(values);
2180         return count;
2181 }
2182
2183 /**
2184  * pull a SEC_DESC from a ADS result
2185  * @param ads connection to ads server
2186  * @param mem_ctx TALLOC_CTX for allocating sid array
2187  * @param msg Results of search
2188  * @param field Attribute to retrieve
2189  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2190  * @return boolean inidicating success
2191 */
2192  BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2193                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2194 {
2195         struct berval **values;
2196         prs_struct      ps;
2197         BOOL ret = False;
2198
2199         values = ldap_get_values_len(ads->ld, msg, field);
2200
2201         if (!values) return False;
2202
2203         if (values[0]) {
2204                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2205                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2206                 prs_set_offset(&ps,0);
2207
2208                 ret = sec_io_desc("sd", sd, &ps, 1);
2209         }
2210         
2211         ldap_value_free_len(values);
2212         return ret;
2213 }
2214
2215 /* 
2216  * in order to support usernames longer than 21 characters we need to 
2217  * use both the sAMAccountName and the userPrincipalName attributes 
2218  * It seems that not all users have the userPrincipalName attribute set
2219  *
2220  * @param ads connection to ads server
2221  * @param mem_ctx TALLOC_CTX for allocating sid array
2222  * @param msg Results of search
2223  * @return the username
2224  */
2225  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2226                          LDAPMessage *msg)
2227 {
2228 #if 0   /* JERRY */
2229         char *ret, *p;
2230
2231         /* lookup_name() only works on the sAMAccountName to 
2232            returning the username portion of userPrincipalName
2233            breaks winbindd_getpwnam() */
2234
2235         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2236         if (ret && (p = strchr_m(ret, '@'))) {
2237                 *p = 0;
2238                 return ret;
2239         }
2240 #endif
2241         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2242 }
2243
2244
2245 /**
2246  * find the update serial number - this is the core of the ldap cache
2247  * @param ads connection to ads server
2248  * @param ads connection to ADS server
2249  * @param usn Pointer to retrieved update serial number
2250  * @return status of search
2251  **/
2252 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2253 {
2254         const char *attrs[] = {"highestCommittedUSN", NULL};
2255         ADS_STATUS status;
2256         LDAPMessage *res;
2257
2258         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2259         if (!ADS_ERR_OK(status)) 
2260                 return status;
2261
2262         if (ads_count_replies(ads, res) != 1) {
2263                 ads_msgfree(ads, res);
2264                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2265         }
2266
2267         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2268                 ads_msgfree(ads, res);
2269                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2270         }
2271
2272         ads_msgfree(ads, res);
2273         return ADS_SUCCESS;
2274 }
2275
2276 /* parse a ADS timestring - typical string is
2277    '20020917091222.0Z0' which means 09:12.22 17th September
2278    2002, timezone 0 */
2279 static time_t ads_parse_time(const char *str)
2280 {
2281         struct tm tm;
2282
2283         ZERO_STRUCT(tm);
2284
2285         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2286                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2287                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2288                 return 0;
2289         }
2290         tm.tm_year -= 1900;
2291         tm.tm_mon -= 1;
2292
2293         return timegm(&tm);
2294 }
2295
2296 /********************************************************************
2297 ********************************************************************/
2298
2299 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2300 {
2301         const char *attrs[] = {"currentTime", NULL};
2302         ADS_STATUS status;
2303         LDAPMessage *res;
2304         char *timestr;
2305         TALLOC_CTX *ctx;
2306         ADS_STRUCT *ads_s = ads;
2307
2308         if (!(ctx = talloc_init("ads_current_time"))) {
2309                 return ADS_ERROR(LDAP_NO_MEMORY);
2310         }
2311
2312         /* establish a new ldap tcp session if necessary */
2313
2314         if ( !ads->ld ) {
2315                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2316                         ads->server.ldap_server )) == NULL )
2317                 {
2318                         goto done;
2319                 }
2320                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2321                 status = ads_connect( ads_s );
2322                 if ( !ADS_ERR_OK(status))
2323                         goto done;
2324         }
2325
2326         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2327         if (!ADS_ERR_OK(status)) {
2328                 goto done;
2329         }
2330
2331         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2332         if (!timestr) {
2333                 ads_msgfree(ads_s, res);
2334                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2335                 goto done;
2336         }
2337
2338         /* but save the time and offset in the original ADS_STRUCT */   
2339         
2340         ads->config.current_time = ads_parse_time(timestr);
2341
2342         if (ads->config.current_time != 0) {
2343                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2344                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2345         }
2346
2347         ads_msgfree(ads, res);
2348
2349         status = ADS_SUCCESS;
2350
2351 done:
2352         /* free any temporary ads connections */
2353         if ( ads_s != ads ) {
2354                 ads_destroy( &ads_s );
2355         }
2356         talloc_destroy(ctx);
2357
2358         return status;
2359 }
2360
2361 /********************************************************************
2362 ********************************************************************/
2363
2364 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2365 {
2366         const char *attrs[] = {"domainFunctionality", NULL};
2367         ADS_STATUS status;
2368         LDAPMessage *res;
2369         ADS_STRUCT *ads_s = ads;
2370         
2371         *val = DS_DOMAIN_FUNCTION_2000;
2372
2373         /* establish a new ldap tcp session if necessary */
2374
2375         if ( !ads->ld ) {
2376                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2377                         ads->server.ldap_server )) == NULL )
2378                 {
2379                         goto done;
2380                 }
2381                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2382                 status = ads_connect( ads_s );
2383                 if ( !ADS_ERR_OK(status))
2384                         goto done;
2385         }
2386
2387         /* If the attribute does not exist assume it is a Windows 2000 
2388            functional domain */
2389            
2390         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2391         if (!ADS_ERR_OK(status)) {
2392                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2393                         status = ADS_SUCCESS;
2394                 }
2395                 goto done;
2396         }
2397
2398         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2399                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2400         }
2401         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2402
2403         
2404         ads_msgfree(ads, res);
2405
2406 done:
2407         /* free any temporary ads connections */
2408         if ( ads_s != ads ) {
2409                 ads_destroy( &ads_s );
2410         }
2411
2412         return status;
2413 }
2414
2415 /**
2416  * find the domain sid for our domain
2417  * @param ads connection to ads server
2418  * @param sid Pointer to domain sid
2419  * @return status of search
2420  **/
2421 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2422 {
2423         const char *attrs[] = {"objectSid", NULL};
2424         LDAPMessage *res;
2425         ADS_STATUS rc;
2426
2427         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2428                            attrs, &res);
2429         if (!ADS_ERR_OK(rc)) return rc;
2430         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2431                 ads_msgfree(ads, res);
2432                 return ADS_ERROR_SYSTEM(ENOENT);
2433         }
2434         ads_msgfree(ads, res);
2435         
2436         return ADS_SUCCESS;
2437 }
2438
2439 /**
2440  * find our site name 
2441  * @param ads connection to ads server
2442  * @param mem_ctx Pointer to talloc context
2443  * @param site_name Pointer to the sitename
2444  * @return status of search
2445  **/
2446 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2447 {
2448         ADS_STATUS status;
2449         LDAPMessage *res;
2450         const char *dn, *service_name;
2451         const char *attrs[] = { "dsServiceName", NULL };
2452
2453         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2454         if (!ADS_ERR_OK(status)) {
2455                 return status;
2456         }
2457
2458         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2459         if (service_name == NULL) {
2460                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2461         }
2462
2463         /* go up three levels */
2464         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2465         if (dn == NULL) {
2466                 return ADS_ERROR(LDAP_NO_MEMORY);
2467         }
2468
2469         *site_name = talloc_strdup(mem_ctx, dn);
2470         if (*site_name == NULL) {
2471                 return ADS_ERROR(LDAP_NO_MEMORY);
2472         }
2473
2474         ads_msgfree(ads, res);
2475
2476         return status;
2477         /*
2478         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2479         */                                               
2480 }
2481
2482 /**
2483  * find the site dn where a machine resides
2484  * @param ads connection to ads server
2485  * @param mem_ctx Pointer to talloc context
2486  * @param computer_name name of the machine
2487  * @param site_name Pointer to the sitename
2488  * @return status of search
2489  **/
2490 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2491 {
2492         ADS_STATUS status;
2493         LDAPMessage *res;
2494         const char *parent, *config_context, *filter;
2495         const char *attrs[] = { "configurationNamingContext", NULL };
2496         char *dn;
2497
2498         /* shortcut a query */
2499         if (strequal(computer_name, ads->config.ldap_server_name)) {
2500                 return ads_site_dn(ads, mem_ctx, site_dn);
2501         }
2502
2503         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2504         if (!ADS_ERR_OK(status)) {
2505                 return status;
2506         }
2507
2508         config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2509         if (config_context == NULL) {
2510                 return ADS_ERROR(LDAP_NO_MEMORY);
2511         }
2512
2513         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2514         if (filter == NULL) {
2515                 return ADS_ERROR(LDAP_NO_MEMORY);
2516         }
2517
2518         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2519         if (!ADS_ERR_OK(status)) {
2520                 return status;
2521         }
2522
2523         if (ads_count_replies(ads, res) != 1) {
2524                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2525         }
2526
2527         dn = ads_get_dn(ads, res);
2528         if (dn == NULL) {
2529                 return ADS_ERROR(LDAP_NO_MEMORY);
2530         }
2531
2532         /* go up three levels */
2533         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2534         if (parent == NULL) {
2535                 ads_memfree(ads, dn);
2536                 return ADS_ERROR(LDAP_NO_MEMORY);
2537         }
2538
2539         *site_dn = talloc_strdup(mem_ctx, parent);
2540         if (*site_dn == NULL) {
2541                 ads_memfree(ads, dn);
2542                 ADS_ERROR(LDAP_NO_MEMORY);
2543         }
2544
2545         ads_memfree(ads, dn);
2546         ads_msgfree(ads, res);
2547
2548         return status;
2549 }
2550
2551 /**
2552  * get the upn suffixes for a domain
2553  * @param ads connection to ads server
2554  * @param mem_ctx Pointer to talloc context
2555  * @param suffixes Pointer to an array of suffixes
2556  * @param site_name Pointer to the number of suffixes
2557  * @return status of search
2558  **/
2559 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2560 {
2561         ADS_STATUS status;
2562         LDAPMessage *res;
2563         const char *config_context, *base;
2564         const char *attrs[] = { "configurationNamingContext", NULL };
2565         const char *attrs2[] = { "uPNSuffixes", NULL };
2566
2567         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2568         if (!ADS_ERR_OK(status)) {
2569                 return status;
2570         }
2571
2572         config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2573         if (config_context == NULL) {
2574                 return ADS_ERROR(LDAP_NO_MEMORY);
2575         }
2576
2577         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2578         if (base == NULL) {
2579                 return ADS_ERROR(LDAP_NO_MEMORY);
2580         }
2581
2582         status = ads_search_dn(ads, &res, base, attrs2); 
2583         if (!ADS_ERR_OK(status)) {
2584                 return status;
2585         }
2586
2587         if (ads_count_replies(ads, res) != 1) {
2588                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2589         }
2590
2591         suffixes = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2592         if (suffixes == NULL) {
2593                 ads_msgfree(ads, res);
2594                 return ADS_ERROR(LDAP_NO_MEMORY);
2595         }
2596
2597         ads_msgfree(ads, res);
2598
2599         return status;
2600 }
2601
2602 /**
2603  * pull a DOM_SID from an extended dn string
2604  * @param mem_ctx TALLOC_CTX 
2605  * @param flags string type of extended_dn
2606  * @param sid pointer to a DOM_SID
2607  * @return boolean inidicating success
2608  **/
2609 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2610                                   const char *dn, 
2611                                   enum ads_extended_dn_flags flags, 
2612                                   DOM_SID *sid)
2613 {
2614         char *p, *q;
2615
2616         if (!dn) {
2617                 return False;
2618         }
2619
2620         /* 
2621          * ADS_EXTENDED_DN_HEX_STRING:
2622          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2623          *
2624          * ADS_EXTENDED_DN_STRING (only with w2k3):
2625         <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
2626          */
2627
2628         p = strchr(dn, ';');
2629         if (!p) {
2630                 return False;
2631         }
2632
2633         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2634                 return False;
2635         }
2636
2637         p += strlen(";<SID=");
2638
2639         q = strchr(p, '>');
2640         if (!q) {
2641                 return False;
2642         }
2643         
2644         *q = '\0';
2645
2646         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2647
2648         switch (flags) {
2649         
2650         case ADS_EXTENDED_DN_STRING:
2651                 if (!string_to_sid(sid, p)) {
2652                         return False;
2653                 }
2654                 break;
2655         case ADS_EXTENDED_DN_HEX_STRING: {
2656                 pstring buf;
2657                 size_t buf_len;
2658
2659                 buf_len = strhex_to_str(buf, strlen(p), p);
2660                 if (buf_len == 0) {
2661                         return False;
2662                 }
2663
2664                 if (!sid_parse(buf, buf_len, sid)) {
2665                         DEBUG(10,("failed to parse sid\n"));
2666                         return False;
2667                 }
2668                 break;
2669                 }
2670         default:
2671                 DEBUG(10,("unknown extended dn format\n"));
2672                 return False;
2673         }
2674
2675         return True;
2676 }
2677
2678 /**
2679  * pull an array of DOM_SIDs from a ADS result
2680  * @param ads connection to ads server
2681  * @param mem_ctx TALLOC_CTX for allocating sid array
2682  * @param msg Results of search
2683  * @param field Attribute to retrieve
2684  * @param flags string type of extended_dn
2685  * @param sids pointer to sid array to allocate
2686  * @return the count of SIDs pulled
2687  **/
2688  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2689                                    TALLOC_CTX *mem_ctx, 
2690                                    LDAPMessage *msg, 
2691                                    const char *field,
2692                                    enum ads_extended_dn_flags flags,
2693                                    DOM_SID **sids)
2694 {
2695         int i;
2696         size_t dn_count;
2697         char **dn_strings;
2698
2699         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
2700                                            &dn_count)) == NULL) {
2701                 return 0;
2702         }
2703
2704         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2705         if (!(*sids)) {
2706                 TALLOC_FREE(dn_strings);
2707                 return 0;
2708         }
2709
2710         for (i=0; i<dn_count; i++) {
2711
2712                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
2713                                                   flags, &(*sids)[i])) {
2714                         TALLOC_FREE(*sids);
2715                         TALLOC_FREE(dn_strings);
2716                         return 0;
2717                 }
2718         }
2719
2720         TALLOC_FREE(dn_strings);
2721
2722         return dn_count;
2723 }
2724
2725 /********************************************************************
2726 ********************************************************************/
2727
2728 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2729 {
2730         LDAPMessage *res = NULL;
2731         ADS_STATUS status;
2732         int count = 0;
2733         char *name = NULL;
2734         
2735         status = ads_find_machine_acct(ads, &res, global_myname());
2736         if (!ADS_ERR_OK(status)) {
2737                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2738                         global_myname()));
2739                 goto out;
2740         }
2741                 
2742         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2743                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2744                 goto out;
2745         }
2746                 
2747         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2748                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2749         }
2750
2751 out:
2752         ads_msgfree(ads, res);
2753         
2754         return name;
2755 }
2756
2757 /********************************************************************
2758 ********************************************************************/
2759
2760 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2761 {
2762         LDAPMessage *res = NULL;
2763         ADS_STATUS status;
2764         int count = 0;
2765         char *name = NULL;
2766         
2767         status = ads_find_machine_acct(ads, &res, global_myname());
2768         if (!ADS_ERR_OK(status)) {
2769                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2770                         global_myname()));
2771                 goto out;
2772         }
2773                 
2774         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2775                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2776                 goto out;
2777         }
2778                 
2779         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2780                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2781         }
2782
2783 out:
2784         ads_msgfree(ads, res);
2785         
2786         return name;
2787 }
2788
2789 /********************************************************************
2790 ********************************************************************/
2791
2792 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2793 {
2794         LDAPMessage *res = NULL;
2795         ADS_STATUS status;
2796         int count = 0;
2797         char *name = NULL;
2798         
2799         status = ads_find_machine_acct(ads, &res, global_myname());
2800         if (!ADS_ERR_OK(status)) {
2801                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2802                         global_myname()));
2803                 goto out;
2804         }
2805                 
2806         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2807                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2808                 goto out;
2809         }
2810                 
2811         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2812                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2813         }
2814
2815 out:
2816         ads_msgfree(ads, res);
2817         
2818         return name;
2819 }
2820
2821 #if 0
2822
2823    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2824
2825 /**
2826  * Join a machine to a realm
2827  *  Creates the machine account and sets the machine password
2828  * @param ads connection to ads server
2829  * @param machine name of host to add
2830  * @param org_unit Organizational unit to place machine in
2831  * @return status of join
2832  **/
2833 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2834                         uint32 account_type, const char *org_unit)
2835 {
2836         ADS_STATUS status;
2837         LDAPMessage *res = NULL;
2838         char *machine;
2839
2840         /* machine name must be lowercase */
2841         machine = SMB_STRDUP(machine_name);
2842         strlower_m(machine);
2843
2844         /*
2845         status = ads_find_machine_acct(ads, (void **)&res, machine);
2846         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2847                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2848                 status = ads_leave_realm(ads, machine);
2849                 if (!ADS_ERR_OK(status)) {
2850                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2851                                 machine, ads->config.realm));
2852                         return status;
2853                 }
2854         }
2855         */
2856         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2857         if (!ADS_ERR_OK(status)) {
2858                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2859                 SAFE_FREE(machine);
2860                 return status;
2861         }
2862
2863         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2864         if (!ADS_ERR_OK(status)) {
2865                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2866                 SAFE_FREE(machine);
2867                 return status;
2868         }
2869
2870         SAFE_FREE(machine);
2871         ads_msgfree(ads, res);
2872
2873         return status;
2874 }
2875 #endif
2876
2877 #ifdef HAVE_LDAP
2878
2879 /**
2880  * Delete a machine from the realm
2881  * @param ads connection to ads server
2882  * @param hostname Machine to remove
2883  * @return status of delete
2884  **/
2885 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2886 {
2887         ADS_STATUS status;
2888         void *msg;
2889         LDAPMessage *res;
2890         char *hostnameDN, *host;
2891         int rc;
2892         LDAPControl ldap_control;
2893         LDAPControl  * pldap_control[2] = {NULL, NULL};
2894
2895         pldap_control[0] = &ldap_control;
2896         memset(&ldap_control, 0, sizeof(LDAPControl));
2897         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
2898
2899         /* hostname must be lowercase */
2900         host = SMB_STRDUP(hostname);
2901         strlower_m(host);
2902
2903         status = ads_find_machine_acct(ads, &res, host);
2904         if (!ADS_ERR_OK(status)) {
2905                 DEBUG(0, ("Host account for %s does not exist.\n", host));
2906                 SAFE_FREE(host);
2907                 return status;
2908         }
2909
2910         msg = ads_first_entry(ads, res);
2911         if (!msg) {
2912                 SAFE_FREE(host);
2913                 return ADS_ERROR_SYSTEM(ENOENT);
2914         }
2915
2916         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
2917
2918         rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
2919         if (rc) {
2920                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
2921         }else {
2922                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
2923         }
2924
2925         if (rc != LDAP_SUCCESS) {
2926                 const char *attrs[] = { "cn", NULL };
2927                 LDAPMessage *msg_sub;
2928
2929                 /* we only search with scope ONE, we do not expect any further
2930                  * objects to be created deeper */
2931
2932                 status = ads_do_search_retry(ads, hostnameDN,
2933                                              LDAP_SCOPE_ONELEVEL,
2934                                              "(objectclass=*)", attrs, &res);
2935
2936                 if (!ADS_ERR_OK(status)) {
2937                         SAFE_FREE(host);
2938                         ads_memfree(ads, hostnameDN);
2939                         return status;
2940                 }
2941
2942                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
2943                         msg_sub = ads_next_entry(ads, msg_sub)) {
2944
2945                         char *dn = NULL;
2946
2947                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
2948                                 SAFE_FREE(host);
2949                                 ads_memfree(ads, hostnameDN);
2950                                 return ADS_ERROR(LDAP_NO_MEMORY);
2951                         }
2952
2953                         status = ads_del_dn(ads, dn);
2954                         if (!ADS_ERR_OK(status)) {
2955                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
2956                                 SAFE_FREE(host);
2957                                 ads_memfree(ads, dn);
2958                                 ads_memfree(ads, hostnameDN);
2959                                 return status;
2960                         }
2961
2962                         ads_memfree(ads, dn);
2963                 }
2964
2965                 /* there should be no subordinate objects anymore */
2966                 status = ads_do_search_retry(ads, hostnameDN,
2967                                              LDAP_SCOPE_ONELEVEL,
2968                                              "(objectclass=*)", attrs, &res);
2969
2970                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
2971                         SAFE_FREE(host);
2972                         ads_memfree(ads, hostnameDN);
2973                         return status;
2974                 }
2975
2976                 /* delete hostnameDN now */
2977                 status = ads_del_dn(ads, hostnameDN);
2978                 if (!ADS_ERR_OK(status)) {
2979                         SAFE_FREE(host);
2980                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
2981                         ads_memfree(ads, hostnameDN);
2982                         return status;
2983                 }
2984         }
2985
2986         ads_memfree(ads, hostnameDN);
2987
2988         status = ads_find_machine_acct(ads, &res, host);
2989         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2990                 DEBUG(3, ("Failed to remove host account.\n"));
2991                 SAFE_FREE(host);
2992                 return status;
2993         }
2994
2995         SAFE_FREE(host);
2996         return status;
2997 }
2998 #endif
2999
3000 #endif