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