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