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