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

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

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