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