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