r5956: more compile warngin fixes from the Mr. Mader
[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 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 (ADS_MODLIST)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 = (ADS_MODLIST)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, (LDAPMod**)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, *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
1301         /* Add the extra principal */
1302         psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1303         strupper_m(psp1);
1304         strlower_m(&psp1[strlen(spn)]);
1305         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1306         servicePrincipalName[0] = psp1;
1307         psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1308         strupper_m(psp2);
1309         strlower_m(&psp2[strlen(spn)]);
1310         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1311         servicePrincipalName[1] = psp2;
1312
1313         /* Add another principal in case the realm != the DNS domain, so that
1314          * the KDC doesn't send "server principal unknown" errors to clients
1315          * which use the DNS name in determining service principal names. */
1316         psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1317         strupper_m(psp3);
1318         strlower_m(&psp3[strlen(spn)]);
1319         if (strcmp(psp2, psp3) != 0) {
1320                 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1321                 servicePrincipalName[2] = psp3;
1322         }
1323
1324         if (!(mods = ads_init_mods(ctx))) {
1325                 talloc_destroy(ctx);
1326                 ads_msgfree(ads, res);
1327                 return ADS_ERROR(LDAP_NO_MEMORY);
1328         }
1329         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1330         if (!ADS_ERR_OK(ret)) {
1331                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1332                 talloc_destroy(ctx);
1333                 ads_msgfree(ads, res);
1334                 return ret;
1335         }
1336         dn_string = ads_get_dn(ads, res);
1337         if (!dn_string) {
1338                 talloc_destroy(ctx);
1339                 ads_msgfree(ads, res);
1340                 return ADS_ERROR(LDAP_NO_MEMORY);
1341         }
1342         ret = ads_gen_mod(ads, dn_string, mods);
1343         ads_memfree(ads,dn_string);
1344         if (!ADS_ERR_OK(ret)) {
1345                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1346                 talloc_destroy(ctx);
1347                 ads_msgfree(ads, res);
1348                 return ret;
1349         }
1350
1351         talloc_destroy(ctx);
1352         ads_msgfree(ads, res);
1353         return ret;
1354 }
1355
1356 /**
1357  * adds a machine account to the ADS server
1358  * @param ads An intialized ADS_STRUCT
1359  * @param machine_name - the NetBIOS machine name of this account.
1360  * @param account_type A number indicating the type of account to create
1361  * @param org_unit The LDAP path in which to place this account
1362  * @return 0 upon success, or non-zero otherwise
1363 **/
1364
1365 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1366                                        uint32 account_type,
1367                                        const char *org_unit)
1368 {
1369         ADS_STATUS ret, status;
1370         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1371         TALLOC_CTX *ctx;
1372         ADS_MODLIST mods;
1373         const char *objectClass[] = {"top", "person", "organizationalPerson",
1374                                      "user", "computer", NULL};
1375         const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1376         char *psp, *psp2, *psp3, *psp4;
1377         unsigned acct_control;
1378         unsigned exists=0;
1379         fstring my_fqdn;
1380         LDAPMessage *res = NULL;
1381         int i, next_spn;
1382
1383         if (!(ctx = talloc_init("ads_add_machine_acct")))
1384                 return ADS_ERROR(LDAP_NO_MEMORY);
1385
1386         ret = ADS_ERROR(LDAP_NO_MEMORY);
1387
1388         name_to_fqdn(my_fqdn, machine_name);
1389
1390         status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1391         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1392                 char *dn_string = ads_get_dn(ads, res);
1393                 if (!dn_string) {
1394                         DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1395                         goto done;
1396                 }
1397                 new_dn = talloc_strdup(ctx, dn_string);
1398                 ads_memfree(ads,dn_string);
1399                 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1400                         machine_name));
1401                 exists=1;
1402         } else {
1403                 char *ou_str = ads_ou_string(ads,org_unit);
1404                 if (!ou_str) {
1405                         DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1406                         goto done;
1407                 }
1408                 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str, 
1409                                 ads->config.bind_path);
1410
1411                 SAFE_FREE(ou_str);
1412         }
1413
1414         if (!new_dn) {
1415                 goto done;
1416         }
1417
1418         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1419                 goto done;
1420         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1421                 goto done;
1422         servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1423         psp = talloc_asprintf(ctx, "HOST/%s.%s", 
1424                                 machine_name, 
1425                                 ads->config.realm);
1426         strlower_m(&psp[5]);
1427         servicePrincipalName[1] = psp;
1428         servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1429         psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 
1430                                machine_name, 
1431                                ads->config.realm);
1432         strlower_m(&psp2[5]);
1433         servicePrincipalName[3] = psp2;
1434
1435         /* Ensure servicePrincipalName[4] and [5] are unique. */
1436         strlower_m(my_fqdn);
1437         psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1438         strlower_m(&psp3[5]);
1439
1440         next_spn = 4;
1441         for (i = 0; i < next_spn; i++) {
1442                 if (strequal(servicePrincipalName[i], psp3))
1443                         break;
1444         }
1445         if (i == next_spn) {
1446                 servicePrincipalName[next_spn++] = psp3;
1447         }
1448
1449         psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1450         strlower_m(&psp4[5]);
1451         for (i = 0; i < next_spn; i++) {
1452                 if (strequal(servicePrincipalName[i], psp4))
1453                         break;
1454         }
1455         if (i == next_spn) {
1456                 servicePrincipalName[next_spn++] = psp4;
1457         }
1458
1459         if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1460                 goto done;
1461         }
1462
1463         acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1464 #ifndef ENCTYPE_ARCFOUR_HMAC
1465         acct_control |= UF_USE_DES_KEY_ONLY;
1466 #endif
1467
1468         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1469                 goto done;
1470         }
1471
1472         if (!(mods = ads_init_mods(ctx))) {
1473                 goto done;
1474         }
1475
1476         if (!exists) {
1477                 ads_mod_str(ctx, &mods, "cn", machine_name);
1478                 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1479                 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1480                 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1481         }
1482         ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1483         ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1484         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1485         ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1486         ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1487
1488         if (!exists)  {
1489                 ret = ads_gen_add(ads, new_dn, mods);
1490         } else {
1491                 ret = ads_gen_mod(ads, new_dn, mods);
1492         }
1493
1494         if (!ADS_ERR_OK(ret)) {
1495                 goto done;
1496         }
1497
1498         /* Do not fail if we can't set security descriptor
1499          * it shouldn't be mandatory and probably we just 
1500          * don't have enough rights to do it.
1501          */
1502         if (!exists) {
1503                 status = ads_set_machine_sd(ads, machine_name, new_dn);
1504         
1505                 if (!ADS_ERR_OK(status)) {
1506                         DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1507                                         ads_errstr(status)));
1508                 }
1509         }
1510 done:
1511         ads_msgfree(ads, res);
1512         talloc_destroy(ctx);
1513         return ret;
1514 }
1515
1516 /*
1517   dump a binary result from ldap
1518 */
1519 static void dump_binary(const char *field, struct berval **values)
1520 {
1521         int i, j;
1522         for (i=0; values[i]; i++) {
1523                 printf("%s: ", field);
1524                 for (j=0; j<values[i]->bv_len; j++) {
1525                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1526                 }
1527                 printf("\n");
1528         }
1529 }
1530
1531 static void dump_guid(const char *field, struct berval **values)
1532 {
1533         int i;
1534         UUID_FLAT guid;
1535         for (i=0; values[i]; i++) {
1536                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1537                 printf("%s: %s\n", field, 
1538                        smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1539         }
1540 }
1541
1542 /*
1543   dump a sid result from ldap
1544 */
1545 static void dump_sid(const char *field, struct berval **values)
1546 {
1547         int i;
1548         for (i=0; values[i]; i++) {
1549                 DOM_SID sid;
1550                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1551                 printf("%s: %s\n", field, sid_string_static(&sid));
1552         }
1553 }
1554
1555 /*
1556   dump ntSecurityDescriptor
1557 */
1558 static void dump_sd(const char *filed, struct berval **values)
1559 {
1560         prs_struct ps;
1561         
1562         SEC_DESC   *psd = 0;
1563         TALLOC_CTX *ctx = 0;
1564
1565         if (!(ctx = talloc_init("sec_io_desc")))
1566                 return;
1567
1568         /* prepare data */
1569         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1570         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1571         prs_set_offset(&ps,0);
1572
1573         /* parse secdesc */
1574         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1575                 prs_mem_free(&ps);
1576                 talloc_destroy(ctx);
1577                 return;
1578         }
1579         if (psd) ads_disp_sd(psd);
1580
1581         prs_mem_free(&ps);
1582         talloc_destroy(ctx);
1583 }
1584
1585 /*
1586   dump a string result from ldap
1587 */
1588 static void dump_string(const char *field, char **values)
1589 {
1590         int i;
1591         for (i=0; values[i]; i++) {
1592                 printf("%s: %s\n", field, values[i]);
1593         }
1594 }
1595
1596 /*
1597   dump a field from LDAP on stdout
1598   used for debugging
1599 */
1600
1601 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1602 {
1603         const struct {
1604                 const char *name;
1605                 BOOL string;
1606                 void (*handler)(const char *, struct berval **);
1607         } handlers[] = {
1608                 {"objectGUID", False, dump_guid},
1609                 {"nTSecurityDescriptor", False, dump_sd},
1610                 {"dnsRecord", False, dump_binary},
1611                 {"objectSid", False, dump_sid},
1612                 {"tokenGroups", False, dump_sid},
1613                 {NULL, True, NULL}
1614         };
1615         int i;
1616
1617         if (!field) { /* must be end of an entry */
1618                 printf("\n");
1619                 return False;
1620         }
1621
1622         for (i=0; handlers[i].name; i++) {
1623                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1624                         if (!values) /* first time, indicate string or not */
1625                                 return handlers[i].string;
1626                         handlers[i].handler(field, (struct berval **) values);
1627                         break;
1628                 }
1629         }
1630         if (!handlers[i].name) {
1631                 if (!values) /* first time, indicate string conversion */
1632                         return True;
1633                 dump_string(field, (char **)values);
1634         }
1635         return False;
1636 }
1637
1638 /**
1639  * Dump a result from LDAP on stdout
1640  *  used for debugging
1641  * @param ads connection to ads server
1642  * @param res Results to dump
1643  **/
1644
1645 void ads_dump(ADS_STRUCT *ads, void *res)
1646 {
1647         ads_process_results(ads, res, ads_dump_field, NULL);
1648 }
1649
1650 /**
1651  * Walk through results, calling a function for each entry found.
1652  *  The function receives a field name, a berval * array of values,
1653  *  and a data area passed through from the start.  The function is
1654  *  called once with null for field and values at the end of each
1655  *  entry.
1656  * @param ads connection to ads server
1657  * @param res Results to process
1658  * @param fn Function for processing each result
1659  * @param data_area user-defined area to pass to function
1660  **/
1661 void ads_process_results(ADS_STRUCT *ads, void *res,
1662                          BOOL(*fn)(char *, void **, void *),
1663                          void *data_area)
1664 {
1665         void *msg;
1666         TALLOC_CTX *ctx;
1667
1668         if (!(ctx = talloc_init("ads_process_results")))
1669                 return;
1670
1671         for (msg = ads_first_entry(ads, res); msg; 
1672              msg = ads_next_entry(ads, msg)) {
1673                 char *utf8_field;
1674                 BerElement *b;
1675         
1676                 for (utf8_field=ldap_first_attribute(ads->ld,
1677                                                      (LDAPMessage *)msg,&b); 
1678                      utf8_field;
1679                      utf8_field=ldap_next_attribute(ads->ld,
1680                                                     (LDAPMessage *)msg,b)) {
1681                         struct berval **ber_vals;
1682                         char **str_vals, **utf8_vals;
1683                         char *field;
1684                         BOOL string; 
1685
1686                         pull_utf8_talloc(ctx, &field, utf8_field);
1687                         string = fn(field, NULL, data_area);
1688
1689                         if (string) {
1690                                 utf8_vals = ldap_get_values(ads->ld,
1691                                                  (LDAPMessage *)msg, field);
1692                                 str_vals = ads_pull_strvals(ctx, 
1693                                                   (const char **) utf8_vals);
1694                                 fn(field, (void **) str_vals, data_area);
1695                                 ldap_value_free(utf8_vals);
1696                         } else {
1697                                 ber_vals = ldap_get_values_len(ads->ld, 
1698                                                  (LDAPMessage *)msg, field);
1699                                 fn(field, (void **) ber_vals, data_area);
1700
1701                                 ldap_value_free_len(ber_vals);
1702                         }
1703                         ldap_memfree(utf8_field);
1704                 }
1705                 ber_free(b, 0);
1706                 talloc_destroy_pool(ctx);
1707                 fn(NULL, NULL, data_area); /* completed an entry */
1708
1709         }
1710         talloc_destroy(ctx);
1711 }
1712
1713 /**
1714  * count how many replies are in a LDAPMessage
1715  * @param ads connection to ads server
1716  * @param res Results to count
1717  * @return number of replies
1718  **/
1719 int ads_count_replies(ADS_STRUCT *ads, void *res)
1720 {
1721         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1722 }
1723
1724 /**
1725  * Join a machine to a realm
1726  *  Creates the machine account and sets the machine password
1727  * @param ads connection to ads server
1728  * @param machine name of host to add
1729  * @param org_unit Organizational unit to place machine in
1730  * @return status of join
1731  **/
1732 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name, 
1733                           uint32 account_type, const char *org_unit)
1734 {
1735         ADS_STATUS status;
1736         LDAPMessage *res = NULL;
1737         char *machine;
1738
1739         /* machine name must be lowercase */
1740         machine = SMB_STRDUP(machine_name);
1741         strlower_m(machine);
1742
1743         /*
1744         status = ads_find_machine_acct(ads, (void **)&res, machine);
1745         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1746                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1747                 status = ads_leave_realm(ads, machine);
1748                 if (!ADS_ERR_OK(status)) {
1749                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1750                                   machine, ads->config.realm));
1751                         return status;
1752                 }
1753         }
1754         */
1755
1756         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1757         if (!ADS_ERR_OK(status)) {
1758                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1759                 SAFE_FREE(machine);
1760                 return status;
1761         }
1762
1763         status = ads_find_machine_acct(ads, (void **)&res, machine);
1764         if (!ADS_ERR_OK(status)) {
1765                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1766                 SAFE_FREE(machine);
1767                 return status;
1768         }
1769
1770         SAFE_FREE(machine);
1771         ads_msgfree(ads, res);
1772
1773         return status;
1774 }
1775
1776 /**
1777  * Delete a machine from the realm
1778  * @param ads connection to ads server
1779  * @param hostname Machine to remove
1780  * @return status of delete
1781  **/
1782 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1783 {
1784         ADS_STATUS status;
1785         void *res, *msg;
1786         char *hostnameDN, *host; 
1787         int rc;
1788
1789         /* hostname must be lowercase */
1790         host = SMB_STRDUP(hostname);
1791         strlower_m(host);
1792
1793         status = ads_find_machine_acct(ads, &res, host);
1794         if (!ADS_ERR_OK(status)) {
1795             DEBUG(0, ("Host account for %s does not exist.\n", host));
1796             return status;
1797         }
1798
1799         msg = ads_first_entry(ads, res);
1800         if (!msg) {
1801                 return ADS_ERROR_SYSTEM(ENOENT);
1802         }
1803
1804         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1805         rc = ldap_delete_s(ads->ld, hostnameDN);
1806         ads_memfree(ads, hostnameDN);
1807         if (rc != LDAP_SUCCESS) {
1808                 return ADS_ERROR(rc);
1809         }
1810
1811         status = ads_find_machine_acct(ads, &res, host);
1812         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1813                 DEBUG(0, ("Failed to remove host account.\n"));
1814                 return status;
1815         }
1816
1817         free(host);
1818
1819         return status;
1820 }
1821
1822 /**
1823  * add machine account to existing security descriptor 
1824  * @param ads connection to ads server
1825  * @param hostname machine to add
1826  * @param dn DN of security descriptor
1827  * @return status
1828  **/
1829 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1830 {
1831         const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1832         char           *expr     = 0;
1833         size_t          sd_size = 0;
1834         struct berval   bval = {0, NULL};
1835         prs_struct      ps_wire;
1836         char           *escaped_hostname = escape_ldap_string_alloc(hostname);
1837
1838         LDAPMessage *res  = 0;
1839         LDAPMessage *msg  = 0;
1840         ADS_MODLIST  mods = 0;
1841
1842         NTSTATUS    status;
1843         ADS_STATUS  ret;
1844         DOM_SID     sid;
1845         SEC_DESC   *psd = NULL;
1846         TALLOC_CTX *ctx = NULL; 
1847
1848         /* Avoid segmentation fault in prs_mem_free if
1849          * we have to bail out before prs_init */
1850         ps_wire.is_dynamic = False;
1851
1852         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1853
1854         ret = ADS_ERROR(LDAP_SUCCESS);
1855
1856         if (!escaped_hostname) {
1857                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1858         }
1859
1860         if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1861                 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1862                 SAFE_FREE(escaped_hostname);
1863                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1864         }
1865
1866         SAFE_FREE(escaped_hostname);
1867
1868         ret = ads_search(ads, (void *) &res, expr, attrs);
1869
1870         if (!ADS_ERR_OK(ret)) return ret;
1871
1872         if ( !(msg = ads_first_entry(ads, res) )) {
1873                 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1874                 goto ads_set_sd_error;
1875         }
1876
1877         if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1878                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1879                 goto ads_set_sd_error;
1880         }
1881
1882         if (!(ctx = talloc_init("sec_io_desc"))) {
1883                 ret =  ADS_ERROR(LDAP_NO_MEMORY);
1884                 goto ads_set_sd_error;
1885         }
1886
1887         if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1888                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1889                 goto ads_set_sd_error;
1890         }
1891
1892         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1893
1894         if (!NT_STATUS_IS_OK(status)) {
1895                 ret = ADS_ERROR_NT(status);
1896                 goto ads_set_sd_error;
1897         }
1898
1899         if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1900                 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1901         }
1902
1903         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1904                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1905                 goto ads_set_sd_error;
1906         }
1907
1908 #if 0
1909         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1910 #endif
1911         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1912
1913         bval.bv_len = prs_offset(&ps_wire);
1914         bval.bv_val = TALLOC(ctx, bval.bv_len);
1915         if (!bval.bv_val) {
1916                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1917                 goto ads_set_sd_error;
1918         }
1919
1920         prs_set_offset(&ps_wire, 0);
1921
1922         if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1923                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1924                 goto ads_set_sd_error;          
1925         }
1926
1927         ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1928         if (ADS_ERR_OK(ret)) {
1929                 ret = ads_gen_mod(ads, dn, mods);
1930         }
1931
1932 ads_set_sd_error:
1933         ads_msgfree(ads, res);
1934         prs_mem_free(&ps_wire);
1935         talloc_destroy(ctx);
1936         return ret;
1937 }
1938
1939 /**
1940  * pull the first entry from a ADS result
1941  * @param ads connection to ads server
1942  * @param res Results of search
1943  * @return first entry from result
1944  **/
1945 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1946 {
1947         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1948 }
1949
1950 /**
1951  * pull the next entry from a ADS result
1952  * @param ads connection to ads server
1953  * @param res Results of search
1954  * @return next entry from result
1955  **/
1956 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1957 {
1958         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1959 }
1960
1961 /**
1962  * pull a single string from a ADS result
1963  * @param ads connection to ads server
1964  * @param mem_ctx TALLOC_CTX to use for allocating result string
1965  * @param msg Results of search
1966  * @param field Attribute to retrieve
1967  * @return Result string in talloc context
1968  **/
1969 char *ads_pull_string(ADS_STRUCT *ads, 
1970                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1971 {
1972         char **values;
1973         char *ret = NULL;
1974         char *ux_string;
1975         size_t rc;
1976
1977         values = ldap_get_values(ads->ld, msg, field);
1978         if (!values)
1979                 return NULL;
1980         
1981         if (values[0]) {
1982                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1983                                       values[0]);
1984                 if (rc != (size_t)-1)
1985                         ret = ux_string;
1986                 
1987         }
1988         ldap_value_free(values);
1989         return ret;
1990 }
1991
1992 /**
1993  * pull an array of strings from a ADS result
1994  * @param ads connection to ads server
1995  * @param mem_ctx TALLOC_CTX to use for allocating result string
1996  * @param msg Results of search
1997  * @param field Attribute to retrieve
1998  * @return Result strings in talloc context
1999  **/
2000 char **ads_pull_strings(ADS_STRUCT *ads, 
2001                         TALLOC_CTX *mem_ctx, void *msg, const char *field,
2002                         size_t *num_values)
2003 {
2004         char **values;
2005         char **ret = NULL;
2006         int i;
2007
2008         values = ldap_get_values(ads->ld, msg, field);
2009         if (!values)
2010                 return NULL;
2011
2012         *num_values = ldap_count_values(values);
2013
2014         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2015         if (!ret) {
2016                 ldap_value_free(values);
2017                 return NULL;
2018         }
2019
2020         for (i=0;i<*num_values;i++) {
2021                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2022                         ldap_value_free(values);
2023                         return NULL;
2024                 }
2025         }
2026         ret[i] = NULL;
2027
2028         ldap_value_free(values);
2029         return ret;
2030 }
2031
2032 /**
2033  * pull an array of strings from a ADS result 
2034  *  (handle large multivalue attributes with range retrieval)
2035  * @param ads connection to ads server
2036  * @param mem_ctx TALLOC_CTX to use for allocating result string
2037  * @param msg Results of search
2038  * @param field Attribute to retrieve
2039  * @param current_strings strings returned by a previous call to this function
2040  * @param next_attribute The next query should ask for this attribute
2041  * @param num_values How many values did we get this time?
2042  * @param more_values Are there more values to get?
2043  * @return Result strings in talloc context
2044  **/
2045 char **ads_pull_strings_range(ADS_STRUCT *ads, 
2046                               TALLOC_CTX *mem_ctx,
2047                               void *msg, const char *field,
2048                               char **current_strings,
2049                               const char **next_attribute,
2050                               size_t *num_strings,
2051                               BOOL *more_strings)
2052 {
2053         char *attr;
2054         char *expected_range_attrib, *range_attr;
2055         BerElement *ptr = NULL;
2056         char **strings;
2057         char **new_strings;
2058         size_t num_new_strings;
2059         unsigned long int range_start;
2060         unsigned long int range_end;
2061         
2062         /* we might have been given the whole lot anyway */
2063         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2064                 *more_strings = False;
2065                 return strings;
2066         }
2067
2068         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2069
2070         /* look for Range result */
2071         for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr); 
2072              attr; 
2073              attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2074                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2075                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2076                         range_attr = attr;
2077                         break;
2078                 }
2079                 ldap_memfree(attr);
2080         }
2081         if (!attr) {
2082                 ber_free(ptr, 0);
2083                 /* nothing here - this field is just empty */
2084                 *more_strings = False;
2085                 return NULL;
2086         }
2087         
2088         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2089                    &range_start, &range_end) == 2) {
2090                 *more_strings = True;
2091         } else {
2092                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2093                            &range_start) == 1) {
2094                         *more_strings = False;
2095                 } else {
2096                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2097                                   range_attr));
2098                         ldap_memfree(range_attr);
2099                         *more_strings = False;
2100                         return NULL;
2101                 }
2102         }
2103
2104         if ((*num_strings) != range_start) {
2105                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2106                           " - aborting range retreival\n",
2107                           range_attr, *num_strings + 1, range_start));
2108                 ldap_memfree(range_attr);
2109                 *more_strings = False;
2110                 return NULL;
2111         }
2112
2113         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2114         
2115         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2116                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2117                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2118                           range_attr, (unsigned long int)range_end - range_start + 1, 
2119                           (unsigned long int)num_new_strings));
2120                 ldap_memfree(range_attr);
2121                 *more_strings = False;
2122                 return NULL;
2123         }
2124
2125         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2126                                  *num_strings + num_new_strings);
2127         
2128         if (strings == NULL) {
2129                 ldap_memfree(range_attr);
2130                 *more_strings = False;
2131                 return NULL;
2132         }
2133         
2134         memcpy(&strings[*num_strings], new_strings,
2135                sizeof(*new_strings) * num_new_strings);
2136
2137         (*num_strings) += num_new_strings;
2138
2139         if (*more_strings) {
2140                 *next_attribute = talloc_asprintf(mem_ctx,
2141                                                   "%s;range=%d-*", 
2142                                                   field,
2143                                                   *num_strings);
2144                 
2145                 if (!*next_attribute) {
2146                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2147                         ldap_memfree(range_attr);
2148                         *more_strings = False;
2149                         return NULL;
2150                 }
2151         }
2152
2153         ldap_memfree(range_attr);
2154
2155         return strings;
2156 }
2157
2158 /**
2159  * pull a single uint32 from a ADS result
2160  * @param ads connection to ads server
2161  * @param msg Results of search
2162  * @param field Attribute to retrieve
2163  * @param v Pointer to int to store result
2164  * @return boolean inidicating success
2165 */
2166 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
2167                      void *msg, const char *field, uint32 *v)
2168 {
2169         char **values;
2170
2171         values = ldap_get_values(ads->ld, msg, field);
2172         if (!values)
2173                 return False;
2174         if (!values[0]) {
2175                 ldap_value_free(values);
2176                 return False;
2177         }
2178
2179         *v = atoi(values[0]);
2180         ldap_value_free(values);
2181         return True;
2182 }
2183
2184 /**
2185  * pull a single objectGUID from an ADS result
2186  * @param ads connection to ADS server
2187  * @param msg results of search
2188  * @param guid 37-byte area to receive text guid
2189  * @return boolean indicating success
2190  **/
2191 BOOL ads_pull_guid(ADS_STRUCT *ads,
2192                    void *msg, struct uuid *guid)
2193 {
2194         char **values;
2195         UUID_FLAT flat_guid;
2196
2197         values = ldap_get_values(ads->ld, msg, "objectGUID");
2198         if (!values)
2199                 return False;
2200         
2201         if (values[0]) {
2202                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2203                 smb_uuid_unpack(flat_guid, guid);
2204                 ldap_value_free(values);
2205                 return True;
2206         }
2207         ldap_value_free(values);
2208         return False;
2209
2210 }
2211
2212
2213 /**
2214  * pull a single DOM_SID from a ADS result
2215  * @param ads connection to ads server
2216  * @param msg Results of search
2217  * @param field Attribute to retrieve
2218  * @param sid Pointer to sid to store result
2219  * @return boolean inidicating success
2220 */
2221 BOOL ads_pull_sid(ADS_STRUCT *ads, 
2222                   void *msg, const char *field, DOM_SID *sid)
2223 {
2224         struct berval **values;
2225         BOOL ret = False;
2226
2227         values = ldap_get_values_len(ads->ld, msg, field);
2228
2229         if (!values)
2230                 return False;
2231
2232         if (values[0])
2233                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2234         
2235         ldap_value_free_len(values);
2236         return ret;
2237 }
2238
2239 /**
2240  * pull an array of DOM_SIDs from a ADS result
2241  * @param ads connection to ads server
2242  * @param mem_ctx TALLOC_CTX for allocating sid array
2243  * @param msg Results of search
2244  * @param field Attribute to retrieve
2245  * @param sids pointer to sid array to allocate
2246  * @return the count of SIDs pulled
2247  **/
2248 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2249                   void *msg, const char *field, DOM_SID **sids)
2250 {
2251         struct berval **values;
2252         BOOL ret;
2253         int count, i;
2254
2255         values = ldap_get_values_len(ads->ld, msg, field);
2256
2257         if (!values)
2258                 return 0;
2259
2260         for (i=0; values[i]; i++)
2261                 /* nop */ ;
2262
2263         (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2264         if (!(*sids)) {
2265                 ldap_value_free_len(values);
2266                 return 0;
2267         }
2268
2269         count = 0;
2270         for (i=0; values[i]; i++) {
2271                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2272                 if (ret) {
2273                         fstring sid;
2274                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2275                         count++;
2276                 }
2277         }
2278         
2279         ldap_value_free_len(values);
2280         return count;
2281 }
2282
2283 /**
2284  * pull a SEC_DESC from a ADS result
2285  * @param ads connection to ads server
2286  * @param mem_ctx TALLOC_CTX for allocating sid array
2287  * @param msg Results of search
2288  * @param field Attribute to retrieve
2289  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2290  * @return boolean inidicating success
2291 */
2292 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2293                   void *msg, const char *field, SEC_DESC **sd)
2294 {
2295         struct berval **values;
2296         prs_struct      ps;
2297         BOOL ret = False;
2298
2299         values = ldap_get_values_len(ads->ld, msg, field);
2300
2301         if (!values) return False;
2302
2303         if (values[0]) {
2304                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2305                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2306                 prs_set_offset(&ps,0);
2307
2308                 ret = sec_io_desc("sd", sd, &ps, 1);
2309         }
2310         
2311         ldap_value_free_len(values);
2312         return ret;
2313 }
2314
2315 /* 
2316  * in order to support usernames longer than 21 characters we need to 
2317  * use both the sAMAccountName and the userPrincipalName attributes 
2318  * It seems that not all users have the userPrincipalName attribute set
2319  *
2320  * @param ads connection to ads server
2321  * @param mem_ctx TALLOC_CTX for allocating sid array
2322  * @param msg Results of search
2323  * @return the username
2324  */
2325 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2326 {
2327 #if 0   /* JERRY */
2328         char *ret, *p;
2329
2330         /* lookup_name() only works on the sAMAccountName to 
2331            returning the username portion of userPrincipalName
2332            breaks winbindd_getpwnam() */
2333
2334         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2335         if (ret && (p = strchr_m(ret, '@'))) {
2336                 *p = 0;
2337                 return ret;
2338         }
2339 #endif
2340         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2341 }
2342
2343
2344 /**
2345  * find the update serial number - this is the core of the ldap cache
2346  * @param ads connection to ads server
2347  * @param ads connection to ADS server
2348  * @param usn Pointer to retrieved update serial number
2349  * @return status of search
2350  **/
2351 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2352 {
2353         const char *attrs[] = {"highestCommittedUSN", NULL};
2354         ADS_STATUS status;
2355         void *res;
2356
2357         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2358         if (!ADS_ERR_OK(status)) 
2359                 return status;
2360
2361         if (ads_count_replies(ads, res) != 1) {
2362                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2363         }
2364
2365         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2366         ads_msgfree(ads, res);
2367         return ADS_SUCCESS;
2368 }
2369
2370 /* parse a ADS timestring - typical string is
2371    '20020917091222.0Z0' which means 09:12.22 17th September
2372    2002, timezone 0 */
2373 static time_t ads_parse_time(const char *str)
2374 {
2375         struct tm tm;
2376
2377         ZERO_STRUCT(tm);
2378
2379         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2380                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2381                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2382                 return 0;
2383         }
2384         tm.tm_year -= 1900;
2385         tm.tm_mon -= 1;
2386
2387         return timegm(&tm);
2388 }
2389
2390
2391 /**
2392  * Find the servers name and realm - this can be done before authentication 
2393  *  The ldapServiceName field on w2k  looks like this:
2394  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2395  * @param ads connection to ads server
2396  * @return status of search
2397  **/
2398 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2399 {
2400         const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2401         ADS_STATUS status;
2402         void *res;
2403         char *value;
2404         char *p;
2405         char *timestr;
2406         TALLOC_CTX *ctx;
2407
2408         if (!(ctx = talloc_init("ads_server_info"))) {
2409                 return ADS_ERROR(LDAP_NO_MEMORY);
2410         }
2411
2412         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2413         if (!ADS_ERR_OK(status)) {
2414                 talloc_destroy(ctx);
2415                 return status;
2416         }
2417
2418         value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2419         if (!value) {
2420                 ads_msgfree(ads, res);
2421                 talloc_destroy(ctx);
2422                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2423         }
2424
2425         timestr = ads_pull_string(ads, ctx, res, "currentTime");
2426         if (!timestr) {
2427                 ads_msgfree(ads, res);
2428                 talloc_destroy(ctx);
2429                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2430         }
2431
2432         ads_msgfree(ads, res);
2433
2434         p = strchr(value, ':');
2435         if (!p) {
2436                 talloc_destroy(ctx);
2437                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2438                           "so was deemed invalid\n"));
2439                 return ADS_ERROR(LDAP_DECODING_ERROR);
2440         }
2441
2442         SAFE_FREE(ads->config.ldap_server_name);
2443
2444         ads->config.ldap_server_name = SMB_STRDUP(p+1);
2445         p = strchr(ads->config.ldap_server_name, '$');
2446         if (!p || p[1] != '@') {
2447                 talloc_destroy(ctx);
2448                 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2449                           " so was deemed invalid\n", ads->config.ldap_server_name));
2450                 SAFE_FREE(ads->config.ldap_server_name);
2451                 return ADS_ERROR(LDAP_DECODING_ERROR);
2452         }
2453
2454         *p = 0;
2455
2456         SAFE_FREE(ads->config.realm);
2457         SAFE_FREE(ads->config.bind_path);
2458
2459         ads->config.realm = SMB_STRDUP(p+2);
2460         ads->config.bind_path = ads_build_dn(ads->config.realm);
2461
2462         DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
2463                  ads->config.ldap_server_name, ads->config.realm,
2464                  ads->config.bind_path));
2465
2466         ads->config.current_time = ads_parse_time(timestr);
2467
2468         if (ads->config.current_time != 0) {
2469                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2470                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2471         }
2472
2473         talloc_destroy(ctx);
2474
2475         return ADS_SUCCESS;
2476 }
2477
2478 /**
2479  * find the domain sid for our domain
2480  * @param ads connection to ads server
2481  * @param sid Pointer to domain sid
2482  * @return status of search
2483  **/
2484 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2485 {
2486         const char *attrs[] = {"objectSid", NULL};
2487         void *res;
2488         ADS_STATUS rc;
2489
2490         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2491                            attrs, &res);
2492         if (!ADS_ERR_OK(rc)) return rc;
2493         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2494                 ads_msgfree(ads, res);
2495                 return ADS_ERROR_SYSTEM(ENOENT);
2496         }
2497         ads_msgfree(ads, res);
2498         
2499         return ADS_SUCCESS;
2500 }
2501
2502 /* this is rather complex - we need to find the allternate (netbios) name
2503    for the domain, but there isn't a simple query to do this. Instead
2504    we look for the principle names on the DCs account and find one that has 
2505    the right form, then extract the netbios name of the domain from that
2506
2507    NOTE! better method is this:
2508
2509 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName 
2510
2511 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2512
2513 */
2514 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2515 {
2516         char *expr;
2517         ADS_STATUS rc;
2518         char **principles;
2519         char *prefix;
2520         int prefix_length;
2521         int i;
2522         void *res;
2523         const char *attrs[] = {"servicePrincipalName", NULL};
2524         size_t num_principals;
2525
2526         (*workgroup) = NULL;
2527
2528         asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))", 
2529                  ads->config.ldap_server_name, ads->config.realm);
2530         if (expr == NULL) {
2531                 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2532         }
2533
2534         rc = ads_search(ads, &res, expr, attrs);
2535         free(expr);
2536
2537         if (!ADS_ERR_OK(rc)) {
2538                 return rc;
2539         }
2540
2541         principles = ads_pull_strings(ads, mem_ctx, res,
2542                                       "servicePrincipalName", &num_principals);
2543
2544         ads_msgfree(ads, res);
2545
2546         if (!principles) {
2547                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2548         }
2549
2550         asprintf(&prefix, "HOST/%s.%s/", 
2551                  ads->config.ldap_server_name, 
2552                  ads->config.realm);
2553
2554         prefix_length = strlen(prefix);
2555
2556         for (i=0;principles[i]; i++) {
2557                 if (strnequal(principles[i], prefix, prefix_length) &&
2558                     !strequal(ads->config.realm, principles[i]+prefix_length) &&
2559                     !strchr(principles[i]+prefix_length, '.')) {
2560                         /* found an alternate (short) name for the domain. */
2561                         DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2562                                  principles[i]+prefix_length, 
2563                                  ads->config.realm));
2564                         (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2565                         break;
2566                 }
2567         }
2568         free(prefix);
2569
2570         if (!*workgroup) {
2571                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2572         }
2573         
2574         return ADS_SUCCESS;
2575 }
2576
2577 #endif