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