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