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