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