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