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