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