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