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