Fix a misleading DEBUG message.
[kai/samba.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7    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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25
26 #ifdef HAVE_LDAP
27
28 /**
29  * @file ldap.c
30  * @brief basic ldap client-side routines for ads server communications
31  *
32  * The routines contained here should do the necessary ldap calls for
33  * ads setups.
34  * 
35  * Important note: attribute names passed into ads_ routines must
36  * already be in UTF-8 format.  We do not convert them because in almost
37  * all cases, they are just ascii (which is represented with the same
38  * codepoints in UTF-8).  This may have to change at some point
39  **/
40
41
42 #define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
43
44 static SIG_ATOMIC_T gotalarm;
45
46 /***************************************************************
47  Signal function to tell us we timed out.
48 ****************************************************************/
49
50 static void gotalarm_sig(void)
51 {
52         gotalarm = 1;
53 }
54
55  LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
56 {
57         LDAP *ldp = NULL;
58
59         /* Setup timeout */
60         gotalarm = 0;
61         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
62         alarm(to);
63         /* End setup timeout. */
64
65         ldp = ldap_open(server, port);
66
67         if (ldp == NULL) {
68                 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
69                          server, port, strerror(errno)));
70         }
71
72         /* Teardown timeout. */
73         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
74         alarm(0);
75
76         return ldp;
77 }
78
79 static int ldap_search_with_timeout(LDAP *ld,
80                                     LDAP_CONST char *base,
81                                     int scope,
82                                     LDAP_CONST char *filter,
83                                     char **attrs,
84                                     int attrsonly,
85                                     LDAPControl **sctrls,
86                                     LDAPControl **cctrls,
87                                     int sizelimit,
88                                     LDAPMessage **res )
89 {
90         struct timeval timeout;
91         int result;
92
93         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
94         timeout.tv_sec = lp_ldap_timeout();
95         timeout.tv_usec = 0;
96
97         /* Setup alarm timeout.... Do we need both of these ? JRA. */
98         gotalarm = 0;
99         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
100         alarm(lp_ldap_timeout());
101         /* End setup timeout. */
102
103         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
104                                    attrsonly, sctrls, cctrls, &timeout,
105                                    sizelimit, res);
106
107         /* Teardown timeout. */
108         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
109         alarm(0);
110
111         if (gotalarm != 0)
112                 return LDAP_TIMELIMIT_EXCEEDED;
113
114         return result;
115 }
116
117 /**********************************************
118  Do client and server sitename match ?
119 **********************************************/
120
121 bool ads_sitename_match(ADS_STRUCT *ads)
122 {
123         if (ads->config.server_site_name == NULL &&
124             ads->config.client_site_name == NULL ) {
125                 DEBUG(10,("ads_sitename_match: both null\n"));
126                 return True;
127         }
128         if (ads->config.server_site_name &&
129             ads->config.client_site_name &&
130             strequal(ads->config.server_site_name,
131                      ads->config.client_site_name)) {
132                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
133                 return True;
134         }
135         DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
136                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
137                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
138         return False;
139 }
140
141 /**********************************************
142  Is this the closest DC ?
143 **********************************************/
144
145 bool ads_closest_dc(ADS_STRUCT *ads)
146 {
147         if (ads->config.flags & ADS_CLOSEST) {
148                 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
149                 return True;
150         }
151
152         /* not sure if this can ever happen */
153         if (ads_sitename_match(ads)) {
154                 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
155                 return True;
156         }
157
158         DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", 
159                 ads->config.ldap_server_name));
160
161         return False;
162 }
163
164
165 /*
166   try a connection to a given ldap server, returning True and setting the servers IP
167   in the ads struct if successful
168  */
169 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
170 {
171         char *srv;
172         struct cldap_netlogon_reply cldap_reply;
173
174         if (!server || !*server) {
175                 return False;
176         }
177         
178         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
179                 server, ads->server.realm));
180
181         /* this copes with inet_ntoa brokenness */
182         
183         srv = SMB_STRDUP(server);
184
185         ZERO_STRUCT( cldap_reply );
186
187         if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
188                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
189                 SAFE_FREE( srv );
190                 return False;
191         }
192
193         /* Check the CLDAP reply flags */
194
195         if ( !(cldap_reply.flags & ADS_LDAP) ) {
196                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
197                         srv));
198                 SAFE_FREE( srv );
199                 return False;
200         }
201
202         /* Fill in the ads->config values */
203
204         SAFE_FREE(ads->config.realm);
205         SAFE_FREE(ads->config.bind_path);
206         SAFE_FREE(ads->config.ldap_server_name);
207         SAFE_FREE(ads->config.server_site_name);
208         SAFE_FREE(ads->config.client_site_name);
209         SAFE_FREE(ads->server.workgroup);
210
211         ads->config.flags              = cldap_reply.flags;
212         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.hostname);
213         strupper_m(cldap_reply.domain);
214         ads->config.realm              = SMB_STRDUP(cldap_reply.domain);
215         ads->config.bind_path          = ads_build_dn(ads->config.realm);
216         if (*cldap_reply.server_site_name) {
217                 ads->config.server_site_name =
218                         SMB_STRDUP(cldap_reply.server_site_name);
219         }
220         if (*cldap_reply.client_site_name) {
221                 ads->config.client_site_name =
222                         SMB_STRDUP(cldap_reply.client_site_name);
223         }
224         ads->server.workgroup          = SMB_STRDUP(cldap_reply.netbios_domain);
225
226         ads->ldap.port = LDAP_PORT;
227         if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
228                 DEBUG(1,("ads_try_connect: unable to convert %s "
229                         "to an address\n",
230                         srv));
231                 SAFE_FREE( srv );
232                 return False;
233         }
234
235         SAFE_FREE(srv);
236
237         /* Store our site name. */
238         sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
239
240         return True;
241 }
242
243 /**********************************************************************
244  Try to find an AD dc using our internal name resolution routines
245  Try the realm first and then then workgroup name if netbios is not 
246  disabled
247 **********************************************************************/
248
249 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
250 {
251         const char *c_realm;
252         int count, i=0;
253         struct ip_service *ip_list;
254         const char *realm;
255         bool got_realm = False;
256         bool use_own_domain = False;
257         char *sitename;
258         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
259
260         /* if the realm and workgroup are both empty, assume they are ours */
261
262         /* realm */
263         c_realm = ads->server.realm;
264
265         if ( !c_realm || !*c_realm ) {
266                 /* special case where no realm and no workgroup means our own */
267                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
268                         use_own_domain = True;
269                         c_realm = lp_realm();
270                 }
271         }
272
273         if (c_realm && *c_realm)
274                 got_realm = True;
275
276         /* we need to try once with the realm name and fallback to the
277            netbios domain name if we fail (if netbios has not been disabled */
278
279         if ( !got_realm && !lp_disable_netbios() ) {
280                 c_realm = ads->server.workgroup;
281                 if (!c_realm || !*c_realm) {
282                         if ( use_own_domain )
283                                 c_realm = lp_workgroup();
284                 }
285
286                 if ( !c_realm || !*c_realm ) {
287                         DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
288                         return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
289                 }
290         }
291
292         realm = c_realm;
293
294         sitename = sitename_fetch(realm);
295
296  again:
297
298         DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
299                 (got_realm ? "realm" : "domain"), realm));
300
301         status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
302         if (!NT_STATUS_IS_OK(status)) {
303                 /* fall back to netbios if we can */
304                 if ( got_realm && !lp_disable_netbios() ) {
305                         got_realm = False;
306                         goto again;
307                 }
308
309                 SAFE_FREE(sitename);
310                 return status;
311         }
312
313         /* if we fail this loop, then giveup since all the IP addresses returned were dead */
314         for ( i=0; i<count; i++ ) {
315                 char server[INET6_ADDRSTRLEN];
316
317                 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
318
319                 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
320                         continue;
321
322                 if (!got_realm) {
323                         /* realm in this case is a workgroup name. We need
324                            to ignore any IP addresses in the negative connection
325                            cache that match ip addresses returned in the ad realm
326                            case. It sucks that I have to reproduce the logic above... */
327                         c_realm = ads->server.realm;
328                         if ( !c_realm || !*c_realm ) {
329                                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
330                                         c_realm = lp_realm();
331                                 }
332                         }
333                         if (c_realm && *c_realm &&
334                                         !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
335                                 /* Ensure we add the workgroup name for this
336                                    IP address as negative too. */
337                                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
338                                 continue;
339                         }
340                 }
341
342                 if ( ads_try_connect(ads, server) ) {
343                         SAFE_FREE(ip_list);
344                         SAFE_FREE(sitename);
345                         return NT_STATUS_OK;
346                 }
347                 
348                 /* keep track of failures */
349                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
350         }
351
352         SAFE_FREE(ip_list);
353
354         /* In case we failed to contact one of our closest DC on our site we
355          * need to try to find another DC, retry with a site-less SRV DNS query
356          * - Guenther */
357
358         if (sitename) {
359                 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
360                                 "trying to find another DC\n", sitename));
361                 SAFE_FREE(sitename);
362                 namecache_delete(realm, 0x1C);
363                 goto again;
364         }
365
366         return NT_STATUS_NO_LOGON_SERVERS;
367 }
368
369
370 /**
371  * Connect to the LDAP server
372  * @param ads Pointer to an existing ADS_STRUCT
373  * @return status of connection
374  **/
375 ADS_STATUS ads_connect(ADS_STRUCT *ads)
376 {
377         int version = LDAP_VERSION3;
378         ADS_STATUS status;
379         NTSTATUS ntstatus;
380         char addr[INET6_ADDRSTRLEN];
381
382         ZERO_STRUCT(ads->ldap);
383         ads->ldap.last_attempt  = time(NULL);
384         ads->ldap.wrap_type     = ADS_SASLWRAP_TYPE_PLAIN;
385
386         /* try with a user specified server */
387
388         if (ads->server.ldap_server &&
389             ads_try_connect(ads, ads->server.ldap_server)) {
390                 goto got_connection;
391         }
392
393         ntstatus = ads_find_dc(ads);
394         if (NT_STATUS_IS_OK(ntstatus)) {
395                 goto got_connection;
396         }
397
398         return ADS_ERROR_NT(ntstatus);
399
400 got_connection:
401
402         print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
403         DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
404
405         if (!ads->auth.user_name) {
406                 /* Must use the userPrincipalName value here or sAMAccountName
407                    and not servicePrincipalName; found by Guenther Deschner */
408
409                 asprintf(&ads->auth.user_name, "%s$", global_myname() );
410         }
411
412         if (!ads->auth.realm) {
413                 ads->auth.realm = SMB_STRDUP(ads->config.realm);
414         }
415
416         if (!ads->auth.kdc_server) {
417                 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
418                 ads->auth.kdc_server = SMB_STRDUP(addr);
419         }
420
421 #if KRB5_DNS_HACK
422         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
423            to MIT kerberos to work (tridge) */
424         {
425                 char *env;
426                 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
427                 setenv(env, ads->auth.kdc_server, 1);
428                 free(env);
429         }
430 #endif
431
432         /* If the caller() requested no LDAP bind, then we are done */
433         
434         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
435                 return ADS_SUCCESS;
436         }
437
438         ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
439         if (!ads->ldap.mem_ctx) {
440                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
441         }
442         
443         /* Otherwise setup the TCP LDAP session */
444
445         if ( (ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name, 
446                 LDAP_PORT, lp_ldap_timeout())) == NULL )
447         {
448                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
449         }
450
451         /* cache the successful connection for workgroup and realm */
452         if (ads_closest_dc(ads)) {
453                 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
454                 saf_store( ads->server.workgroup, addr);
455                 saf_store( ads->server.realm, addr);
456         }
457
458         ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
459
460         status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
461         if (!ADS_ERR_OK(status)) {
462                 return status;
463         }
464
465         /* fill in the current time and offsets */
466         
467         status = ads_current_time( ads );
468         if ( !ADS_ERR_OK(status) ) {
469                 return status;
470         }
471
472         /* Now do the bind */
473         
474         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
475                 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, NULL, NULL));
476         }
477
478         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
479                 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
480         }
481
482         return ads_sasl_bind(ads);
483 }
484
485 /**
486  * Disconnect the LDAP server
487  * @param ads Pointer to an existing ADS_STRUCT
488  **/
489 void ads_disconnect(ADS_STRUCT *ads)
490 {
491         if (ads->ldap.ld) {
492                 ldap_unbind(ads->ldap.ld);
493                 ads->ldap.ld = NULL;
494         }
495         if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
496                 ads->ldap.wrap_ops->disconnect(ads);
497         }
498         if (ads->ldap.mem_ctx) {
499                 talloc_free(ads->ldap.mem_ctx);
500         }
501         ZERO_STRUCT(ads->ldap);
502 }
503
504 /*
505   Duplicate a struct berval into talloc'ed memory
506  */
507 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
508 {
509         struct berval *value;
510
511         if (!in_val) return NULL;
512
513         value = TALLOC_ZERO_P(ctx, struct berval);
514         if (value == NULL)
515                 return NULL;
516         if (in_val->bv_len == 0) return value;
517
518         value->bv_len = in_val->bv_len;
519         value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
520                                               in_val->bv_len);
521         return value;
522 }
523
524 /*
525   Make a values list out of an array of (struct berval *)
526  */
527 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
528                                       const struct berval **in_vals)
529 {
530         struct berval **values;
531         int i;
532        
533         if (!in_vals) return NULL;
534         for (i=0; in_vals[i]; i++)
535                 ; /* count values */
536         values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
537         if (!values) return NULL;
538
539         for (i=0; in_vals[i]; i++) {
540                 values[i] = dup_berval(ctx, in_vals[i]);
541         }
542         return values;
543 }
544
545 /*
546   UTF8-encode a values list out of an array of (char *)
547  */
548 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
549 {
550         char **values;
551         int i;
552        
553         if (!in_vals) return NULL;
554         for (i=0; in_vals[i]; i++)
555                 ; /* count values */
556         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
557         if (!values) return NULL;
558
559         for (i=0; in_vals[i]; i++) {
560                 push_utf8_talloc(ctx, &values[i], in_vals[i]);
561         }
562         return values;
563 }
564
565 /*
566   Pull a (char *) array out of a UTF8-encoded values list
567  */
568 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
569 {
570         char **values;
571         int i;
572        
573         if (!in_vals) return NULL;
574         for (i=0; in_vals[i]; i++)
575                 ; /* count values */
576         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
577         if (!values) return NULL;
578
579         for (i=0; in_vals[i]; i++) {
580                 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
581         }
582         return values;
583 }
584
585 /**
586  * Do a search with paged results.  cookie must be null on the first
587  *  call, and then returned on each subsequent call.  It will be null
588  *  again when the entire search is complete 
589  * @param ads connection to ads server 
590  * @param bind_path Base dn for the search
591  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
592  * @param expr Search expression - specified in local charset
593  * @param attrs Attributes to retrieve - specified in utf8 or ascii
594  * @param res ** which will contain results - free res* with ads_msgfree()
595  * @param count Number of entries retrieved on this page
596  * @param cookie The paged results cookie to be returned on subsequent calls
597  * @return status of search
598  **/
599 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
600                                            const char *bind_path,
601                                            int scope, const char *expr,
602                                            const char **attrs, void *args,
603                                            LDAPMessage **res, 
604                                            int *count, struct berval **cookie)
605 {
606         int rc, i, version;
607         char *utf8_expr, *utf8_path, **search_attrs;
608         LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
609         BerElement *cookie_be = NULL;
610         struct berval *cookie_bv= NULL;
611         BerElement *ext_be = NULL;
612         struct berval *ext_bv= NULL;
613
614         TALLOC_CTX *ctx;
615         ads_control *external_control = (ads_control *) args;
616
617         *res = NULL;
618
619         if (!(ctx = talloc_init("ads_do_paged_search_args")))
620                 return ADS_ERROR(LDAP_NO_MEMORY);
621
622         /* 0 means the conversion worked but the result was empty 
623            so we only fail if it's -1.  In any case, it always 
624            at least nulls out the dest */
625         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
626             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
627                 rc = LDAP_NO_MEMORY;
628                 goto done;
629         }
630
631         if (!attrs || !(*attrs))
632                 search_attrs = NULL;
633         else {
634                 /* This would be the utf8-encoded version...*/
635                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
636                 if (!(str_list_copy(&search_attrs, attrs))) {
637                         rc = LDAP_NO_MEMORY;
638                         goto done;
639                 }
640         }
641                 
642         /* Paged results only available on ldap v3 or later */
643         ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
644         if (version < LDAP_VERSION3) {
645                 rc =  LDAP_NOT_SUPPORTED;
646                 goto done;
647         }
648
649         cookie_be = ber_alloc_t(LBER_USE_DER);
650         if (*cookie) {
651                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
652                 ber_bvfree(*cookie); /* don't need it from last time */
653                 *cookie = NULL;
654         } else {
655                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
656         }
657         ber_flatten(cookie_be, &cookie_bv);
658         PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
659         PagedResults.ldctl_iscritical = (char) 1;
660         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
661         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
662
663         NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
664         NoReferrals.ldctl_iscritical = (char) 0;
665         NoReferrals.ldctl_value.bv_len = 0;
666         NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
667
668         if (external_control && 
669             (strequal(external_control->control, ADS_EXTENDED_DN_OID) || 
670              strequal(external_control->control, ADS_SD_FLAGS_OID))) {
671
672                 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
673                 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
674
675                 /* win2k does not accept a ldctl_value beeing passed in */
676
677                 if (external_control->val != 0) {
678
679                         if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
680                                 rc = LDAP_NO_MEMORY;
681                                 goto done;
682                         }
683
684                         if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
685                                 rc = LDAP_NO_MEMORY;
686                                 goto done;
687                         }
688                         if ((ber_flatten(ext_be, &ext_bv)) == -1) {
689                                 rc = LDAP_NO_MEMORY;
690                                 goto done;
691                         }
692
693                         ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
694                         ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
695
696                 } else {
697                         ExternalCtrl.ldctl_value.bv_len = 0;
698                         ExternalCtrl.ldctl_value.bv_val = NULL;
699                 }
700
701                 controls[0] = &NoReferrals;
702                 controls[1] = &PagedResults;
703                 controls[2] = &ExternalCtrl;
704                 controls[3] = NULL;
705
706         } else {
707                 controls[0] = &NoReferrals;
708                 controls[1] = &PagedResults;
709                 controls[2] = NULL;
710         }
711
712         /* we need to disable referrals as the openldap libs don't
713            handle them and paged results at the same time.  Using them
714            together results in the result record containing the server 
715            page control being removed from the result list (tridge/jmcd) 
716         
717            leaving this in despite the control that says don't generate
718            referrals, in case the server doesn't support it (jmcd)
719         */
720         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
721
722         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, 
723                                       search_attrs, 0, controls,
724                                       NULL, LDAP_NO_LIMIT,
725                                       (LDAPMessage **)res);
726
727         ber_free(cookie_be, 1);
728         ber_bvfree(cookie_bv);
729
730         if (rc) {
731                 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
732                          ldap_err2string(rc)));
733                 goto done;
734         }
735
736         rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
737                                         NULL, &rcontrols,  0);
738
739         if (!rcontrols) {
740                 goto done;
741         }
742
743         for (i=0; rcontrols[i]; i++) {
744                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
745                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
746                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
747                                   &cookie_bv);
748                         /* the berval is the cookie, but must be freed when
749                            it is all done */
750                         if (cookie_bv->bv_len) /* still more to do */
751                                 *cookie=ber_bvdup(cookie_bv);
752                         else
753                                 *cookie=NULL;
754                         ber_bvfree(cookie_bv);
755                         ber_free(cookie_be, 1);
756                         break;
757                 }
758         }
759         ldap_controls_free(rcontrols);
760
761 done:
762         talloc_destroy(ctx);
763
764         if (ext_be) {
765                 ber_free(ext_be, 1);
766         }
767
768         if (ext_bv) {
769                 ber_bvfree(ext_bv);
770         }
771  
772         /* if/when we decide to utf8-encode attrs, take out this next line */
773         str_list_free(&search_attrs);
774
775         return ADS_ERROR(rc);
776 }
777
778 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
779                                       int scope, const char *expr,
780                                       const char **attrs, LDAPMessage **res, 
781                                       int *count, struct berval **cookie)
782 {
783         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
784 }
785
786
787 /**
788  * Get all results for a search.  This uses ads_do_paged_search() to return 
789  * all entries in a large search.
790  * @param ads connection to ads server 
791  * @param bind_path Base dn for the search
792  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
793  * @param expr Search expression
794  * @param attrs Attributes to retrieve
795  * @param res ** which will contain results - free res* with ads_msgfree()
796  * @return status of search
797  **/
798  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
799                                    int scope, const char *expr,
800                                    const char **attrs, void *args,
801                                    LDAPMessage **res)
802 {
803         struct berval *cookie = NULL;
804         int count = 0;
805         ADS_STATUS status;
806
807         *res = NULL;
808         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
809                                      &count, &cookie);
810
811         if (!ADS_ERR_OK(status)) 
812                 return status;
813
814 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
815         while (cookie) {
816                 LDAPMessage *res2 = NULL;
817                 ADS_STATUS status2;
818                 LDAPMessage *msg, *next;
819
820                 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
821                                               attrs, args, &res2, &count, &cookie);
822
823                 if (!ADS_ERR_OK(status2)) break;
824
825                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
826                    that this works on all ldap libs, but I have only tested with openldap */
827                 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
828                         next = ads_next_entry(ads, msg);
829                         ldap_add_result_entry((LDAPMessage **)res, msg);
830                 }
831                 /* note that we do not free res2, as the memory is now
832                    part of the main returned list */
833         }
834 #else
835         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
836         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
837 #endif
838
839         return status;
840 }
841
842  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
843                               int scope, const char *expr,
844                               const char **attrs, LDAPMessage **res)
845 {
846         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
847 }
848
849  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
850                                        int scope, const char *expr,
851                                        const char **attrs, uint32 sd_flags, 
852                                        LDAPMessage **res)
853 {
854         ads_control args;
855
856         args.control = ADS_SD_FLAGS_OID;
857         args.val = sd_flags;
858         args.critical = True;
859
860         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
861 }
862
863
864 /**
865  * Run a function on all results for a search.  Uses ads_do_paged_search() and
866  *  runs the function as each page is returned, using ads_process_results()
867  * @param ads connection to ads server
868  * @param bind_path Base dn for the search
869  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
870  * @param expr Search expression - specified in local charset
871  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
872  * @param fn Function which takes attr name, values list, and data_area
873  * @param data_area Pointer which is passed to function on each call
874  * @return status of search
875  **/
876 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
877                                 int scope, const char *expr, const char **attrs,
878                                 bool (*fn)(ADS_STRUCT *, char *, void **, void *), 
879                                 void *data_area)
880 {
881         struct berval *cookie = NULL;
882         int count = 0;
883         ADS_STATUS status;
884         LDAPMessage *res;
885
886         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
887                                      &count, &cookie);
888
889         if (!ADS_ERR_OK(status)) return status;
890
891         ads_process_results(ads, res, fn, data_area);
892         ads_msgfree(ads, res);
893
894         while (cookie) {
895                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
896                                              &res, &count, &cookie);
897
898                 if (!ADS_ERR_OK(status)) break;
899                 
900                 ads_process_results(ads, res, fn, data_area);
901                 ads_msgfree(ads, res);
902         }
903
904         return status;
905 }
906
907 /**
908  * Do a search with a timeout.
909  * @param ads connection to ads server
910  * @param bind_path Base dn for the search
911  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
912  * @param expr Search expression
913  * @param attrs Attributes to retrieve
914  * @param res ** which will contain results - free res* with ads_msgfree()
915  * @return status of search
916  **/
917  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
918                           const char *expr,
919                           const char **attrs, LDAPMessage **res)
920 {
921         int rc;
922         char *utf8_expr, *utf8_path, **search_attrs = NULL;
923         TALLOC_CTX *ctx;
924
925         *res = NULL;
926         if (!(ctx = talloc_init("ads_do_search"))) {
927                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
928                 return ADS_ERROR(LDAP_NO_MEMORY);
929         }
930
931         /* 0 means the conversion worked but the result was empty 
932            so we only fail if it's negative.  In any case, it always 
933            at least nulls out the dest */
934         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
935             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
936                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
937                 rc = LDAP_NO_MEMORY;
938                 goto done;
939         }
940
941         if (!attrs || !(*attrs))
942                 search_attrs = NULL;
943         else {
944                 /* This would be the utf8-encoded version...*/
945                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
946                 if (!(str_list_copy(&search_attrs, attrs)))
947                 {
948                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
949                         rc = LDAP_NO_MEMORY;
950                         goto done;
951                 }
952         }
953
954         /* see the note in ads_do_paged_search - we *must* disable referrals */
955         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
956
957         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
958                                       search_attrs, 0, NULL, NULL, 
959                                       LDAP_NO_LIMIT,
960                                       (LDAPMessage **)res);
961
962         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
963                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
964                 rc = 0;
965         }
966
967  done:
968         talloc_destroy(ctx);
969         /* if/when we decide to utf8-encode attrs, take out this next line */
970         str_list_free(&search_attrs);
971         return ADS_ERROR(rc);
972 }
973 /**
974  * Do a general ADS search
975  * @param ads connection to ads server
976  * @param res ** which will contain results - free res* with ads_msgfree()
977  * @param expr Search expression
978  * @param attrs Attributes to retrieve
979  * @return status of search
980  **/
981  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
982                        const char *expr, const char **attrs)
983 {
984         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
985                              expr, attrs, res);
986 }
987
988 /**
989  * Do a search on a specific DistinguishedName
990  * @param ads connection to ads server
991  * @param res ** which will contain results - free res* with ads_msgfree()
992  * @param dn DistinguishName to search
993  * @param attrs Attributes to retrieve
994  * @return status of search
995  **/
996  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
997                           const char *dn, const char **attrs)
998 {
999         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1000                              attrs, res);
1001 }
1002
1003 /**
1004  * Free up memory from a ads_search
1005  * @param ads connection to ads server
1006  * @param msg Search results to free
1007  **/
1008  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1009 {
1010         if (!msg) return;
1011         ldap_msgfree(msg);
1012 }
1013
1014 /**
1015  * Free up memory from various ads requests
1016  * @param ads connection to ads server
1017  * @param mem Area to free
1018  **/
1019 void ads_memfree(ADS_STRUCT *ads, void *mem)
1020 {
1021         SAFE_FREE(mem);
1022 }
1023
1024 /**
1025  * Get a dn from search results
1026  * @param ads connection to ads server
1027  * @param msg Search result
1028  * @return dn string
1029  **/
1030  char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1031 {
1032         char *utf8_dn, *unix_dn;
1033
1034         utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1035
1036         if (!utf8_dn) {
1037                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1038                 return NULL;
1039         }
1040
1041         if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1042                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1043                         utf8_dn ));
1044                 return NULL;
1045         }
1046         ldap_memfree(utf8_dn);
1047         return unix_dn;
1048 }
1049
1050 /**
1051  * Get the parent from a dn
1052  * @param dn the dn to return the parent from
1053  * @return parent dn string
1054  **/
1055 char *ads_parent_dn(const char *dn)
1056 {
1057         char *p;
1058
1059         if (dn == NULL) {
1060                 return NULL;
1061         }
1062
1063         p = strchr(dn, ',');
1064
1065         if (p == NULL) {
1066                 return NULL;
1067         }
1068
1069         return p+1;
1070 }
1071
1072 /**
1073  * Find a machine account given a hostname
1074  * @param ads connection to ads server
1075  * @param res ** which will contain results - free res* with ads_msgfree()
1076  * @param host Hostname to search for
1077  * @return status of search
1078  **/
1079  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1080                                   const char *machine)
1081 {
1082         ADS_STATUS status;
1083         char *expr;
1084         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1085
1086         *res = NULL;
1087
1088         /* the easiest way to find a machine account anywhere in the tree
1089            is to look for hostname$ */
1090         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1091                 DEBUG(1, ("asprintf failed!\n"));
1092                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1093         }
1094         
1095         status = ads_search(ads, res, expr, attrs);
1096         SAFE_FREE(expr);
1097         return status;
1098 }
1099
1100 /**
1101  * Initialize a list of mods to be used in a modify request
1102  * @param ctx An initialized TALLOC_CTX
1103  * @return allocated ADS_MODLIST
1104  **/
1105 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1106 {
1107 #define ADS_MODLIST_ALLOC_SIZE 10
1108         LDAPMod **mods;
1109         
1110         if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1111                 /* -1 is safety to make sure we don't go over the end.
1112                    need to reset it to NULL before doing ldap modify */
1113                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1114         
1115         return (ADS_MODLIST)mods;
1116 }
1117
1118
1119 /*
1120   add an attribute to the list, with values list already constructed
1121 */
1122 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1123                                   int mod_op, const char *name, 
1124                                   const void *_invals)
1125 {
1126         const void **invals = (const void **)_invals;
1127         int curmod;
1128         LDAPMod **modlist = (LDAPMod **) *mods;
1129         struct berval **ber_values = NULL;
1130         char **char_values = NULL;
1131
1132         if (!invals) {
1133                 mod_op = LDAP_MOD_DELETE;
1134         } else {
1135                 if (mod_op & LDAP_MOD_BVALUES)
1136                         ber_values = ads_dup_values(ctx, 
1137                                                 (const struct berval **)invals);
1138                 else
1139                         char_values = ads_push_strvals(ctx, 
1140                                                   (const char **) invals);
1141         }
1142
1143         /* find the first empty slot */
1144         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1145              curmod++);
1146         if (modlist[curmod] == (LDAPMod *) -1) {
1147                 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1148                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1149                         return ADS_ERROR(LDAP_NO_MEMORY);
1150                 memset(&modlist[curmod], 0, 
1151                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1152                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1153                 *mods = (ADS_MODLIST)modlist;
1154         }
1155                 
1156         if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1157                 return ADS_ERROR(LDAP_NO_MEMORY);
1158         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1159         if (mod_op & LDAP_MOD_BVALUES) {
1160                 modlist[curmod]->mod_bvalues = ber_values;
1161         } else if (mod_op & LDAP_MOD_DELETE) {
1162                 modlist[curmod]->mod_values = NULL;
1163         } else {
1164                 modlist[curmod]->mod_values = char_values;
1165         }
1166
1167         modlist[curmod]->mod_op = mod_op;
1168         return ADS_ERROR(LDAP_SUCCESS);
1169 }
1170
1171 /**
1172  * Add a single string value to a mod list
1173  * @param ctx An initialized TALLOC_CTX
1174  * @param mods An initialized ADS_MODLIST
1175  * @param name The attribute name to add
1176  * @param val The value to add - NULL means DELETE
1177  * @return ADS STATUS indicating success of add
1178  **/
1179 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1180                        const char *name, const char *val)
1181 {
1182         const char *values[2];
1183
1184         values[0] = val;
1185         values[1] = NULL;
1186
1187         if (!val)
1188                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1189         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1190 }
1191
1192 /**
1193  * Add an array of string values to a mod list
1194  * @param ctx An initialized TALLOC_CTX
1195  * @param mods An initialized ADS_MODLIST
1196  * @param name The attribute name to add
1197  * @param vals The array of string values to add - NULL means DELETE
1198  * @return ADS STATUS indicating success of add
1199  **/
1200 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1201                            const char *name, const char **vals)
1202 {
1203         if (!vals)
1204                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1205         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1206                                name, (const void **) vals);
1207 }
1208
1209 #if 0
1210 /**
1211  * Add a single ber-encoded value to a mod list
1212  * @param ctx An initialized TALLOC_CTX
1213  * @param mods An initialized ADS_MODLIST
1214  * @param name The attribute name to add
1215  * @param val The value to add - NULL means DELETE
1216  * @return ADS STATUS indicating success of add
1217  **/
1218 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1219                               const char *name, const struct berval *val)
1220 {
1221         const struct berval *values[2];
1222
1223         values[0] = val;
1224         values[1] = NULL;
1225         if (!val)
1226                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1227         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1228                                name, (const void **) values);
1229 }
1230 #endif
1231
1232 /**
1233  * Perform an ldap modify
1234  * @param ads connection to ads server
1235  * @param mod_dn DistinguishedName to modify
1236  * @param mods list of modifications to perform
1237  * @return status of modify
1238  **/
1239 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1240 {
1241         int ret,i;
1242         char *utf8_dn = NULL;
1243         /* 
1244            this control is needed to modify that contains a currently 
1245            non-existent attribute (but allowable for the object) to run
1246         */
1247         LDAPControl PermitModify = {
1248                 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1249                 {0, NULL},
1250                 (char) 1};
1251         LDAPControl *controls[2];
1252
1253         controls[0] = &PermitModify;
1254         controls[1] = NULL;
1255
1256         if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1257                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1258         }
1259
1260         /* find the end of the list, marked by NULL or -1 */
1261         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1262         /* make sure the end of the list is NULL */
1263         mods[i] = NULL;
1264         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1265                                 (LDAPMod **) mods, controls, NULL);
1266         SAFE_FREE(utf8_dn);
1267         return ADS_ERROR(ret);
1268 }
1269
1270 /**
1271  * Perform an ldap add
1272  * @param ads connection to ads server
1273  * @param new_dn DistinguishedName to add
1274  * @param mods list of attributes and values for DN
1275  * @return status of add
1276  **/
1277 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1278 {
1279         int ret, i;
1280         char *utf8_dn = NULL;
1281
1282         if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1283                 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1284                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1285         }
1286         
1287         /* find the end of the list, marked by NULL or -1 */
1288         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1289         /* make sure the end of the list is NULL */
1290         mods[i] = NULL;
1291
1292         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1293         SAFE_FREE(utf8_dn);
1294         return ADS_ERROR(ret);
1295 }
1296
1297 /**
1298  * Delete a DistinguishedName
1299  * @param ads connection to ads server
1300  * @param new_dn DistinguishedName to delete
1301  * @return status of delete
1302  **/
1303 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1304 {
1305         int ret;
1306         char *utf8_dn = NULL;
1307         if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1308                 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1309                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1310         }
1311         
1312         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1313         SAFE_FREE(utf8_dn);
1314         return ADS_ERROR(ret);
1315 }
1316
1317 /**
1318  * Build an org unit string
1319  *  if org unit is Computers or blank then assume a container, otherwise
1320  *  assume a / separated list of organisational units.
1321  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1322  * @param ads connection to ads server
1323  * @param org_unit Organizational unit
1324  * @return org unit string - caller must free
1325  **/
1326 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1327 {
1328         char *ret = NULL;
1329
1330         if (!org_unit || !*org_unit) {
1331
1332                 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1333
1334                 /* samba4 might not yet respond to a wellknownobject-query */
1335                 return ret ? ret : SMB_STRDUP("cn=Computers");
1336         }
1337         
1338         if (strequal(org_unit, "Computers")) {
1339                 return SMB_STRDUP("cn=Computers");
1340         }
1341
1342         /* jmcd: removed "\\" from the separation chars, because it is
1343            needed as an escape for chars like '#' which are valid in an
1344            OU name */
1345         return ads_build_path(org_unit, "/", "ou=", 1);
1346 }
1347
1348 /**
1349  * Get a org unit string for a well-known GUID
1350  * @param ads connection to ads server
1351  * @param wknguid Well known GUID
1352  * @return org unit string - caller must free
1353  **/
1354 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1355 {
1356         ADS_STATUS status;
1357         LDAPMessage *res = NULL;
1358         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1359                 **bind_dn_exp = NULL;
1360         const char *attrs[] = {"distinguishedName", NULL};
1361         int new_ln, wkn_ln, bind_ln, i;
1362
1363         if (wknguid == NULL) {
1364                 return NULL;
1365         }
1366
1367         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1368                 DEBUG(1, ("asprintf failed!\n"));
1369                 return NULL;
1370         }
1371
1372         status = ads_search_dn(ads, &res, base, attrs);
1373         if (!ADS_ERR_OK(status)) {
1374                 DEBUG(1,("Failed while searching for: %s\n", base));
1375                 goto out;
1376         }
1377
1378         if (ads_count_replies(ads, res) != 1) {
1379                 goto out;
1380         }
1381
1382         /* substitute the bind-path from the well-known-guid-search result */
1383         wkn_dn = ads_get_dn(ads, res);
1384         if (!wkn_dn) {
1385                 goto out;
1386         }
1387
1388         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1389         if (!wkn_dn_exp) {
1390                 goto out;
1391         }
1392
1393         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1394         if (!bind_dn_exp) {
1395                 goto out;
1396         }
1397
1398         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1399                 ;
1400         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1401                 ;
1402
1403         new_ln = wkn_ln - bind_ln;
1404
1405         ret = SMB_STRDUP(wkn_dn_exp[0]);
1406         if (!ret) {
1407                 goto out;
1408         }
1409
1410         for (i=1; i < new_ln; i++) {
1411                 char *s = NULL;
1412                 
1413                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1414                         SAFE_FREE(ret);
1415                         goto out;
1416                 }
1417
1418                 SAFE_FREE(ret);
1419                 ret = SMB_STRDUP(s);
1420                 free(s);
1421                 if (!ret) {
1422                         goto out;
1423                 }
1424         }
1425
1426  out:
1427         SAFE_FREE(base);
1428         ads_msgfree(ads, res);
1429         ads_memfree(ads, wkn_dn);
1430         if (wkn_dn_exp) {
1431                 ldap_value_free(wkn_dn_exp);
1432         }
1433         if (bind_dn_exp) {
1434                 ldap_value_free(bind_dn_exp);
1435         }
1436
1437         return ret;
1438 }
1439
1440 /**
1441  * Adds (appends) an item to an attribute array, rather then
1442  * replacing the whole list
1443  * @param ctx An initialized TALLOC_CTX
1444  * @param mods An initialized ADS_MODLIST
1445  * @param name name of the ldap attribute to append to
1446  * @param vals an array of values to add
1447  * @return status of addition
1448  **/
1449
1450 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1451                                 const char *name, const char **vals)
1452 {
1453         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1454                                (const void *) vals);
1455 }
1456
1457 /**
1458  * Determines the computer account's current KVNO via an LDAP lookup
1459  * @param ads An initialized ADS_STRUCT
1460  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1461  * @return the kvno for the computer account, or -1 in case of a failure.
1462  **/
1463
1464 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1465 {
1466         LDAPMessage *res = NULL;
1467         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1468         char *filter;
1469         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1470         char *dn_string = NULL;
1471         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1472
1473         DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1474         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1475                 return kvno;
1476         }
1477         ret = ads_search(ads, &res, filter, attrs);
1478         SAFE_FREE(filter);
1479         if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1480                 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1481                 ads_msgfree(ads, res);
1482                 return kvno;
1483         }
1484
1485         dn_string = ads_get_dn(ads, res);
1486         if (!dn_string) {
1487                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1488                 ads_msgfree(ads, res);
1489                 return kvno;
1490         }
1491         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1492         ads_memfree(ads, dn_string);
1493
1494         /* ---------------------------------------------------------
1495          * 0 is returned as a default KVNO from this point on...
1496          * This is done because Windows 2000 does not support key
1497          * version numbers.  Chances are that a failure in the next
1498          * step is simply due to Windows 2000 being used for a
1499          * domain controller. */
1500         kvno = 0;
1501
1502         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1503                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1504                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1505                 ads_msgfree(ads, res);
1506                 return kvno;
1507         }
1508
1509         /* Success */
1510         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1511         ads_msgfree(ads, res);
1512         return kvno;
1513 }
1514
1515 /**
1516  * This clears out all registered spn's for a given hostname
1517  * @param ads An initilaized ADS_STRUCT
1518  * @param machine_name the NetBIOS name of the computer.
1519  * @return 0 upon success, non-zero otherwise.
1520  **/
1521
1522 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1523 {
1524         TALLOC_CTX *ctx;
1525         LDAPMessage *res = NULL;
1526         ADS_MODLIST mods;
1527         const char *servicePrincipalName[1] = {NULL};
1528         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1529         char *dn_string = NULL;
1530
1531         ret = ads_find_machine_acct(ads, &res, machine_name);
1532         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1533                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1534                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1535                 ads_msgfree(ads, res);
1536                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1537         }
1538
1539         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1540         ctx = talloc_init("ads_clear_service_principal_names");
1541         if (!ctx) {
1542                 ads_msgfree(ads, res);
1543                 return ADS_ERROR(LDAP_NO_MEMORY);
1544         }
1545
1546         if (!(mods = ads_init_mods(ctx))) {
1547                 talloc_destroy(ctx);
1548                 ads_msgfree(ads, res);
1549                 return ADS_ERROR(LDAP_NO_MEMORY);
1550         }
1551         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1552         if (!ADS_ERR_OK(ret)) {
1553                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1554                 ads_msgfree(ads, res);
1555                 talloc_destroy(ctx);
1556                 return ret;
1557         }
1558         dn_string = ads_get_dn(ads, res);
1559         if (!dn_string) {
1560                 talloc_destroy(ctx);
1561                 ads_msgfree(ads, res);
1562                 return ADS_ERROR(LDAP_NO_MEMORY);
1563         }
1564         ret = ads_gen_mod(ads, dn_string, mods);
1565         ads_memfree(ads,dn_string);
1566         if (!ADS_ERR_OK(ret)) {
1567                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1568                         machine_name));
1569                 ads_msgfree(ads, res);
1570                 talloc_destroy(ctx);
1571                 return ret;
1572         }
1573
1574         ads_msgfree(ads, res);
1575         talloc_destroy(ctx);
1576         return ret;
1577 }
1578
1579 /**
1580  * This adds a service principal name to an existing computer account
1581  * (found by hostname) in AD.
1582  * @param ads An initialized ADS_STRUCT
1583  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1584  * @param my_fqdn The fully qualified DNS name of the machine
1585  * @param spn A string of the service principal to add, i.e. 'host'
1586  * @return 0 upon sucess, or non-zero if a failure occurs
1587  **/
1588
1589 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1590                                           const char *my_fqdn, const char *spn)
1591 {
1592         ADS_STATUS ret;
1593         TALLOC_CTX *ctx;
1594         LDAPMessage *res = NULL;
1595         char *psp1, *psp2;
1596         ADS_MODLIST mods;
1597         char *dn_string = NULL;
1598         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1599
1600         ret = ads_find_machine_acct(ads, &res, machine_name);
1601         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1602                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1603                         machine_name));
1604                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1605                         spn, machine_name, ads->config.realm));
1606                 ads_msgfree(ads, res);
1607                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1608         }
1609
1610         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1611         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1612                 ads_msgfree(ads, res);
1613                 return ADS_ERROR(LDAP_NO_MEMORY);
1614         }
1615
1616         /* add short name spn */
1617         
1618         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1619                 talloc_destroy(ctx);
1620                 ads_msgfree(ads, res);
1621                 return ADS_ERROR(LDAP_NO_MEMORY);
1622         }
1623         strupper_m(psp1);
1624         strlower_m(&psp1[strlen(spn)]);
1625         servicePrincipalName[0] = psp1;
1626         
1627         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1628                 psp1, machine_name));
1629
1630
1631         /* add fully qualified spn */
1632         
1633         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1634                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1635                 goto out;
1636         }
1637         strupper_m(psp2);
1638         strlower_m(&psp2[strlen(spn)]);
1639         servicePrincipalName[1] = psp2;
1640
1641         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1642                 psp2, machine_name));
1643
1644         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1645                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1646                 goto out;
1647         }
1648         
1649         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1650         if (!ADS_ERR_OK(ret)) {
1651                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1652                 goto out;
1653         }
1654         
1655         if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1656                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1657                 goto out;
1658         }
1659         
1660         ret = ads_gen_mod(ads, dn_string, mods);
1661         ads_memfree(ads,dn_string);
1662         if (!ADS_ERR_OK(ret)) {
1663                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1664                 goto out;
1665         }
1666
1667  out:
1668         TALLOC_FREE( ctx );
1669         ads_msgfree(ads, res);
1670         return ret;
1671 }
1672
1673 /**
1674  * adds a machine account to the ADS server
1675  * @param ads An intialized ADS_STRUCT
1676  * @param machine_name - the NetBIOS machine name of this account.
1677  * @param account_type A number indicating the type of account to create
1678  * @param org_unit The LDAP path in which to place this account
1679  * @return 0 upon success, or non-zero otherwise
1680 **/
1681
1682 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1683                                    const char *org_unit)
1684 {
1685         ADS_STATUS ret;
1686         char *samAccountName, *controlstr;
1687         TALLOC_CTX *ctx;
1688         ADS_MODLIST mods;
1689         char *machine_escaped = NULL;
1690         char *new_dn;
1691         const char *objectClass[] = {"top", "person", "organizationalPerson",
1692                                      "user", "computer", NULL};
1693         LDAPMessage *res = NULL;
1694         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1695                                 UF_DONT_EXPIRE_PASSWD |\
1696                                 UF_ACCOUNTDISABLE );
1697                               
1698         if (!(ctx = talloc_init("ads_add_machine_acct")))
1699                 return ADS_ERROR(LDAP_NO_MEMORY);
1700
1701         ret = ADS_ERROR(LDAP_NO_MEMORY);
1702
1703         machine_escaped = escape_rdn_val_string_alloc(machine_name);
1704         if (!machine_escaped) {
1705                 goto done;
1706         }
1707
1708         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1709         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1710
1711         if ( !new_dn || !samAccountName ) {
1712                 goto done;
1713         }
1714         
1715 #ifndef ENCTYPE_ARCFOUR_HMAC
1716         acct_control |= UF_USE_DES_KEY_ONLY;
1717 #endif
1718
1719         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1720                 goto done;
1721         }
1722
1723         if (!(mods = ads_init_mods(ctx))) {
1724                 goto done;
1725         }
1726         
1727         ads_mod_str(ctx, &mods, "cn", machine_name);
1728         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1729         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1730         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1731
1732         ret = ads_gen_add(ads, new_dn, mods);
1733
1734 done:
1735         SAFE_FREE(machine_escaped);
1736         ads_msgfree(ads, res);
1737         talloc_destroy(ctx);
1738         
1739         return ret;
1740 }
1741
1742 /**
1743  * move a machine account to another OU on the ADS server
1744  * @param ads - An intialized ADS_STRUCT
1745  * @param machine_name - the NetBIOS machine name of this account.
1746  * @param org_unit - The LDAP path in which to place this account
1747  * @param moved - whether we moved the machine account (optional)
1748  * @return 0 upon success, or non-zero otherwise
1749 **/
1750
1751 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1752                                  const char *org_unit, bool *moved)
1753 {
1754         ADS_STATUS rc;
1755         int ldap_status;
1756         LDAPMessage *res = NULL;
1757         char *filter = NULL;
1758         char *computer_dn = NULL;
1759         char *parent_dn;
1760         char *computer_rdn = NULL;
1761         bool need_move = False;
1762
1763         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1764                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1765                 goto done;
1766         }
1767
1768         /* Find pre-existing machine */
1769         rc = ads_search(ads, &res, filter, NULL);
1770         if (!ADS_ERR_OK(rc)) {
1771                 goto done;
1772         }
1773
1774         computer_dn = ads_get_dn(ads, res);
1775         if (!computer_dn) {
1776                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1777                 goto done;
1778         }
1779
1780         parent_dn = ads_parent_dn(computer_dn);
1781         if (strequal(parent_dn, org_unit)) {
1782                 goto done;
1783         }
1784
1785         need_move = True;
1786
1787         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1788                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1789                 goto done;
1790         }
1791
1792         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
1793                                     org_unit, 1, NULL, NULL);
1794         rc = ADS_ERROR(ldap_status);
1795
1796 done:
1797         ads_msgfree(ads, res);
1798         SAFE_FREE(filter);
1799         SAFE_FREE(computer_dn);
1800         SAFE_FREE(computer_rdn);
1801
1802         if (!ADS_ERR_OK(rc)) {
1803                 need_move = False;
1804         }
1805
1806         if (moved) {
1807                 *moved = need_move;
1808         }
1809
1810         return rc;
1811 }
1812
1813 /*
1814   dump a binary result from ldap
1815 */
1816 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1817 {
1818         int i, j;
1819         for (i=0; values[i]; i++) {
1820                 printf("%s: ", field);
1821                 for (j=0; j<values[i]->bv_len; j++) {
1822                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1823                 }
1824                 printf("\n");
1825         }
1826 }
1827
1828 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1829 {
1830         int i;
1831         for (i=0; values[i]; i++) {
1832
1833                 UUID_FLAT guid;
1834                 struct GUID tmp;
1835
1836                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1837                 smb_uuid_unpack(guid, &tmp);
1838                 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1839         }
1840 }
1841
1842 /*
1843   dump a sid result from ldap
1844 */
1845 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1846 {
1847         int i;
1848         for (i=0; values[i]; i++) {
1849                 DOM_SID sid;
1850                 fstring tmp;
1851                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1852                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1853         }
1854 }
1855
1856 /*
1857   dump ntSecurityDescriptor
1858 */
1859 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1860 {
1861         TALLOC_CTX *frame = talloc_stackframe();
1862         struct security_descriptor *psd;
1863         NTSTATUS status;
1864
1865         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1866                                      values[0]->bv_len, &psd);
1867         if (!NT_STATUS_IS_OK(status)) {
1868                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1869                           nt_errstr(status)));
1870                 TALLOC_FREE(frame);
1871                 return;
1872         }
1873
1874         if (psd) {
1875                 ads_disp_sd(ads, talloc_tos(), psd);
1876         }
1877
1878         TALLOC_FREE(frame);
1879 }
1880
1881 /*
1882   dump a string result from ldap
1883 */
1884 static void dump_string(const char *field, char **values)
1885 {
1886         int i;
1887         for (i=0; values[i]; i++) {
1888                 printf("%s: %s\n", field, values[i]);
1889         }
1890 }
1891
1892 /*
1893   dump a field from LDAP on stdout
1894   used for debugging
1895 */
1896
1897 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1898 {
1899         const struct {
1900                 const char *name;
1901                 bool string;
1902                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1903         } handlers[] = {
1904                 {"objectGUID", False, dump_guid},
1905                 {"netbootGUID", False, dump_guid},
1906                 {"nTSecurityDescriptor", False, dump_sd},
1907                 {"dnsRecord", False, dump_binary},
1908                 {"objectSid", False, dump_sid},
1909                 {"tokenGroups", False, dump_sid},
1910                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1911                 {"tokengroupsGlobalandUniversal", False, dump_sid},
1912                 {"mS-DS-CreatorSID", False, dump_sid},
1913                 {"msExchMailboxGuid", False, dump_guid},
1914                 {NULL, True, NULL}
1915         };
1916         int i;
1917
1918         if (!field) { /* must be end of an entry */
1919                 printf("\n");
1920                 return False;
1921         }
1922
1923         for (i=0; handlers[i].name; i++) {
1924                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1925                         if (!values) /* first time, indicate string or not */
1926                                 return handlers[i].string;
1927                         handlers[i].handler(ads, field, (struct berval **) values);
1928                         break;
1929                 }
1930         }
1931         if (!handlers[i].name) {
1932                 if (!values) /* first time, indicate string conversion */
1933                         return True;
1934                 dump_string(field, (char **)values);
1935         }
1936         return False;
1937 }
1938
1939 /**
1940  * Dump a result from LDAP on stdout
1941  *  used for debugging
1942  * @param ads connection to ads server
1943  * @param res Results to dump
1944  **/
1945
1946  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1947 {
1948         ads_process_results(ads, res, ads_dump_field, NULL);
1949 }
1950
1951 /**
1952  * Walk through results, calling a function for each entry found.
1953  *  The function receives a field name, a berval * array of values,
1954  *  and a data area passed through from the start.  The function is
1955  *  called once with null for field and values at the end of each
1956  *  entry.
1957  * @param ads connection to ads server
1958  * @param res Results to process
1959  * @param fn Function for processing each result
1960  * @param data_area user-defined area to pass to function
1961  **/
1962  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1963                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1964                           void *data_area)
1965 {
1966         LDAPMessage *msg;
1967         TALLOC_CTX *ctx;
1968
1969         if (!(ctx = talloc_init("ads_process_results")))
1970                 return;
1971
1972         for (msg = ads_first_entry(ads, res); msg; 
1973              msg = ads_next_entry(ads, msg)) {
1974                 char *utf8_field;
1975                 BerElement *b;
1976         
1977                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1978                                                      (LDAPMessage *)msg,&b); 
1979                      utf8_field;
1980                      utf8_field=ldap_next_attribute(ads->ldap.ld,
1981                                                     (LDAPMessage *)msg,b)) {
1982                         struct berval **ber_vals;
1983                         char **str_vals, **utf8_vals;
1984                         char *field;
1985                         bool string; 
1986
1987                         pull_utf8_talloc(ctx, &field, utf8_field);
1988                         string = fn(ads, field, NULL, data_area);
1989
1990                         if (string) {
1991                                 utf8_vals = ldap_get_values(ads->ldap.ld,
1992                                                  (LDAPMessage *)msg, field);
1993                                 str_vals = ads_pull_strvals(ctx, 
1994                                                   (const char **) utf8_vals);
1995                                 fn(ads, field, (void **) str_vals, data_area);
1996                                 ldap_value_free(utf8_vals);
1997                         } else {
1998                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
1999                                                  (LDAPMessage *)msg, field);
2000                                 fn(ads, field, (void **) ber_vals, data_area);
2001
2002                                 ldap_value_free_len(ber_vals);
2003                         }
2004                         ldap_memfree(utf8_field);
2005                 }
2006                 ber_free(b, 0);
2007                 talloc_free_children(ctx);
2008                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2009
2010         }
2011         talloc_destroy(ctx);
2012 }
2013
2014 /**
2015  * count how many replies are in a LDAPMessage
2016  * @param ads connection to ads server
2017  * @param res Results to count
2018  * @return number of replies
2019  **/
2020 int ads_count_replies(ADS_STRUCT *ads, void *res)
2021 {
2022         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2023 }
2024
2025 /**
2026  * pull the first entry from a ADS result
2027  * @param ads connection to ads server
2028  * @param res Results of search
2029  * @return first entry from result
2030  **/
2031  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2032 {
2033         return ldap_first_entry(ads->ldap.ld, res);
2034 }
2035
2036 /**
2037  * pull the next entry from a ADS result
2038  * @param ads connection to ads server
2039  * @param res Results of search
2040  * @return next entry from result
2041  **/
2042  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2043 {
2044         return ldap_next_entry(ads->ldap.ld, res);
2045 }
2046
2047 /**
2048  * pull a single string from a ADS result
2049  * @param ads connection to ads server
2050  * @param mem_ctx TALLOC_CTX to use for allocating result string
2051  * @param msg Results of search
2052  * @param field Attribute to retrieve
2053  * @return Result string in talloc context
2054  **/
2055  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2056                        const char *field)
2057 {
2058         char **values;
2059         char *ret = NULL;
2060         char *ux_string;
2061         size_t rc;
2062
2063         values = ldap_get_values(ads->ldap.ld, msg, field);
2064         if (!values)
2065                 return NULL;
2066         
2067         if (values[0]) {
2068                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
2069                                       values[0]);
2070                 if (rc != (size_t)-1)
2071                         ret = ux_string;
2072                 
2073         }
2074         ldap_value_free(values);
2075         return ret;
2076 }
2077
2078 /**
2079  * pull an array of strings from a ADS result
2080  * @param ads connection to ads server
2081  * @param mem_ctx TALLOC_CTX to use for allocating result string
2082  * @param msg Results of search
2083  * @param field Attribute to retrieve
2084  * @return Result strings in talloc context
2085  **/
2086  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2087                          LDAPMessage *msg, const char *field,
2088                          size_t *num_values)
2089 {
2090         char **values;
2091         char **ret = NULL;
2092         int i;
2093
2094         values = ldap_get_values(ads->ldap.ld, msg, field);
2095         if (!values)
2096                 return NULL;
2097
2098         *num_values = ldap_count_values(values);
2099
2100         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2101         if (!ret) {
2102                 ldap_value_free(values);
2103                 return NULL;
2104         }
2105
2106         for (i=0;i<*num_values;i++) {
2107                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2108                         ldap_value_free(values);
2109                         return NULL;
2110                 }
2111         }
2112         ret[i] = NULL;
2113
2114         ldap_value_free(values);
2115         return ret;
2116 }
2117
2118 /**
2119  * pull an array of strings from a ADS result 
2120  *  (handle large multivalue attributes with range retrieval)
2121  * @param ads connection to ads server
2122  * @param mem_ctx TALLOC_CTX to use for allocating result string
2123  * @param msg Results of search
2124  * @param field Attribute to retrieve
2125  * @param current_strings strings returned by a previous call to this function
2126  * @param next_attribute The next query should ask for this attribute
2127  * @param num_values How many values did we get this time?
2128  * @param more_values Are there more values to get?
2129  * @return Result strings in talloc context
2130  **/
2131  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2132                                TALLOC_CTX *mem_ctx,
2133                                LDAPMessage *msg, const char *field,
2134                                char **current_strings,
2135                                const char **next_attribute,
2136                                size_t *num_strings,
2137                                bool *more_strings)
2138 {
2139         char *attr;
2140         char *expected_range_attrib, *range_attr;
2141         BerElement *ptr = NULL;
2142         char **strings;
2143         char **new_strings;
2144         size_t num_new_strings;
2145         unsigned long int range_start;
2146         unsigned long int range_end;
2147         
2148         /* we might have been given the whole lot anyway */
2149         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2150                 *more_strings = False;
2151                 return strings;
2152         }
2153
2154         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2155
2156         /* look for Range result */
2157         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2158              attr; 
2159              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2160                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2161                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2162                         range_attr = attr;
2163                         break;
2164                 }
2165                 ldap_memfree(attr);
2166         }
2167         if (!attr) {
2168                 ber_free(ptr, 0);
2169                 /* nothing here - this field is just empty */
2170                 *more_strings = False;
2171                 return NULL;
2172         }
2173         
2174         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2175                    &range_start, &range_end) == 2) {
2176                 *more_strings = True;
2177         } else {
2178                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2179                            &range_start) == 1) {
2180                         *more_strings = False;
2181                 } else {
2182                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2183                                   range_attr));
2184                         ldap_memfree(range_attr);
2185                         *more_strings = False;
2186                         return NULL;
2187                 }
2188         }
2189
2190         if ((*num_strings) != range_start) {
2191                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2192                           " - aborting range retreival\n",
2193                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2194                 ldap_memfree(range_attr);
2195                 *more_strings = False;
2196                 return NULL;
2197         }
2198
2199         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2200         
2201         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2202                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2203                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2204                           range_attr, (unsigned long int)range_end - range_start + 1, 
2205                           (unsigned long int)num_new_strings));
2206                 ldap_memfree(range_attr);
2207                 *more_strings = False;
2208                 return NULL;
2209         }
2210
2211         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2212                                  *num_strings + num_new_strings);
2213         
2214         if (strings == NULL) {
2215                 ldap_memfree(range_attr);
2216                 *more_strings = False;
2217                 return NULL;
2218         }
2219         
2220         if (new_strings && num_new_strings) {
2221                 memcpy(&strings[*num_strings], new_strings,
2222                        sizeof(*new_strings) * num_new_strings);
2223         }
2224
2225         (*num_strings) += num_new_strings;
2226
2227         if (*more_strings) {
2228                 *next_attribute = talloc_asprintf(mem_ctx,
2229                                                   "%s;range=%d-*", 
2230                                                   field,
2231                                                   (int)*num_strings);
2232                 
2233                 if (!*next_attribute) {
2234                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2235                         ldap_memfree(range_attr);
2236                         *more_strings = False;
2237                         return NULL;
2238                 }
2239         }
2240
2241         ldap_memfree(range_attr);
2242
2243         return strings;
2244 }
2245
2246 /**
2247  * pull a single uint32 from a ADS result
2248  * @param ads connection to ads server
2249  * @param msg Results of search
2250  * @param field Attribute to retrieve
2251  * @param v Pointer to int to store result
2252  * @return boolean inidicating success
2253 */
2254  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2255                       uint32 *v)
2256 {
2257         char **values;
2258
2259         values = ldap_get_values(ads->ldap.ld, msg, field);
2260         if (!values)
2261                 return False;
2262         if (!values[0]) {
2263                 ldap_value_free(values);
2264                 return False;
2265         }
2266
2267         *v = atoi(values[0]);
2268         ldap_value_free(values);
2269         return True;
2270 }
2271
2272 /**
2273  * pull a single objectGUID from an ADS result
2274  * @param ads connection to ADS server
2275  * @param msg results of search
2276  * @param guid 37-byte area to receive text guid
2277  * @return boolean indicating success
2278  **/
2279  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2280 {
2281         char **values;
2282         UUID_FLAT flat_guid;
2283
2284         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2285         if (!values)
2286                 return False;
2287         
2288         if (values[0]) {
2289                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2290                 smb_uuid_unpack(flat_guid, guid);
2291                 ldap_value_free(values);
2292                 return True;
2293         }
2294         ldap_value_free(values);
2295         return False;
2296
2297 }
2298
2299
2300 /**
2301  * pull a single DOM_SID from a ADS result
2302  * @param ads connection to ads server
2303  * @param msg Results of search
2304  * @param field Attribute to retrieve
2305  * @param sid Pointer to sid to store result
2306  * @return boolean inidicating success
2307 */
2308  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2309                    DOM_SID *sid)
2310 {
2311         struct berval **values;
2312         bool ret = False;
2313
2314         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2315
2316         if (!values)
2317                 return False;
2318
2319         if (values[0])
2320                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2321         
2322         ldap_value_free_len(values);
2323         return ret;
2324 }
2325
2326 /**
2327  * pull an array of DOM_SIDs from a ADS result
2328  * @param ads connection to ads server
2329  * @param mem_ctx TALLOC_CTX for allocating sid array
2330  * @param msg Results of search
2331  * @param field Attribute to retrieve
2332  * @param sids pointer to sid array to allocate
2333  * @return the count of SIDs pulled
2334  **/
2335  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2336                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2337 {
2338         struct berval **values;
2339         bool ret;
2340         int count, i;
2341
2342         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2343
2344         if (!values)
2345                 return 0;
2346
2347         for (i=0; values[i]; i++)
2348                 /* nop */ ;
2349
2350         if (i) {
2351                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2352                 if (!(*sids)) {
2353                         ldap_value_free_len(values);
2354                         return 0;
2355                 }
2356         } else {
2357                 (*sids) = NULL;
2358         }
2359
2360         count = 0;
2361         for (i=0; values[i]; i++) {
2362                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2363                 if (ret) {
2364                         DEBUG(10, ("pulling SID: %s\n",
2365                                    sid_string_dbg(&(*sids)[count])));
2366                         count++;
2367                 }
2368         }
2369         
2370         ldap_value_free_len(values);
2371         return count;
2372 }
2373
2374 /**
2375  * pull a SEC_DESC from a ADS result
2376  * @param ads connection to ads server
2377  * @param mem_ctx TALLOC_CTX for allocating sid array
2378  * @param msg Results of search
2379  * @param field Attribute to retrieve
2380  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2381  * @return boolean inidicating success
2382 */
2383  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2384                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2385 {
2386         struct berval **values;
2387         bool ret = true;
2388
2389         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2390
2391         if (!values) return false;
2392
2393         if (values[0]) {
2394                 NTSTATUS status;
2395                 status = unmarshall_sec_desc(mem_ctx,
2396                                              (uint8 *)values[0]->bv_val,
2397                                              values[0]->bv_len, sd);
2398                 if (!NT_STATUS_IS_OK(status)) {
2399                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2400                                   nt_errstr(status)));
2401                         ret = false;
2402                 }
2403         }
2404         
2405         ldap_value_free_len(values);
2406         return ret;
2407 }
2408
2409 /* 
2410  * in order to support usernames longer than 21 characters we need to 
2411  * use both the sAMAccountName and the userPrincipalName attributes 
2412  * It seems that not all users have the userPrincipalName attribute set
2413  *
2414  * @param ads connection to ads server
2415  * @param mem_ctx TALLOC_CTX for allocating sid array
2416  * @param msg Results of search
2417  * @return the username
2418  */
2419  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2420                          LDAPMessage *msg)
2421 {
2422 #if 0   /* JERRY */
2423         char *ret, *p;
2424
2425         /* lookup_name() only works on the sAMAccountName to 
2426            returning the username portion of userPrincipalName
2427            breaks winbindd_getpwnam() */
2428
2429         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2430         if (ret && (p = strchr_m(ret, '@'))) {
2431                 *p = 0;
2432                 return ret;
2433         }
2434 #endif
2435         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2436 }
2437
2438
2439 /**
2440  * find the update serial number - this is the core of the ldap cache
2441  * @param ads connection to ads server
2442  * @param ads connection to ADS server
2443  * @param usn Pointer to retrieved update serial number
2444  * @return status of search
2445  **/
2446 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2447 {
2448         const char *attrs[] = {"highestCommittedUSN", NULL};
2449         ADS_STATUS status;
2450         LDAPMessage *res;
2451
2452         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2453         if (!ADS_ERR_OK(status)) 
2454                 return status;
2455
2456         if (ads_count_replies(ads, res) != 1) {
2457                 ads_msgfree(ads, res);
2458                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2459         }
2460
2461         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2462                 ads_msgfree(ads, res);
2463                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2464         }
2465
2466         ads_msgfree(ads, res);
2467         return ADS_SUCCESS;
2468 }
2469
2470 /* parse a ADS timestring - typical string is
2471    '20020917091222.0Z0' which means 09:12.22 17th September
2472    2002, timezone 0 */
2473 static time_t ads_parse_time(const char *str)
2474 {
2475         struct tm tm;
2476
2477         ZERO_STRUCT(tm);
2478
2479         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2480                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2481                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2482                 return 0;
2483         }
2484         tm.tm_year -= 1900;
2485         tm.tm_mon -= 1;
2486
2487         return timegm(&tm);
2488 }
2489
2490 /********************************************************************
2491 ********************************************************************/
2492
2493 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2494 {
2495         const char *attrs[] = {"currentTime", NULL};
2496         ADS_STATUS status;
2497         LDAPMessage *res;
2498         char *timestr;
2499         TALLOC_CTX *ctx;
2500         ADS_STRUCT *ads_s = ads;
2501
2502         if (!(ctx = talloc_init("ads_current_time"))) {
2503                 return ADS_ERROR(LDAP_NO_MEMORY);
2504         }
2505
2506         /* establish a new ldap tcp session if necessary */
2507
2508         if ( !ads->ldap.ld ) {
2509                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2510                         ads->server.ldap_server )) == NULL )
2511                 {
2512                         goto done;
2513                 }
2514                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2515                 status = ads_connect( ads_s );
2516                 if ( !ADS_ERR_OK(status))
2517                         goto done;
2518         }
2519
2520         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2521         if (!ADS_ERR_OK(status)) {
2522                 goto done;
2523         }
2524
2525         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2526         if (!timestr) {
2527                 ads_msgfree(ads_s, res);
2528                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2529                 goto done;
2530         }
2531
2532         /* but save the time and offset in the original ADS_STRUCT */   
2533         
2534         ads->config.current_time = ads_parse_time(timestr);
2535
2536         if (ads->config.current_time != 0) {
2537                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2538                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2539         }
2540
2541         ads_msgfree(ads, res);
2542
2543         status = ADS_SUCCESS;
2544
2545 done:
2546         /* free any temporary ads connections */
2547         if ( ads_s != ads ) {
2548                 ads_destroy( &ads_s );
2549         }
2550         talloc_destroy(ctx);
2551
2552         return status;
2553 }
2554
2555 /********************************************************************
2556 ********************************************************************/
2557
2558 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2559 {
2560         const char *attrs[] = {"domainFunctionality", NULL};
2561         ADS_STATUS status;
2562         LDAPMessage *res;
2563         ADS_STRUCT *ads_s = ads;
2564         
2565         *val = DS_DOMAIN_FUNCTION_2000;
2566
2567         /* establish a new ldap tcp session if necessary */
2568
2569         if ( !ads->ldap.ld ) {
2570                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2571                         ads->server.ldap_server )) == NULL )
2572                 {
2573                         goto done;
2574                 }
2575                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2576                 status = ads_connect( ads_s );
2577                 if ( !ADS_ERR_OK(status))
2578                         goto done;
2579         }
2580
2581         /* If the attribute does not exist assume it is a Windows 2000 
2582            functional domain */
2583            
2584         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2585         if (!ADS_ERR_OK(status)) {
2586                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2587                         status = ADS_SUCCESS;
2588                 }
2589                 goto done;
2590         }
2591
2592         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2593                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2594         }
2595         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2596
2597         
2598         ads_msgfree(ads, res);
2599
2600 done:
2601         /* free any temporary ads connections */
2602         if ( ads_s != ads ) {
2603                 ads_destroy( &ads_s );
2604         }
2605
2606         return status;
2607 }
2608
2609 /**
2610  * find the domain sid for our domain
2611  * @param ads connection to ads server
2612  * @param sid Pointer to domain sid
2613  * @return status of search
2614  **/
2615 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2616 {
2617         const char *attrs[] = {"objectSid", NULL};
2618         LDAPMessage *res;
2619         ADS_STATUS rc;
2620
2621         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2622                            attrs, &res);
2623         if (!ADS_ERR_OK(rc)) return rc;
2624         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2625                 ads_msgfree(ads, res);
2626                 return ADS_ERROR_SYSTEM(ENOENT);
2627         }
2628         ads_msgfree(ads, res);
2629         
2630         return ADS_SUCCESS;
2631 }
2632
2633 /**
2634  * find our site name 
2635  * @param ads connection to ads server
2636  * @param mem_ctx Pointer to talloc context
2637  * @param site_name Pointer to the sitename
2638  * @return status of search
2639  **/
2640 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2641 {
2642         ADS_STATUS status;
2643         LDAPMessage *res;
2644         const char *dn, *service_name;
2645         const char *attrs[] = { "dsServiceName", NULL };
2646
2647         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2648         if (!ADS_ERR_OK(status)) {
2649                 return status;
2650         }
2651
2652         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2653         if (service_name == NULL) {
2654                 ads_msgfree(ads, res);
2655                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2656         }
2657
2658         ads_msgfree(ads, res);
2659
2660         /* go up three levels */
2661         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2662         if (dn == NULL) {
2663                 return ADS_ERROR(LDAP_NO_MEMORY);
2664         }
2665
2666         *site_name = talloc_strdup(mem_ctx, dn);
2667         if (*site_name == NULL) {
2668                 return ADS_ERROR(LDAP_NO_MEMORY);
2669         }
2670
2671         return status;
2672         /*
2673         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2674         */                                               
2675 }
2676
2677 /**
2678  * find the site dn where a machine resides
2679  * @param ads connection to ads server
2680  * @param mem_ctx Pointer to talloc context
2681  * @param computer_name name of the machine
2682  * @param site_name Pointer to the sitename
2683  * @return status of search
2684  **/
2685 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2686 {
2687         ADS_STATUS status;
2688         LDAPMessage *res;
2689         const char *parent, *filter;
2690         char *config_context = NULL;
2691         char *dn;
2692
2693         /* shortcut a query */
2694         if (strequal(computer_name, ads->config.ldap_server_name)) {
2695                 return ads_site_dn(ads, mem_ctx, site_dn);
2696         }
2697
2698         status = ads_config_path(ads, mem_ctx, &config_context);
2699         if (!ADS_ERR_OK(status)) {
2700                 return status;
2701         }
2702
2703         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2704         if (filter == NULL) {
2705                 return ADS_ERROR(LDAP_NO_MEMORY);
2706         }
2707
2708         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
2709                                filter, NULL, &res);
2710         if (!ADS_ERR_OK(status)) {
2711                 return status;
2712         }
2713
2714         if (ads_count_replies(ads, res) != 1) {
2715                 ads_msgfree(ads, res);
2716                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2717         }
2718
2719         dn = ads_get_dn(ads, res);
2720         if (dn == NULL) {
2721                 ads_msgfree(ads, res);
2722                 return ADS_ERROR(LDAP_NO_MEMORY);
2723         }
2724
2725         /* go up three levels */
2726         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2727         if (parent == NULL) {
2728                 ads_msgfree(ads, res);
2729                 ads_memfree(ads, dn);
2730                 return ADS_ERROR(LDAP_NO_MEMORY);
2731         }
2732
2733         *site_dn = talloc_strdup(mem_ctx, parent);
2734         if (*site_dn == NULL) {
2735                 ads_msgfree(ads, res);
2736                 ads_memfree(ads, dn);
2737                 return ADS_ERROR(LDAP_NO_MEMORY);
2738         }
2739
2740         ads_memfree(ads, dn);
2741         ads_msgfree(ads, res);
2742
2743         return status;
2744 }
2745
2746 /**
2747  * get the upn suffixes for a domain
2748  * @param ads connection to ads server
2749  * @param mem_ctx Pointer to talloc context
2750  * @param suffixes Pointer to an array of suffixes
2751  * @param num_suffixes Pointer to the number of suffixes
2752  * @return status of search
2753  **/
2754 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2755 {
2756         ADS_STATUS status;
2757         LDAPMessage *res;
2758         const char *base;
2759         char *config_context = NULL;
2760         const char *attrs[] = { "uPNSuffixes", NULL };
2761
2762         status = ads_config_path(ads, mem_ctx, &config_context);
2763         if (!ADS_ERR_OK(status)) {
2764                 return status;
2765         }
2766
2767         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2768         if (base == NULL) {
2769                 return ADS_ERROR(LDAP_NO_MEMORY);
2770         }
2771
2772         status = ads_search_dn(ads, &res, base, attrs);
2773         if (!ADS_ERR_OK(status)) {
2774                 return status;
2775         }
2776
2777         if (ads_count_replies(ads, res) != 1) {
2778                 ads_msgfree(ads, res);
2779                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2780         }
2781
2782         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2783         if ((*suffixes) == NULL) {
2784                 ads_msgfree(ads, res);
2785                 return ADS_ERROR(LDAP_NO_MEMORY);
2786         }
2787
2788         ads_msgfree(ads, res);
2789
2790         return status;
2791 }
2792
2793 /**
2794  * get the joinable ous for a domain
2795  * @param ads connection to ads server
2796  * @param mem_ctx Pointer to talloc context
2797  * @param ous Pointer to an array of ous
2798  * @param num_ous Pointer to the number of ous
2799  * @return status of search
2800  **/
2801 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2802                                 TALLOC_CTX *mem_ctx,
2803                                 char ***ous,
2804                                 size_t *num_ous)
2805 {
2806         ADS_STATUS status;
2807         LDAPMessage *res = NULL;
2808         LDAPMessage *msg = NULL;
2809         const char *attrs[] = { "dn", NULL };
2810         int count = 0;
2811
2812         status = ads_search(ads, &res,
2813                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
2814                             attrs);
2815         if (!ADS_ERR_OK(status)) {
2816                 return status;
2817         }
2818
2819         count = ads_count_replies(ads, res);
2820         if (count < 1) {
2821                 ads_msgfree(ads, res);
2822                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2823         }
2824
2825         for (msg = ads_first_entry(ads, res); msg;
2826              msg = ads_next_entry(ads, msg)) {
2827
2828                 char *dn = NULL;
2829
2830                 dn = ads_get_dn(ads, msg);
2831                 if (!dn) {
2832                         ads_msgfree(ads, res);
2833                         return ADS_ERROR(LDAP_NO_MEMORY);
2834                 }
2835
2836                 if (!add_string_to_array(mem_ctx, dn,
2837                                          (const char ***)ous,
2838                                          (int *)num_ous)) {
2839                         ads_memfree(ads, dn);
2840                         ads_msgfree(ads, res);
2841                         return ADS_ERROR(LDAP_NO_MEMORY);
2842                 }
2843
2844                 ads_memfree(ads, dn);
2845         }
2846
2847         ads_msgfree(ads, res);
2848
2849         return status;
2850 }
2851
2852
2853 /**
2854  * pull a DOM_SID from an extended dn string
2855  * @param mem_ctx TALLOC_CTX 
2856  * @param extended_dn string
2857  * @param flags string type of extended_dn
2858  * @param sid pointer to a DOM_SID
2859  * @return boolean inidicating success
2860  **/
2861 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2862                                   const char *extended_dn, 
2863                                   enum ads_extended_dn_flags flags, 
2864                                   DOM_SID *sid)
2865 {
2866         char *p, *q, *dn;
2867
2868         if (!extended_dn) {
2869                 return False;
2870         }
2871
2872         /* otherwise extended_dn gets stripped off */
2873         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2874                 return False;
2875         }
2876         /* 
2877          * ADS_EXTENDED_DN_HEX_STRING:
2878          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2879          *
2880          * ADS_EXTENDED_DN_STRING (only with w2k3):
2881         <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
2882          */
2883
2884         p = strchr(dn, ';');
2885         if (!p) {
2886                 return False;
2887         }
2888
2889         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2890                 return False;
2891         }
2892
2893         p += strlen(";<SID=");
2894
2895         q = strchr(p, '>');
2896         if (!q) {
2897                 return False;
2898         }
2899         
2900         *q = '\0';
2901
2902         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2903
2904         switch (flags) {
2905         
2906         case ADS_EXTENDED_DN_STRING:
2907                 if (!string_to_sid(sid, p)) {
2908                         return False;
2909                 }
2910                 break;
2911         case ADS_EXTENDED_DN_HEX_STRING: {
2912                 fstring buf;
2913                 size_t buf_len;
2914
2915                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2916                 if (buf_len == 0) {
2917                         return False;
2918                 }
2919
2920                 if (!sid_parse(buf, buf_len, sid)) {
2921                         DEBUG(10,("failed to parse sid\n"));
2922                         return False;
2923                 }
2924                 break;
2925                 }
2926         default:
2927                 DEBUG(10,("unknown extended dn format\n"));
2928                 return False;
2929         }
2930
2931         return True;
2932 }
2933
2934 /**
2935  * pull an array of DOM_SIDs from a ADS result
2936  * @param ads connection to ads server
2937  * @param mem_ctx TALLOC_CTX for allocating sid array
2938  * @param msg Results of search
2939  * @param field Attribute to retrieve
2940  * @param flags string type of extended_dn
2941  * @param sids pointer to sid array to allocate
2942  * @return the count of SIDs pulled
2943  **/
2944  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2945                                    TALLOC_CTX *mem_ctx, 
2946                                    LDAPMessage *msg, 
2947                                    const char *field,
2948                                    enum ads_extended_dn_flags flags,
2949                                    DOM_SID **sids)
2950 {
2951         int i;
2952         size_t dn_count;
2953         char **dn_strings;
2954
2955         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
2956                                            &dn_count)) == NULL) {
2957                 return 0;
2958         }
2959
2960         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2961         if (!(*sids)) {
2962                 TALLOC_FREE(dn_strings);
2963                 return 0;
2964         }
2965
2966         for (i=0; i<dn_count; i++) {
2967
2968                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
2969                                                   flags, &(*sids)[i])) {
2970                         TALLOC_FREE(*sids);
2971                         TALLOC_FREE(dn_strings);
2972                         return 0;
2973                 }
2974         }
2975
2976         TALLOC_FREE(dn_strings);
2977
2978         return dn_count;
2979 }
2980
2981 /********************************************************************
2982 ********************************************************************/
2983
2984 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2985 {
2986         LDAPMessage *res = NULL;
2987         ADS_STATUS status;
2988         int count = 0;
2989         char *name = NULL;
2990         
2991         status = ads_find_machine_acct(ads, &res, global_myname());
2992         if (!ADS_ERR_OK(status)) {
2993                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2994                         global_myname()));
2995                 goto out;
2996         }
2997                 
2998         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2999                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3000                 goto out;
3001         }
3002                 
3003         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3004                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3005         }
3006
3007 out:
3008         ads_msgfree(ads, res);
3009         
3010         return name;
3011 }
3012
3013 /********************************************************************
3014 ********************************************************************/
3015
3016 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3017 {
3018         LDAPMessage *res = NULL;
3019         ADS_STATUS status;
3020         int count = 0;
3021         char *name = NULL;
3022         
3023         status = ads_find_machine_acct(ads, &res, global_myname());
3024         if (!ADS_ERR_OK(status)) {
3025                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3026                         global_myname()));
3027                 goto out;
3028         }
3029                 
3030         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3031                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3032                 goto out;
3033         }
3034                 
3035         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3036                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3037         }
3038
3039 out:
3040         ads_msgfree(ads, res);
3041         
3042         return name;
3043 }
3044
3045 /********************************************************************
3046 ********************************************************************/
3047
3048 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3049 {
3050         LDAPMessage *res = NULL;
3051         ADS_STATUS status;
3052         int count = 0;
3053         char *name = NULL;
3054         
3055         status = ads_find_machine_acct(ads, &res, global_myname());
3056         if (!ADS_ERR_OK(status)) {
3057                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3058                         global_myname()));
3059                 goto out;
3060         }
3061                 
3062         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3063                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3064                 goto out;
3065         }
3066                 
3067         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3068                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3069         }
3070
3071 out:
3072         ads_msgfree(ads, res);
3073         
3074         return name;
3075 }
3076
3077 #if 0
3078
3079    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3080
3081 /**
3082  * Join a machine to a realm
3083  *  Creates the machine account and sets the machine password
3084  * @param ads connection to ads server
3085  * @param machine name of host to add
3086  * @param org_unit Organizational unit to place machine in
3087  * @return status of join
3088  **/
3089 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3090                         uint32 account_type, const char *org_unit)
3091 {
3092         ADS_STATUS status;
3093         LDAPMessage *res = NULL;
3094         char *machine;
3095
3096         /* machine name must be lowercase */
3097         machine = SMB_STRDUP(machine_name);
3098         strlower_m(machine);
3099
3100         /*
3101         status = ads_find_machine_acct(ads, (void **)&res, machine);
3102         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3103                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3104                 status = ads_leave_realm(ads, machine);
3105                 if (!ADS_ERR_OK(status)) {
3106                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3107                                 machine, ads->config.realm));
3108                         return status;
3109                 }
3110         }
3111         */
3112         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3113         if (!ADS_ERR_OK(status)) {
3114                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3115                 SAFE_FREE(machine);
3116                 return status;
3117         }
3118
3119         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3120         if (!ADS_ERR_OK(status)) {
3121                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3122                 SAFE_FREE(machine);
3123                 return status;
3124         }
3125
3126         SAFE_FREE(machine);
3127         ads_msgfree(ads, res);
3128
3129         return status;
3130 }
3131 #endif
3132
3133 /**
3134  * Delete a machine from the realm
3135  * @param ads connection to ads server
3136  * @param hostname Machine to remove
3137  * @return status of delete
3138  **/
3139 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3140 {
3141         ADS_STATUS status;
3142         void *msg;
3143         LDAPMessage *res;
3144         char *hostnameDN, *host;
3145         int rc;
3146         LDAPControl ldap_control;
3147         LDAPControl  * pldap_control[2] = {NULL, NULL};
3148
3149         pldap_control[0] = &ldap_control;
3150         memset(&ldap_control, 0, sizeof(LDAPControl));
3151         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3152
3153         /* hostname must be lowercase */
3154         host = SMB_STRDUP(hostname);
3155         strlower_m(host);
3156
3157         status = ads_find_machine_acct(ads, &res, host);
3158         if (!ADS_ERR_OK(status)) {
3159                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3160                 SAFE_FREE(host);
3161                 return status;
3162         }
3163
3164         msg = ads_first_entry(ads, res);
3165         if (!msg) {
3166                 SAFE_FREE(host);
3167                 return ADS_ERROR_SYSTEM(ENOENT);
3168         }
3169
3170         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3171
3172         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3173         if (rc) {
3174                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3175         }else {
3176                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3177         }
3178
3179         if (rc != LDAP_SUCCESS) {
3180                 const char *attrs[] = { "cn", NULL };
3181                 LDAPMessage *msg_sub;
3182
3183                 /* we only search with scope ONE, we do not expect any further
3184                  * objects to be created deeper */
3185
3186                 status = ads_do_search_retry(ads, hostnameDN,
3187                                              LDAP_SCOPE_ONELEVEL,
3188                                              "(objectclass=*)", attrs, &res);
3189
3190                 if (!ADS_ERR_OK(status)) {
3191                         SAFE_FREE(host);
3192                         ads_memfree(ads, hostnameDN);
3193                         return status;
3194                 }
3195
3196                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3197                         msg_sub = ads_next_entry(ads, msg_sub)) {
3198
3199                         char *dn = NULL;
3200
3201                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3202                                 SAFE_FREE(host);
3203                                 ads_memfree(ads, hostnameDN);
3204                                 return ADS_ERROR(LDAP_NO_MEMORY);
3205                         }
3206
3207                         status = ads_del_dn(ads, dn);
3208                         if (!ADS_ERR_OK(status)) {
3209                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3210                                 SAFE_FREE(host);
3211                                 ads_memfree(ads, dn);
3212                                 ads_memfree(ads, hostnameDN);
3213                                 return status;
3214                         }
3215
3216                         ads_memfree(ads, dn);
3217                 }
3218
3219                 /* there should be no subordinate objects anymore */
3220                 status = ads_do_search_retry(ads, hostnameDN,
3221                                              LDAP_SCOPE_ONELEVEL,
3222                                              "(objectclass=*)", attrs, &res);
3223
3224                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3225                         SAFE_FREE(host);
3226                         ads_memfree(ads, hostnameDN);
3227                         return status;
3228                 }
3229
3230                 /* delete hostnameDN now */
3231                 status = ads_del_dn(ads, hostnameDN);
3232                 if (!ADS_ERR_OK(status)) {
3233                         SAFE_FREE(host);
3234                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3235                         ads_memfree(ads, hostnameDN);
3236                         return status;
3237                 }
3238         }
3239
3240         ads_memfree(ads, hostnameDN);
3241
3242         status = ads_find_machine_acct(ads, &res, host);
3243         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3244                 DEBUG(3, ("Failed to remove host account.\n"));
3245                 SAFE_FREE(host);
3246                 return status;
3247         }
3248
3249         SAFE_FREE(host);
3250         return status;
3251 }
3252
3253 /**
3254  * pull all token-sids from an LDAP dn
3255  * @param ads connection to ads server
3256  * @param mem_ctx TALLOC_CTX for allocating sid array
3257  * @param dn of LDAP object
3258  * @param user_sid pointer to DOM_SID (objectSid)
3259  * @param primary_group_sid pointer to DOM_SID (self composed)
3260  * @param sids pointer to sid array to allocate
3261  * @param num_sids counter of SIDs pulled
3262  * @return status of token query
3263  **/
3264  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3265                               TALLOC_CTX *mem_ctx,
3266                               const char *dn,
3267                               DOM_SID *user_sid,
3268                               DOM_SID *primary_group_sid,
3269                               DOM_SID **sids,
3270                               size_t *num_sids)
3271 {
3272         ADS_STATUS status;
3273         LDAPMessage *res = NULL;
3274         int count = 0;
3275         size_t tmp_num_sids;
3276         DOM_SID *tmp_sids;
3277         DOM_SID tmp_user_sid;
3278         DOM_SID tmp_primary_group_sid;
3279         uint32 pgid;
3280         const char *attrs[] = {
3281                 "objectSid",
3282                 "tokenGroups",
3283                 "primaryGroupID",
3284                 NULL
3285         };
3286
3287         status = ads_search_retry_dn(ads, &res, dn, attrs);
3288         if (!ADS_ERR_OK(status)) {
3289                 return status;
3290         }
3291
3292         count = ads_count_replies(ads, res);
3293         if (count != 1) {
3294                 ads_msgfree(ads, res);
3295                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3296         }
3297
3298         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3299                 ads_msgfree(ads, res);
3300                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3301         }
3302
3303         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3304                 ads_msgfree(ads, res);
3305                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3306         }
3307
3308         {
3309                 /* hack to compose the primary group sid without knowing the
3310                  * domsid */
3311
3312                 DOM_SID domsid;
3313                 uint32 dummy_rid;
3314
3315                 sid_copy(&domsid, &tmp_user_sid);
3316
3317                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3318                         ads_msgfree(ads, res);
3319                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3320                 }
3321
3322                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3323                         ads_msgfree(ads, res);
3324                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3325                 }
3326         }
3327
3328         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3329
3330         if (tmp_num_sids == 0 || !tmp_sids) {
3331                 ads_msgfree(ads, res);
3332                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3333         }
3334
3335         if (num_sids) {
3336                 *num_sids = tmp_num_sids;
3337         }
3338
3339         if (sids) {
3340                 *sids = tmp_sids;
3341         }
3342
3343         if (user_sid) {
3344                 *user_sid = tmp_user_sid;
3345         }
3346
3347         if (primary_group_sid) {
3348                 *primary_group_sid = tmp_primary_group_sid;
3349         }
3350
3351         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3352
3353         ads_msgfree(ads, res);
3354         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3355 }
3356
3357 /**
3358  * Find a sAMAccoutName in LDAP
3359  * @param ads connection to ads server
3360  * @param mem_ctx TALLOC_CTX for allocating sid array
3361  * @param samaccountname to search
3362  * @param uac_ret uint32 pointer userAccountControl attribute value
3363  * @param dn_ret pointer to dn
3364  * @return status of token query
3365  **/
3366 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3367                                TALLOC_CTX *mem_ctx,
3368                                const char *samaccountname,
3369                                uint32 *uac_ret,
3370                                const char **dn_ret)
3371 {
3372         ADS_STATUS status;
3373         const char *attrs[] = { "userAccountControl", NULL };
3374         const char *filter;
3375         LDAPMessage *res = NULL;
3376         char *dn = NULL;
3377         uint32 uac = 0;
3378
3379         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3380                 samaccountname);
3381         if (filter == NULL) {
3382                 goto out;
3383         }
3384
3385         status = ads_do_search_all(ads, ads->config.bind_path,
3386                                    LDAP_SCOPE_SUBTREE,
3387                                    filter, attrs, &res);
3388         
3389         if (!ADS_ERR_OK(status)) {
3390                 goto out;
3391         }
3392
3393         if (ads_count_replies(ads, res) != 1) {
3394                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3395                 goto out;
3396         }
3397
3398         dn = ads_get_dn(ads, res);
3399         if (dn == NULL) {
3400                 status = ADS_ERROR(LDAP_NO_MEMORY);
3401                 goto out;
3402         }
3403
3404         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3405                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3406                 goto out;
3407         }
3408
3409         if (uac_ret) {
3410                 *uac_ret = uac;
3411         }
3412
3413         if (dn_ret) {
3414                 *dn_ret = talloc_strdup(mem_ctx, dn);
3415                 if (!*dn_ret) {
3416                         status = ADS_ERROR(LDAP_NO_MEMORY);
3417                         goto out;
3418                 }
3419         }
3420  out:
3421         ads_memfree(ads, dn);
3422         ads_msgfree(ads, res);
3423
3424         return status;
3425 }
3426
3427 /**
3428  * find our configuration path 
3429  * @param ads connection to ads server
3430  * @param mem_ctx Pointer to talloc context
3431  * @param config_path Pointer to the config path
3432  * @return status of search
3433  **/
3434 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3435                            TALLOC_CTX *mem_ctx, 
3436                            char **config_path)
3437 {
3438         ADS_STATUS status;
3439         LDAPMessage *res = NULL;
3440         const char *config_context = NULL;
3441         const char *attrs[] = { "configurationNamingContext", NULL };
3442
3443         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3444                                "(objectclass=*)", attrs, &res);
3445         if (!ADS_ERR_OK(status)) {
3446                 return status;
3447         }
3448
3449         config_context = ads_pull_string(ads, mem_ctx, res, 
3450                                          "configurationNamingContext");
3451         ads_msgfree(ads, res);
3452         if (!config_context) {
3453                 return ADS_ERROR(LDAP_NO_MEMORY);
3454         }
3455
3456         if (config_path) {
3457                 *config_path = talloc_strdup(mem_ctx, config_context);
3458                 if (!*config_path) {
3459                         return ADS_ERROR(LDAP_NO_MEMORY);
3460                 }
3461         }
3462
3463         return ADS_ERROR(LDAP_SUCCESS);
3464 }
3465
3466 /**
3467  * find the displayName of an extended right 
3468  * @param ads connection to ads server
3469  * @param config_path The config path
3470  * @param mem_ctx Pointer to talloc context
3471  * @param GUID struct of the rightsGUID
3472  * @return status of search
3473  **/
3474 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3475                                                 const char *config_path, 
3476                                                 TALLOC_CTX *mem_ctx, 
3477                                                 const struct GUID *rights_guid)
3478 {
3479         ADS_STATUS rc;
3480         LDAPMessage *res = NULL;
3481         char *expr = NULL;
3482         const char *attrs[] = { "displayName", NULL };
3483         const char *result = NULL;
3484         const char *path;
3485
3486         if (!ads || !mem_ctx || !rights_guid) {
3487                 goto done;
3488         }
3489
3490         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3491                                smb_uuid_string(mem_ctx, *rights_guid));
3492         if (!expr) {
3493                 goto done;
3494         }
3495
3496         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3497         if (!path) {
3498                 goto done;
3499         }
3500
3501         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3502                                  expr, attrs, &res);
3503         if (!ADS_ERR_OK(rc)) {
3504                 goto done;
3505         }
3506
3507         if (ads_count_replies(ads, res) != 1) {
3508                 goto done;
3509         }
3510
3511         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3512
3513  done:
3514         ads_msgfree(ads, res);
3515         return result;
3516         
3517 }
3518
3519 #endif