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