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