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