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