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