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