source: vendor/3.6.0/source3/libads/ldap.c

Last change on this file was 740, checked in by Silvan Scherrer, 13 years ago

Samba Server: update vendor to 3.6.0

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