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