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