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