source: branches/samba-3.5.x/source3/libads/ldap.c@ 736

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

Samba Server 3.5: update branche to 3.5.16

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