r1222: Valgrind memory leak fixes. Still tracking down a strange one...
[vlendec/samba-autobuild/.git] / source3 / 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 = NULL;
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                 ads_msgfree(ads, res);
1022                 return kvno;
1023         }
1024
1025         dn_string = ads_get_dn(ads, res);
1026         if (!dn_string) {
1027                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1028                 ads_msgfree(ads, res);
1029                 return kvno;
1030         }
1031         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1032         ads_memfree(ads, dn_string);
1033
1034         /* ---------------------------------------------------------
1035          * 0 is returned as a default KVNO from this point on...
1036          * This is done because Windows 2000 does not support key
1037          * version numbers.  Chances are that a failure in the next
1038          * step is simply due to Windows 2000 being used for a
1039          * domain controller. */
1040         kvno = 0;
1041
1042         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1043                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1044                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1045                 ads_msgfree(ads, res);
1046                 return kvno;
1047         }
1048
1049         /* Success */
1050         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1051         ads_msgfree(ads, res);
1052         return kvno;
1053 }
1054
1055 /**
1056  * This clears out all registered spn's for a given hostname
1057  * @param ads An initilaized ADS_STRUCT
1058  * @param machine_name the NetBIOS name of the computer.
1059  * @return 0 upon success, non-zero otherwise.
1060  **/
1061
1062 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1063 {
1064         TALLOC_CTX *ctx;
1065         LDAPMessage *res = NULL;
1066         ADS_MODLIST mods;
1067         const char *servicePrincipalName[1] = {NULL};
1068         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1069         char *dn_string = NULL;
1070
1071         ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1072         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1073                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1074                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1075                 ads_msgfree(ads, res);
1076                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1077         }
1078
1079         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1080         ctx = talloc_init("ads_clear_service_principal_names");
1081         if (!ctx) {
1082                 ads_msgfree(ads, res);
1083                 return ADS_ERROR(LDAP_NO_MEMORY);
1084         }
1085
1086         if (!(mods = ads_init_mods(ctx))) {
1087                 talloc_destroy(ctx);
1088                 ads_msgfree(ads, res);
1089                 return ADS_ERROR(LDAP_NO_MEMORY);
1090         }
1091         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1092         if (!ADS_ERR_OK(ret)) {
1093                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1094                 ads_msgfree(ads, res);
1095                 talloc_destroy(ctx);
1096                 return ret;
1097         }
1098         dn_string = ads_get_dn(ads, res);
1099         if (!dn_string) {
1100                 talloc_destroy(ctx);
1101                 ads_msgfree(ads, res);
1102                 return ADS_ERROR(LDAP_NO_MEMORY);
1103         }
1104         ret = ads_gen_mod(ads, dn_string, mods);
1105         ads_memfree(ads,dn_string);
1106         if (!ADS_ERR_OK(ret)) {
1107                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1108                         machine_name));
1109                 ads_msgfree(ads, res);
1110                 talloc_destroy(ctx);
1111                 return ret;
1112         }
1113
1114         ads_msgfree(ads, res);
1115         talloc_destroy(ctx);
1116         return ret;
1117 }
1118
1119 /**
1120  * This adds a service principal name to an existing computer account
1121  * (found by hostname) in AD.
1122  * @param ads An initialized ADS_STRUCT
1123  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1124  * @param spn A string of the service principal to add, i.e. 'host'
1125  * @return 0 upon sucess, or non-zero if a failure occurs
1126  **/
1127
1128 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1129 {
1130         ADS_STATUS ret;
1131         TALLOC_CTX *ctx;
1132         LDAPMessage *res = NULL;
1133         char *host_spn, *host_upn, *psp1, *psp2;
1134         ADS_MODLIST mods;
1135         fstring my_fqdn;
1136         char *dn_string = NULL;
1137         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1138
1139         ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1140         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1141                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1142                         machine_name));
1143                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1144                         spn, machine_name, ads->config.realm));
1145                 ads_msgfree(ads, res);
1146                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1147         }
1148
1149         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1150         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1151                 ads_msgfree(ads, res);
1152                 return ADS_ERROR(LDAP_NO_MEMORY);
1153         }
1154
1155         name_to_fqdn(my_fqdn, machine_name);
1156         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1157                 talloc_destroy(ctx);
1158                 ads_msgfree(ads, res);
1159                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1160         }
1161         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1162                 talloc_destroy(ctx);
1163                 ads_msgfree(ads, res);
1164                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1165         }
1166
1167         /* Add the extra principal */
1168         psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1169         strupper_m(psp1);
1170         strlower_m(&psp1[strlen(spn)]);
1171         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1172         servicePrincipalName[0] = psp1;
1173         psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1174         strupper_m(psp2);
1175         strlower_m(&psp2[strlen(spn)]);
1176         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1177         servicePrincipalName[1] = psp2;
1178
1179         if (!(mods = ads_init_mods(ctx))) {
1180                 talloc_destroy(ctx);
1181                 ads_msgfree(ads, res);
1182                 return ADS_ERROR(LDAP_NO_MEMORY);
1183         }
1184         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1185         if (!ADS_ERR_OK(ret)) {
1186                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1187                 talloc_destroy(ctx);
1188                 ads_msgfree(ads, res);
1189                 return ret;
1190         }
1191         dn_string = ads_get_dn(ads, res);
1192         if (!dn_string) {
1193                 talloc_destroy(ctx);
1194                 ads_msgfree(ads, res);
1195                 return ADS_ERROR(LDAP_NO_MEMORY);
1196         }
1197         ret = ads_gen_mod(ads, dn_string, mods);
1198         ads_memfree(ads,dn_string);
1199         if (!ADS_ERR_OK(ret)) {
1200                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1201                 talloc_destroy(ctx);
1202                 ads_msgfree(ads, res);
1203                 return ret;
1204         }
1205
1206         talloc_destroy(ctx);
1207         ads_msgfree(ads, res);
1208         return ret;
1209 }
1210
1211 /**
1212  * adds a machine account to the ADS server
1213  * @param ads An intialized ADS_STRUCT
1214  * @param machine_name - the NetBIOS machine name of this account.
1215  * @param account_type A number indicating the type of account to create
1216  * @param org_unit The LDAP path in which to place this account
1217  * @return 0 upon success, or non-zero otherwise
1218 **/
1219
1220 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1221                                        uint32 account_type,
1222                                        const char *org_unit)
1223 {
1224         ADS_STATUS ret, status;
1225         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1226         TALLOC_CTX *ctx;
1227         ADS_MODLIST mods;
1228         const char *objectClass[] = {"top", "person", "organizationalPerson",
1229                                      "user", "computer", NULL};
1230         const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1231         char *psp, *psp2;
1232         unsigned acct_control;
1233         unsigned exists=0;
1234         fstring my_fqdn;
1235         LDAPMessage *res = NULL;
1236
1237         if (!(ctx = talloc_init("ads_add_machine_acct")))
1238                 return ADS_ERROR(LDAP_NO_MEMORY);
1239
1240         ret = ADS_ERROR(LDAP_NO_MEMORY);
1241
1242         name_to_fqdn(my_fqdn, machine_name);
1243
1244         status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1245         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1246                 char *dn_string = ads_get_dn(ads, res);
1247                 if (!dn_string) {
1248                         DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1249                         goto done;
1250                 }
1251                 new_dn = talloc_strdup(ctx, dn_string);
1252                 ads_memfree(ads,dn_string);
1253                 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1254                         machine_name));
1255                 exists=1;
1256         } else {
1257                 char *ou_str = ads_ou_string(org_unit);
1258                 if (!ou_str) {
1259                         DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1260                         goto done;
1261                 }
1262                 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str, 
1263                                 ads->config.bind_path);
1264
1265                 SAFE_FREE(ou_str);
1266         }
1267
1268         if (!new_dn) {
1269                 goto done;
1270         }
1271
1272         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1273                 goto done;
1274         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1275                 goto done;
1276         servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1277         psp = talloc_asprintf(ctx, "HOST/%s.%s", 
1278                                 machine_name, 
1279                                 ads->config.realm);
1280         strlower_m(&psp[5]);
1281         servicePrincipalName[1] = psp;
1282         servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1283         psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 
1284                                machine_name, 
1285                                ads->config.realm);
1286         strlower_m(&psp2[5]);
1287         servicePrincipalName[3] = psp2;
1288
1289         if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1290                 goto done;
1291         }
1292
1293         acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1294 #ifndef ENCTYPE_ARCFOUR_HMAC
1295         acct_control |= UF_USE_DES_KEY_ONLY;
1296 #endif
1297
1298         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1299                 goto done;
1300         }
1301
1302         if (!(mods = ads_init_mods(ctx))) {
1303                 goto done;
1304         }
1305
1306         if (!exists) {
1307                 ads_mod_str(ctx, &mods, "cn", machine_name);
1308                 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1309                 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1310                 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1311         }
1312         ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1313         ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1314         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1315         ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1316         ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1317
1318         if (!exists)  {
1319                 ret = ads_gen_add(ads, new_dn, mods);
1320         } else {
1321                 ret = ads_gen_mod(ads, new_dn, mods);
1322         }
1323
1324         if (!ADS_ERR_OK(ret)) {
1325                 goto done;
1326         }
1327
1328         /* Do not fail if we can't set security descriptor
1329          * it shouldn't be mandatory and probably we just 
1330          * don't have enough rights to do it.
1331          */
1332         if (!exists) {
1333                 status = ads_set_machine_sd(ads, machine_name, new_dn);
1334         
1335                 if (!ADS_ERR_OK(status)) {
1336                         DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1337                                         ads_errstr(status)));
1338                 }
1339         }
1340 done:
1341         ads_msgfree(ads, res);
1342         talloc_destroy(ctx);
1343         return ret;
1344 }
1345
1346 /*
1347   dump a binary result from ldap
1348 */
1349 static void dump_binary(const char *field, struct berval **values)
1350 {
1351         int i, j;
1352         for (i=0; values[i]; i++) {
1353                 printf("%s: ", field);
1354                 for (j=0; j<values[i]->bv_len; j++) {
1355                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1356                 }
1357                 printf("\n");
1358         }
1359 }
1360
1361 static void dump_guid(const char *field, struct berval **values)
1362 {
1363         int i;
1364         UUID_FLAT guid;
1365         for (i=0; values[i]; i++) {
1366                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1367                 printf("%s: %s\n", field, 
1368                        smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1369         }
1370 }
1371
1372 /*
1373   dump a sid result from ldap
1374 */
1375 static void dump_sid(const char *field, struct berval **values)
1376 {
1377         int i;
1378         for (i=0; values[i]; i++) {
1379                 DOM_SID sid;
1380                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1381                 printf("%s: %s\n", field, sid_string_static(&sid));
1382         }
1383 }
1384
1385 /*
1386   dump ntSecurityDescriptor
1387 */
1388 static void dump_sd(const char *filed, struct berval **values)
1389 {
1390         prs_struct ps;
1391         
1392         SEC_DESC   *psd = 0;
1393         TALLOC_CTX *ctx = 0;
1394
1395         if (!(ctx = talloc_init("sec_io_desc")))
1396                 return;
1397
1398         /* prepare data */
1399         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1400         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1401         prs_set_offset(&ps,0);
1402
1403         /* parse secdesc */
1404         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1405                 prs_mem_free(&ps);
1406                 talloc_destroy(ctx);
1407                 return;
1408         }
1409         if (psd) ads_disp_sd(psd);
1410
1411         prs_mem_free(&ps);
1412         talloc_destroy(ctx);
1413 }
1414
1415 /*
1416   dump a string result from ldap
1417 */
1418 static void dump_string(const char *field, char **values)
1419 {
1420         int i;
1421         for (i=0; values[i]; i++) {
1422                 printf("%s: %s\n", field, values[i]);
1423         }
1424 }
1425
1426 /*
1427   dump a field from LDAP on stdout
1428   used for debugging
1429 */
1430
1431 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1432 {
1433         const struct {
1434                 const char *name;
1435                 BOOL string;
1436                 void (*handler)(const char *, struct berval **);
1437         } handlers[] = {
1438                 {"objectGUID", False, dump_guid},
1439                 {"nTSecurityDescriptor", False, dump_sd},
1440                 {"dnsRecord", False, dump_binary},
1441                 {"objectSid", False, dump_sid},
1442                 {"tokenGroups", False, dump_sid},
1443                 {NULL, True, NULL}
1444         };
1445         int i;
1446
1447         if (!field) { /* must be end of an entry */
1448                 printf("\n");
1449                 return False;
1450         }
1451
1452         for (i=0; handlers[i].name; i++) {
1453                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1454                         if (!values) /* first time, indicate string or not */
1455                                 return handlers[i].string;
1456                         handlers[i].handler(field, (struct berval **) values);
1457                         break;
1458                 }
1459         }
1460         if (!handlers[i].name) {
1461                 if (!values) /* first time, indicate string conversion */
1462                         return True;
1463                 dump_string(field, (char **)values);
1464         }
1465         return False;
1466 }
1467
1468 /**
1469  * Dump a result from LDAP on stdout
1470  *  used for debugging
1471  * @param ads connection to ads server
1472  * @param res Results to dump
1473  **/
1474
1475 void ads_dump(ADS_STRUCT *ads, void *res)
1476 {
1477         ads_process_results(ads, res, ads_dump_field, NULL);
1478 }
1479
1480 /**
1481  * Walk through results, calling a function for each entry found.
1482  *  The function receives a field name, a berval * array of values,
1483  *  and a data area passed through from the start.  The function is
1484  *  called once with null for field and values at the end of each
1485  *  entry.
1486  * @param ads connection to ads server
1487  * @param res Results to process
1488  * @param fn Function for processing each result
1489  * @param data_area user-defined area to pass to function
1490  **/
1491 void ads_process_results(ADS_STRUCT *ads, void *res,
1492                          BOOL(*fn)(char *, void **, void *),
1493                          void *data_area)
1494 {
1495         void *msg;
1496         TALLOC_CTX *ctx;
1497
1498         if (!(ctx = talloc_init("ads_process_results")))
1499                 return;
1500
1501         for (msg = ads_first_entry(ads, res); msg; 
1502              msg = ads_next_entry(ads, msg)) {
1503                 char *utf8_field;
1504                 BerElement *b;
1505         
1506                 for (utf8_field=ldap_first_attribute(ads->ld,
1507                                                      (LDAPMessage *)msg,&b); 
1508                      utf8_field;
1509                      utf8_field=ldap_next_attribute(ads->ld,
1510                                                     (LDAPMessage *)msg,b)) {
1511                         struct berval **ber_vals;
1512                         char **str_vals, **utf8_vals;
1513                         char *field;
1514                         BOOL string; 
1515
1516                         pull_utf8_talloc(ctx, &field, utf8_field);
1517                         string = fn(field, NULL, data_area);
1518
1519                         if (string) {
1520                                 utf8_vals = ldap_get_values(ads->ld,
1521                                                  (LDAPMessage *)msg, field);
1522                                 str_vals = ads_pull_strvals(ctx, 
1523                                                   (const char **) utf8_vals);
1524                                 fn(field, (void **) str_vals, data_area);
1525                                 ldap_value_free(utf8_vals);
1526                         } else {
1527                                 ber_vals = ldap_get_values_len(ads->ld, 
1528                                                  (LDAPMessage *)msg, field);
1529                                 fn(field, (void **) ber_vals, data_area);
1530
1531                                 ldap_value_free_len(ber_vals);
1532                         }
1533                         ldap_memfree(utf8_field);
1534                 }
1535                 ber_free(b, 0);
1536                 talloc_destroy_pool(ctx);
1537                 fn(NULL, NULL, data_area); /* completed an entry */
1538
1539         }
1540         talloc_destroy(ctx);
1541 }
1542
1543 /**
1544  * count how many replies are in a LDAPMessage
1545  * @param ads connection to ads server
1546  * @param res Results to count
1547  * @return number of replies
1548  **/
1549 int ads_count_replies(ADS_STRUCT *ads, void *res)
1550 {
1551         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1552 }
1553
1554 /**
1555  * Join a machine to a realm
1556  *  Creates the machine account and sets the machine password
1557  * @param ads connection to ads server
1558  * @param machine name of host to add
1559  * @param org_unit Organizational unit to place machine in
1560  * @return status of join
1561  **/
1562 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name, 
1563                           uint32 account_type, const char *org_unit)
1564 {
1565         ADS_STATUS status;
1566         LDAPMessage *res = NULL;
1567         char *machine;
1568
1569         /* machine name must be lowercase */
1570         machine = strdup(machine_name);
1571         strlower_m(machine);
1572
1573         /*
1574         status = ads_find_machine_acct(ads, (void **)&res, machine);
1575         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1576                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1577                 status = ads_leave_realm(ads, machine);
1578                 if (!ADS_ERR_OK(status)) {
1579                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1580                                   machine, ads->config.realm));
1581                         return status;
1582                 }
1583         }
1584         */
1585
1586         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1587         if (!ADS_ERR_OK(status)) {
1588                 DEBUG(0, ("ads_add_machine_acct (%s): %s\n", machine, ads_errstr(status)));
1589                 SAFE_FREE(machine);
1590                 return status;
1591         }
1592
1593         status = ads_find_machine_acct(ads, (void **)&res, machine);
1594         if (!ADS_ERR_OK(status)) {
1595                 DEBUG(0, ("Host account test failed for machine %s\n", machine));
1596                 SAFE_FREE(machine);
1597                 return status;
1598         }
1599
1600         SAFE_FREE(machine);
1601         ads_msgfree(ads, res);
1602
1603         return status;
1604 }
1605
1606 /**
1607  * Delete a machine from the realm
1608  * @param ads connection to ads server
1609  * @param hostname Machine to remove
1610  * @return status of delete
1611  **/
1612 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1613 {
1614         ADS_STATUS status;
1615         void *res, *msg;
1616         char *hostnameDN, *host; 
1617         int rc;
1618
1619         /* hostname must be lowercase */
1620         host = strdup(hostname);
1621         strlower_m(host);
1622
1623         status = ads_find_machine_acct(ads, &res, host);
1624         if (!ADS_ERR_OK(status)) {
1625             DEBUG(0, ("Host account for %s does not exist.\n", host));
1626             return status;
1627         }
1628
1629         msg = ads_first_entry(ads, res);
1630         if (!msg) {
1631                 return ADS_ERROR_SYSTEM(ENOENT);
1632         }
1633
1634         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1635         rc = ldap_delete_s(ads->ld, hostnameDN);
1636         ads_memfree(ads, hostnameDN);
1637         if (rc != LDAP_SUCCESS) {
1638                 return ADS_ERROR(rc);
1639         }
1640
1641         status = ads_find_machine_acct(ads, &res, host);
1642         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1643                 DEBUG(0, ("Failed to remove host account.\n"));
1644                 return status;
1645         }
1646
1647         free(host);
1648
1649         return status;
1650 }
1651
1652 /**
1653  * add machine account to existing security descriptor 
1654  * @param ads connection to ads server
1655  * @param hostname machine to add
1656  * @param dn DN of security descriptor
1657  * @return status
1658  **/
1659 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1660 {
1661         const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1662         char           *expr     = 0;
1663         size_t          sd_size = 0;
1664         struct berval   bval = {0, NULL};
1665         prs_struct      ps_wire;
1666         char           *escaped_hostname = escape_ldap_string_alloc(hostname);
1667
1668         LDAPMessage *res  = 0;
1669         LDAPMessage *msg  = 0;
1670         ADS_MODLIST  mods = 0;
1671
1672         NTSTATUS    status;
1673         ADS_STATUS  ret;
1674         DOM_SID     sid;
1675         SEC_DESC   *psd = NULL;
1676         TALLOC_CTX *ctx = NULL; 
1677
1678         /* Avoid segmentation fault in prs_mem_free if
1679          * we have to bail out before prs_init */
1680         ps_wire.is_dynamic = False;
1681
1682         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1683
1684         ret = ADS_ERROR(LDAP_SUCCESS);
1685
1686         if (!escaped_hostname) {
1687                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1688         }
1689
1690         if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1691                 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1692                 SAFE_FREE(escaped_hostname);
1693                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1694         }
1695
1696         SAFE_FREE(escaped_hostname);
1697
1698         ret = ads_search(ads, (void *) &res, expr, attrs);
1699
1700         if (!ADS_ERR_OK(ret)) return ret;
1701
1702         if ( !(msg = ads_first_entry(ads, res) )) {
1703                 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1704                 goto ads_set_sd_error;
1705         }
1706
1707         if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1708                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1709                 goto ads_set_sd_error;
1710         }
1711
1712         if (!(ctx = talloc_init("sec_io_desc"))) {
1713                 ret =  ADS_ERROR(LDAP_NO_MEMORY);
1714                 goto ads_set_sd_error;
1715         }
1716
1717         if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1718                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1719                 goto ads_set_sd_error;
1720         }
1721
1722         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1723
1724         if (!NT_STATUS_IS_OK(status)) {
1725                 ret = ADS_ERROR_NT(status);
1726                 goto ads_set_sd_error;
1727         }
1728
1729         if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1730                 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1731         }
1732
1733         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1734                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1735                 goto ads_set_sd_error;
1736         }
1737
1738 #if 0
1739         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1740 #endif
1741         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1742
1743         bval.bv_len = prs_offset(&ps_wire);
1744         bval.bv_val = talloc(ctx, bval.bv_len);
1745         if (!bval.bv_val) {
1746                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1747                 goto ads_set_sd_error;
1748         }
1749
1750         prs_set_offset(&ps_wire, 0);
1751
1752         if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1753                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1754                 goto ads_set_sd_error;          
1755         }
1756
1757         ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1758         if (ADS_ERR_OK(ret)) {
1759                 ret = ads_gen_mod(ads, dn, mods);
1760         }
1761
1762 ads_set_sd_error:
1763         ads_msgfree(ads, res);
1764         prs_mem_free(&ps_wire);
1765         talloc_destroy(ctx);
1766         return ret;
1767 }
1768
1769 /**
1770  * pull the first entry from a ADS result
1771  * @param ads connection to ads server
1772  * @param res Results of search
1773  * @return first entry from result
1774  **/
1775 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1776 {
1777         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1778 }
1779
1780 /**
1781  * pull the next entry from a ADS result
1782  * @param ads connection to ads server
1783  * @param res Results of search
1784  * @return next entry from result
1785  **/
1786 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1787 {
1788         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1789 }
1790
1791 /**
1792  * pull a single string from a ADS result
1793  * @param ads connection to ads server
1794  * @param mem_ctx TALLOC_CTX to use for allocating result string
1795  * @param msg Results of search
1796  * @param field Attribute to retrieve
1797  * @return Result string in talloc context
1798  **/
1799 char *ads_pull_string(ADS_STRUCT *ads, 
1800                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1801 {
1802         char **values;
1803         char *ret = NULL;
1804         char *ux_string;
1805         size_t rc;
1806
1807         values = ldap_get_values(ads->ld, msg, field);
1808         if (!values)
1809                 return NULL;
1810         
1811         if (values[0]) {
1812                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1813                                       values[0]);
1814                 if (rc != (size_t)-1)
1815                         ret = ux_string;
1816                 
1817         }
1818         ldap_value_free(values);
1819         return ret;
1820 }
1821
1822 /**
1823  * pull an array of strings from a ADS result
1824  * @param ads connection to ads server
1825  * @param mem_ctx TALLOC_CTX to use for allocating result string
1826  * @param msg Results of search
1827  * @param field Attribute to retrieve
1828  * @return Result strings in talloc context
1829  **/
1830 char **ads_pull_strings(ADS_STRUCT *ads, 
1831                         TALLOC_CTX *mem_ctx, void *msg, const char *field,
1832                         size_t *num_values)
1833 {
1834         char **values;
1835         char **ret = NULL;
1836         int i;
1837
1838         values = ldap_get_values(ads->ld, msg, field);
1839         if (!values)
1840                 return NULL;
1841
1842         *num_values = ldap_count_values(values);
1843
1844         ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
1845         if (!ret) {
1846                 ldap_value_free(values);
1847                 return NULL;
1848         }
1849
1850         for (i=0;i<*num_values;i++) {
1851                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1852                         ldap_value_free(values);
1853                         return NULL;
1854                 }
1855         }
1856         ret[i] = NULL;
1857
1858         ldap_value_free(values);
1859         return ret;
1860 }
1861
1862 /**
1863  * pull an array of strings from a ADS result 
1864  *  (handle large multivalue attributes with range retrieval)
1865  * @param ads connection to ads server
1866  * @param mem_ctx TALLOC_CTX to use for allocating result string
1867  * @param msg Results of search
1868  * @param field Attribute to retrieve
1869  * @param current_strings strings returned by a previous call to this function
1870  * @param next_attribute The next query should ask for this attribute
1871  * @param num_values How many values did we get this time?
1872  * @param more_values Are there more values to get?
1873  * @return Result strings in talloc context
1874  **/
1875 char **ads_pull_strings_range(ADS_STRUCT *ads, 
1876                               TALLOC_CTX *mem_ctx,
1877                               void *msg, const char *field,
1878                               char **current_strings,
1879                               const char **next_attribute,
1880                               size_t *num_strings,
1881                               BOOL *more_strings)
1882 {
1883         char *attr;
1884         char *expected_range_attrib, *range_attr;
1885         BerElement *ptr = NULL;
1886         char **strings;
1887         char **new_strings;
1888         size_t num_new_strings;
1889         unsigned long int range_start;
1890         unsigned long int range_end;
1891         
1892         /* we might have been given the whole lot anyway */
1893         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1894                 *more_strings = False;
1895                 return strings;
1896         }
1897
1898         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1899
1900         /* look for Range result */
1901         for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr); 
1902              attr; 
1903              attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1904                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1905                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1906                         range_attr = attr;
1907                         break;
1908                 }
1909                 ldap_memfree(attr);
1910         }
1911         if (!attr) {
1912                 ber_free(ptr, 0);
1913                 /* nothing here - this field is just empty */
1914                 *more_strings = False;
1915                 return NULL;
1916         }
1917         
1918         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
1919                    &range_start, &range_end) == 2) {
1920                 *more_strings = True;
1921         } else {
1922                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
1923                            &range_start) == 1) {
1924                         *more_strings = False;
1925                 } else {
1926                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
1927                                   range_attr));
1928                         ldap_memfree(range_attr);
1929                         *more_strings = False;
1930                         return NULL;
1931                 }
1932         }
1933
1934         if ((*num_strings) != range_start) {
1935                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1936                           " - aborting range retreival\n",
1937                           range_attr, *num_strings + 1, range_start));
1938                 ldap_memfree(range_attr);
1939                 *more_strings = False;
1940                 return NULL;
1941         }
1942
1943         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1944         
1945         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1946                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1947                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
1948                           range_attr, (unsigned long int)range_end - range_start + 1, 
1949                           (unsigned long int)num_new_strings));
1950                 ldap_memfree(range_attr);
1951                 *more_strings = False;
1952                 return NULL;
1953         }
1954
1955         strings = talloc_realloc(mem_ctx, current_strings,
1956                                  sizeof(*current_strings) *
1957                                  (*num_strings + num_new_strings));
1958         
1959         if (strings == NULL) {
1960                 ldap_memfree(range_attr);
1961                 *more_strings = False;
1962                 return NULL;
1963         }
1964         
1965         memcpy(&strings[*num_strings], new_strings,
1966                sizeof(*new_strings) * num_new_strings);
1967
1968         (*num_strings) += num_new_strings;
1969
1970         if (*more_strings) {
1971                 *next_attribute = talloc_asprintf(mem_ctx,
1972                                                   "%s;range=%d-*", 
1973                                                   field,
1974                                                   *num_strings);
1975                 
1976                 if (!*next_attribute) {
1977                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1978                         ldap_memfree(range_attr);
1979                         *more_strings = False;
1980                         return NULL;
1981                 }
1982         }
1983
1984         ldap_memfree(range_attr);
1985
1986         return strings;
1987 }
1988
1989 /**
1990  * pull a single uint32 from a ADS result
1991  * @param ads connection to ads server
1992  * @param msg Results of search
1993  * @param field Attribute to retrieve
1994  * @param v Pointer to int to store result
1995  * @return boolean inidicating success
1996 */
1997 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
1998                      void *msg, const char *field, uint32 *v)
1999 {
2000         char **values;
2001
2002         values = ldap_get_values(ads->ld, msg, field);
2003         if (!values)
2004                 return False;
2005         if (!values[0]) {
2006                 ldap_value_free(values);
2007                 return False;
2008         }
2009
2010         *v = atoi(values[0]);
2011         ldap_value_free(values);
2012         return True;
2013 }
2014
2015 /**
2016  * pull a single objectGUID from an ADS result
2017  * @param ads connection to ADS server
2018  * @param msg results of search
2019  * @param guid 37-byte area to receive text guid
2020  * @return boolean indicating success
2021  **/
2022 BOOL ads_pull_guid(ADS_STRUCT *ads,
2023                    void *msg, struct uuid *guid)
2024 {
2025         char **values;
2026         UUID_FLAT flat_guid;
2027
2028         values = ldap_get_values(ads->ld, msg, "objectGUID");
2029         if (!values)
2030                 return False;
2031         
2032         if (values[0]) {
2033                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2034                 smb_uuid_unpack(flat_guid, guid);
2035                 ldap_value_free(values);
2036                 return True;
2037         }
2038         ldap_value_free(values);
2039         return False;
2040
2041 }
2042
2043
2044 /**
2045  * pull a single DOM_SID from a ADS result
2046  * @param ads connection to ads server
2047  * @param msg Results of search
2048  * @param field Attribute to retrieve
2049  * @param sid Pointer to sid to store result
2050  * @return boolean inidicating success
2051 */
2052 BOOL ads_pull_sid(ADS_STRUCT *ads, 
2053                   void *msg, const char *field, DOM_SID *sid)
2054 {
2055         struct berval **values;
2056         BOOL ret = False;
2057
2058         values = ldap_get_values_len(ads->ld, msg, field);
2059
2060         if (!values)
2061                 return False;
2062
2063         if (values[0])
2064                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2065         
2066         ldap_value_free_len(values);
2067         return ret;
2068 }
2069
2070 /**
2071  * pull an array of DOM_SIDs from a ADS result
2072  * @param ads connection to ads server
2073  * @param mem_ctx TALLOC_CTX for allocating sid array
2074  * @param msg Results of search
2075  * @param field Attribute to retrieve
2076  * @param sids pointer to sid array to allocate
2077  * @return the count of SIDs pulled
2078  **/
2079 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2080                   void *msg, const char *field, DOM_SID **sids)
2081 {
2082         struct berval **values;
2083         BOOL ret;
2084         int count, i;
2085
2086         values = ldap_get_values_len(ads->ld, msg, field);
2087
2088         if (!values)
2089                 return 0;
2090
2091         for (i=0; values[i]; i++)
2092                 /* nop */ ;
2093
2094         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
2095         if (!(*sids)) {
2096                 ldap_value_free_len(values);
2097                 return 0;
2098         }
2099
2100         count = 0;
2101         for (i=0; values[i]; i++) {
2102                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2103                 if (ret) {
2104                         fstring sid;
2105                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2106                         count++;
2107                 }
2108         }
2109         
2110         ldap_value_free_len(values);
2111         return count;
2112 }
2113
2114 /**
2115  * pull a SEC_DESC from a ADS result
2116  * @param ads connection to ads server
2117  * @param mem_ctx TALLOC_CTX for allocating sid array
2118  * @param msg Results of search
2119  * @param field Attribute to retrieve
2120  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2121  * @return boolean inidicating success
2122 */
2123 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2124                   void *msg, const char *field, SEC_DESC **sd)
2125 {
2126         struct berval **values;
2127         prs_struct      ps;
2128         BOOL ret = False;
2129
2130         values = ldap_get_values_len(ads->ld, msg, field);
2131
2132         if (!values) return False;
2133
2134         if (values[0]) {
2135                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2136                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2137                 prs_set_offset(&ps,0);
2138
2139                 ret = sec_io_desc("sd", sd, &ps, 1);
2140         }
2141         
2142         ldap_value_free_len(values);
2143         return ret;
2144 }
2145
2146 /* 
2147  * in order to support usernames longer than 21 characters we need to 
2148  * use both the sAMAccountName and the userPrincipalName attributes 
2149  * It seems that not all users have the userPrincipalName attribute set
2150  *
2151  * @param ads connection to ads server
2152  * @param mem_ctx TALLOC_CTX for allocating sid array
2153  * @param msg Results of search
2154  * @return the username
2155  */
2156 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2157 {
2158         char *ret, *p;
2159
2160         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2161         if (ret && (p = strchr(ret, '@'))) {
2162                 *p = 0;
2163                 return ret;
2164         }
2165         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2166 }
2167
2168
2169 /**
2170  * find the update serial number - this is the core of the ldap cache
2171  * @param ads connection to ads server
2172  * @param ads connection to ADS server
2173  * @param usn Pointer to retrieved update serial number
2174  * @return status of search
2175  **/
2176 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2177 {
2178         const char *attrs[] = {"highestCommittedUSN", NULL};
2179         ADS_STATUS status;
2180         void *res;
2181
2182         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2183         if (!ADS_ERR_OK(status)) 
2184                 return status;
2185
2186         if (ads_count_replies(ads, res) != 1) {
2187                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2188         }
2189
2190         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2191         ads_msgfree(ads, res);
2192         return ADS_SUCCESS;
2193 }
2194
2195 /* parse a ADS timestring - typical string is
2196    '20020917091222.0Z0' which means 09:12.22 17th September
2197    2002, timezone 0 */
2198 static time_t ads_parse_time(const char *str)
2199 {
2200         struct tm tm;
2201
2202         ZERO_STRUCT(tm);
2203
2204         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2205                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2206                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2207                 return 0;
2208         }
2209         tm.tm_year -= 1900;
2210         tm.tm_mon -= 1;
2211
2212         return timegm(&tm);
2213 }
2214
2215
2216 /**
2217  * Find the servers name and realm - this can be done before authentication 
2218  *  The ldapServiceName field on w2k  looks like this:
2219  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2220  * @param ads connection to ads server
2221  * @return status of search
2222  **/
2223 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2224 {
2225         const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2226         ADS_STATUS status;
2227         void *res;
2228         char *value;
2229         char *p;
2230         char *timestr;
2231         TALLOC_CTX *ctx;
2232
2233         if (!(ctx = talloc_init("ads_server_info"))) {
2234                 return ADS_ERROR(LDAP_NO_MEMORY);
2235         }
2236
2237         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2238         if (!ADS_ERR_OK(status)) {
2239                 talloc_destroy(ctx);
2240                 return status;
2241         }
2242
2243         value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2244         if (!value) {
2245                 ads_msgfree(ads, res);
2246                 talloc_destroy(ctx);
2247                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2248         }
2249
2250         timestr = ads_pull_string(ads, ctx, res, "currentTime");
2251         if (!timestr) {
2252                 ads_msgfree(ads, res);
2253                 talloc_destroy(ctx);
2254                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2255         }
2256
2257         ads_msgfree(ads, res);
2258
2259         p = strchr(value, ':');
2260         if (!p) {
2261                 talloc_destroy(ctx);
2262                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2263                           "so was deemed invalid\n"));
2264                 return ADS_ERROR(LDAP_DECODING_ERROR);
2265         }
2266
2267         SAFE_FREE(ads->config.ldap_server_name);
2268
2269         ads->config.ldap_server_name = strdup(p+1);
2270         p = strchr(ads->config.ldap_server_name, '$');
2271         if (!p || p[1] != '@') {
2272                 talloc_destroy(ctx);
2273                 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2274                           " so was deemed invalid\n", ads->config.ldap_server_name));
2275                 SAFE_FREE(ads->config.ldap_server_name);
2276                 return ADS_ERROR(LDAP_DECODING_ERROR);
2277         }
2278
2279         *p = 0;
2280
2281         SAFE_FREE(ads->config.realm);
2282         SAFE_FREE(ads->config.bind_path);
2283
2284         ads->config.realm = strdup(p+2);
2285         ads->config.bind_path = ads_build_dn(ads->config.realm);
2286
2287         DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
2288                  ads->config.ldap_server_name, ads->config.realm,
2289                  ads->config.bind_path));
2290
2291         ads->config.current_time = ads_parse_time(timestr);
2292
2293         if (ads->config.current_time != 0) {
2294                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2295                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2296         }
2297
2298         talloc_destroy(ctx);
2299
2300         return ADS_SUCCESS;
2301 }
2302
2303 /**
2304  * find the domain sid for our domain
2305  * @param ads connection to ads server
2306  * @param sid Pointer to domain sid
2307  * @return status of search
2308  **/
2309 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2310 {
2311         const char *attrs[] = {"objectSid", NULL};
2312         void *res;
2313         ADS_STATUS rc;
2314
2315         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2316                            attrs, &res);
2317         if (!ADS_ERR_OK(rc)) return rc;
2318         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2319                 ads_msgfree(ads, res);
2320                 return ADS_ERROR_SYSTEM(ENOENT);
2321         }
2322         ads_msgfree(ads, res);
2323         
2324         return ADS_SUCCESS;
2325 }
2326
2327 /* this is rather complex - we need to find the allternate (netbios) name
2328    for the domain, but there isn't a simple query to do this. Instead
2329    we look for the principle names on the DCs account and find one that has 
2330    the right form, then extract the netbios name of the domain from that
2331
2332    NOTE! better method is this:
2333
2334 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName 
2335
2336 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2337
2338 */
2339 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2340 {
2341         char *expr;
2342         ADS_STATUS rc;
2343         char **principles;
2344         char *prefix;
2345         int prefix_length;
2346         int i;
2347         void *res;
2348         const char *attrs[] = {"servicePrincipalName", NULL};
2349         int num_principals;
2350
2351         (*workgroup) = NULL;
2352
2353         asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))", 
2354                  ads->config.ldap_server_name, ads->config.realm);
2355         rc = ads_search(ads, &res, expr, attrs);
2356         free(expr);
2357
2358         if (!ADS_ERR_OK(rc)) {
2359                 return rc;
2360         }
2361
2362         principles = ads_pull_strings(ads, mem_ctx, res,
2363                                       "servicePrincipalName", &num_principals);
2364
2365         ads_msgfree(ads, res);
2366
2367         if (!principles) {
2368                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2369         }
2370
2371         asprintf(&prefix, "HOST/%s.%s/", 
2372                  ads->config.ldap_server_name, 
2373                  ads->config.realm);
2374
2375         prefix_length = strlen(prefix);
2376
2377         for (i=0;principles[i]; i++) {
2378                 if (strnequal(principles[i], prefix, prefix_length) &&
2379                     !strequal(ads->config.realm, principles[i]+prefix_length) &&
2380                     !strchr(principles[i]+prefix_length, '.')) {
2381                         /* found an alternate (short) name for the domain. */
2382                         DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2383                                  principles[i]+prefix_length, 
2384                                  ads->config.realm));
2385                         (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2386                         break;
2387                 }
2388         }
2389         free(prefix);
2390
2391         if (!*workgroup) {
2392                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2393         }
2394         
2395         return ADS_SUCCESS;
2396 }
2397
2398 #endif