winbindd: queryuser - only get group name if needed
[Samba.git] / source3 / libads / ldap.c
blobc18837cc524d3097f83b20a33e167078288e7ca5
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
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.
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.
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/>.
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"
35 #ifdef HAVE_LDAP
37 /**
38 * @file ldap.c
39 * @brief basic ldap client-side routines for ads server communications
41 * The routines contained here should do the necessary ldap calls for
42 * ads setups.
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 **/
51 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
53 static SIG_ATOMIC_T gotalarm;
55 /***************************************************************
56 Signal function to tell us we timed out.
57 ****************************************************************/
59 static void gotalarm_sig(int signum)
61 gotalarm = 1;
64 LDAP *ldap_open_with_timeout(const char *server,
65 struct sockaddr_storage *ss,
66 int port, unsigned int to)
68 LDAP *ldp = NULL;
69 int ldap_err;
70 char *uri;
72 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
73 "%u seconds\n", server, port, to));
75 if (to) {
76 /* Setup timeout */
77 gotalarm = 0;
78 CatchSignal(SIGALRM, gotalarm_sig);
79 alarm(to);
80 /* End setup timeout. */
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);
90 if (uri == NULL) {
91 return NULL;
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;
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));
111 if (to) {
112 /* Teardown timeout. */
113 alarm(0);
114 CatchSignal(SIGALRM, SIG_IGN);
117 return ldp;
120 static 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 )
131 int to = lp_ldap_timeout();
132 struct timeval timeout;
133 struct timeval *timeout_ptr = NULL;
134 int result;
136 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
137 gotalarm = 0;
139 if (to) {
140 timeout.tv_sec = to;
141 timeout.tv_usec = 0;
142 timeout_ptr = &timeout;
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. */
155 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
156 attrsonly, sctrls, cctrls, timeout_ptr,
157 sizelimit, res);
159 if (to) {
160 /* Teardown alarm timeout. */
161 CatchSignal(SIGALRM, SIG_IGN);
162 alarm(0);
165 if (gotalarm != 0)
166 return LDAP_TIMELIMIT_EXCEEDED;
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.
174 if (*res == NULL) {
175 return LDAP_TIMELIMIT_EXCEEDED;
178 return result;
181 /**********************************************
182 Do client and server sitename match ?
183 **********************************************/
185 bool ads_sitename_match(ADS_STRUCT *ads)
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;
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;
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;
205 /**********************************************
206 Is this the closest DC ?
207 **********************************************/
209 bool ads_closest_dc(ADS_STRUCT *ads)
211 if (ads->config.flags & NBT_SERVER_CLOSEST) {
212 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
213 return True;
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;
222 if (ads->config.client_site_name == NULL) {
223 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
224 return True;
227 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
228 ads->config.ldap_server_name));
230 return False;
235 try a connection to a given ldap server, returning True and setting the servers IP
236 in the ads struct if successful
238 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
239 struct sockaddr_storage *ss)
241 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
242 TALLOC_CTX *frame = talloc_stackframe();
243 bool ret = false;
244 char addr[INET6_ADDRSTRLEN];
246 if (ss == NULL) {
247 TALLOC_FREE(frame);
248 return False;
251 print_sockaddr(addr, sizeof(addr), ss);
253 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
254 addr, ads->server.realm));
256 ZERO_STRUCT( cldap_reply );
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;
264 /* Check the CLDAP reply flags */
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;
273 /* Fill in the ads->config values */
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);
282 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
283 ads->config.flags)) {
284 ret = false;
285 goto out;
288 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
289 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
290 if (!strupper_m(ads->config.realm)) {
291 ret = false;
292 goto out;
295 ads->config.bind_path = ads_build_dn(ads->config.realm);
296 if (*cldap_reply.server_site) {
297 ads->config.server_site_name =
298 SMB_STRDUP(cldap_reply.server_site);
300 if (*cldap_reply.client_site) {
301 ads->config.client_site_name =
302 SMB_STRDUP(cldap_reply.client_site);
304 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
306 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
307 ads->ldap.ss = *ss;
309 /* Store our site name. */
310 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
311 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
313 /* Leave this until last so that the flags are not clobbered */
314 ads->config.flags = cldap_reply.server_type;
316 ret = true;
318 out:
320 TALLOC_FREE(frame);
321 return ret;
324 /**********************************************************************
325 send a cldap ping to list of servers, one at a time, until one of
326 them answers it's an ldap server. Record success in the ADS_STRUCT.
327 Take note of and update negative connection cache.
328 **********************************************************************/
330 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
331 struct ip_service *ip_list, int count)
333 int i;
334 bool ok;
336 for (i = 0; i < count; i++) {
337 char server[INET6_ADDRSTRLEN];
339 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
341 if (!NT_STATUS_IS_OK(
342 check_negative_conn_cache(domain, server)))
343 continue;
345 /* Returns ok only if it matches the correct server type */
346 ok = ads_try_connect(ads, false, &ip_list[i].ss);
348 if (ok) {
349 return NT_STATUS_OK;
352 /* keep track of failures */
353 add_failed_connection_entry(domain, server,
354 NT_STATUS_UNSUCCESSFUL);
357 return NT_STATUS_NO_LOGON_SERVERS;
360 /***************************************************************************
361 resolve a name and perform an "ldap ping" using NetBIOS and related methods
362 ****************************************************************************/
364 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
365 const char *domain, const char *realm)
367 int count, i;
368 struct ip_service *ip_list;
369 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
371 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
372 domain));
374 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
375 false);
376 if (!NT_STATUS_IS_OK(status)) {
377 return status;
380 /* remove servers which are known to be dead based on
381 the corresponding DNS method */
382 if (*realm) {
383 for (i = 0; i < count; ++i) {
384 char server[INET6_ADDRSTRLEN];
386 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
388 if(!NT_STATUS_IS_OK(
389 check_negative_conn_cache(realm, server))) {
390 /* Ensure we add the workgroup name for this
391 IP address as negative too. */
392 add_failed_connection_entry(
393 domain, server,
394 NT_STATUS_UNSUCCESSFUL);
399 status = cldap_ping_list(ads, domain, ip_list, count);
401 SAFE_FREE(ip_list);
403 return status;
407 /**********************************************************************
408 resolve a name and perform an "ldap ping" using DNS
409 **********************************************************************/
411 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
412 const char *realm)
414 int count;
415 struct ip_service *ip_list;
416 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
418 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
419 realm));
421 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
422 true);
423 if (!NT_STATUS_IS_OK(status)) {
424 return status;
427 status = cldap_ping_list(ads, realm, ip_list, count);
429 SAFE_FREE(ip_list);
431 return status;
434 /**********************************************************************
435 Try to find an AD dc using our internal name resolution routines
436 Try the realm first and then then workgroup name if netbios is not
437 disabled
438 **********************************************************************/
440 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
442 const char *c_domain = "";
443 const char *c_realm;
444 bool use_own_domain = False;
445 char *sitename = NULL;
446 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
447 bool ok = false;
449 /* if the realm and workgroup are both empty, assume they are ours */
451 /* realm */
452 c_realm = ads->server.realm;
454 if (c_realm == NULL)
455 c_realm = "";
457 if (!*c_realm) {
458 /* special case where no realm and no workgroup means our own */
459 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
460 use_own_domain = True;
461 c_realm = lp_realm();
465 if (!lp_disable_netbios()) {
466 if (use_own_domain) {
467 c_domain = lp_workgroup();
468 } else {
469 c_domain = ads->server.workgroup;
470 if (!*c_realm && (!c_domain || !*c_domain)) {
471 c_domain = lp_workgroup();
475 if (!c_domain) {
476 c_domain = "";
480 if (!*c_realm && !*c_domain) {
481 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
482 "what to do\n"));
483 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
487 * In case of LDAP we use get_dc_name() as that
488 * creates the custom krb5.conf file
490 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
491 fstring srv_name;
492 struct sockaddr_storage ip_out;
494 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
495 " and falling back to domain '%s'\n",
496 c_realm, c_domain));
498 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
499 if (ok) {
501 * we call ads_try_connect() to fill in the
502 * ads->config details
504 ok = ads_try_connect(ads, false, &ip_out);
505 if (ok) {
506 return NT_STATUS_OK;
510 return NT_STATUS_NO_LOGON_SERVERS;
513 if (*c_realm) {
514 sitename = sitename_fetch(talloc_tos(), c_realm);
515 status = resolve_and_ping_dns(ads, sitename, c_realm);
517 if (NT_STATUS_IS_OK(status)) {
518 TALLOC_FREE(sitename);
519 return status;
522 /* In case we failed to contact one of our closest DC on our
523 * site we
524 * need to try to find another DC, retry with a site-less SRV
525 * DNS query
526 * - Guenther */
528 if (sitename) {
529 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
530 "our site (%s), Trying to find another DC "
531 "for realm '%s' (domain '%s')\n",
532 sitename, c_realm, c_domain));
533 namecache_delete(c_realm, 0x1C);
534 status =
535 resolve_and_ping_dns(ads, NULL, c_realm);
537 if (NT_STATUS_IS_OK(status)) {
538 TALLOC_FREE(sitename);
539 return status;
543 TALLOC_FREE(sitename);
546 /* try netbios as fallback - if permitted,
547 or if configuration specifically requests it */
548 if (*c_domain) {
549 if (*c_realm) {
550 DEBUG(3, ("ads_find_dc: falling back to netbios "
551 "name resolution for domain '%s' (realm '%s')\n",
552 c_domain, c_realm));
555 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
556 if (NT_STATUS_IS_OK(status)) {
557 return status;
561 DEBUG(1, ("ads_find_dc: "
562 "name resolution for realm '%s' (domain '%s') failed: %s\n",
563 c_realm, c_domain, nt_errstr(status)));
564 return status;
567 * Connect to the LDAP server
568 * @param ads Pointer to an existing ADS_STRUCT
569 * @return status of connection
571 ADS_STATUS ads_connect(ADS_STRUCT *ads)
573 int version = LDAP_VERSION3;
574 ADS_STATUS status;
575 NTSTATUS ntstatus;
576 char addr[INET6_ADDRSTRLEN];
578 ZERO_STRUCT(ads->ldap);
579 ZERO_STRUCT(ads->ldap_wrap_data);
580 ads->ldap.last_attempt = time_mono(NULL);
581 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
583 /* try with a user specified server */
585 if (DEBUGLEVEL >= 11) {
586 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
587 DEBUG(11,("ads_connect: entering\n"));
588 DEBUGADD(11,("%s\n", s));
589 TALLOC_FREE(s);
592 if (ads->server.ldap_server) {
593 bool ok = false;
594 struct sockaddr_storage ss;
596 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
597 if (!ok) {
598 DEBUG(5,("ads_connect: unable to resolve name %s\n",
599 ads->server.ldap_server));
600 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
601 goto out;
603 ok = ads_try_connect(ads, ads->server.gc, &ss);
604 if (ok) {
605 goto got_connection;
608 /* The choice of which GC use is handled one level up in
609 ads_connect_gc(). If we continue on from here with
610 ads_find_dc() we will get GC searches on port 389 which
611 doesn't work. --jerry */
613 if (ads->server.gc == true) {
614 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
617 if (ads->server.no_fallback) {
618 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
619 goto out;
623 ntstatus = ads_find_dc(ads);
624 if (NT_STATUS_IS_OK(ntstatus)) {
625 goto got_connection;
628 status = ADS_ERROR_NT(ntstatus);
629 goto out;
631 got_connection:
633 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
634 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
636 if (!ads->auth.user_name) {
637 /* Must use the userPrincipalName value here or sAMAccountName
638 and not servicePrincipalName; found by Guenther Deschner */
640 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
641 DEBUG(0,("ads_connect: asprintf fail.\n"));
642 ads->auth.user_name = NULL;
646 if (!ads->auth.realm) {
647 ads->auth.realm = SMB_STRDUP(ads->config.realm);
650 if (!ads->auth.kdc_server) {
651 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
652 ads->auth.kdc_server = SMB_STRDUP(addr);
655 /* If the caller() requested no LDAP bind, then we are done */
657 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
658 status = ADS_SUCCESS;
659 goto out;
662 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
663 if (!ads->ldap_wrap_data.mem_ctx) {
664 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
665 goto out;
668 /* Otherwise setup the TCP LDAP session */
670 ads->ldap.ld = ldap_open_with_timeout(addr,
671 &ads->ldap.ss,
672 ads->ldap.port, lp_ldap_timeout());
673 if (ads->ldap.ld == NULL) {
674 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
675 goto out;
677 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
679 /* cache the successful connection for workgroup and realm */
680 if (ads_closest_dc(ads)) {
681 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
682 saf_store( ads->server.realm, ads->config.ldap_server_name);
685 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
687 if ( lp_ldap_ssl_ads() ) {
688 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
689 if (!ADS_ERR_OK(status)) {
690 goto out;
694 /* fill in the current time and offsets */
696 status = ads_current_time( ads );
697 if ( !ADS_ERR_OK(status) ) {
698 goto out;
701 /* Now do the bind */
703 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
704 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
705 goto out;
708 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
709 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
710 goto out;
713 status = ads_sasl_bind(ads);
715 out:
716 if (DEBUGLEVEL >= 11) {
717 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
718 DEBUG(11,("ads_connect: leaving with: %s\n",
719 ads_errstr(status)));
720 DEBUGADD(11,("%s\n", s));
721 TALLOC_FREE(s);
724 return status;
728 * Connect to the LDAP server using given credentials
729 * @param ads Pointer to an existing ADS_STRUCT
730 * @return status of connection
732 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
734 ads->auth.flags |= ADS_AUTH_USER_CREDS;
736 return ads_connect(ads);
740 * Disconnect the LDAP server
741 * @param ads Pointer to an existing ADS_STRUCT
743 void ads_disconnect(ADS_STRUCT *ads)
745 if (ads->ldap.ld) {
746 ldap_unbind(ads->ldap.ld);
747 ads->ldap.ld = NULL;
749 if (ads->ldap_wrap_data.wrap_ops &&
750 ads->ldap_wrap_data.wrap_ops->disconnect) {
751 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
753 if (ads->ldap_wrap_data.mem_ctx) {
754 talloc_free(ads->ldap_wrap_data.mem_ctx);
756 ZERO_STRUCT(ads->ldap);
757 ZERO_STRUCT(ads->ldap_wrap_data);
761 Duplicate a struct berval into talloc'ed memory
763 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
765 struct berval *value;
767 if (!in_val) return NULL;
769 value = talloc_zero(ctx, struct berval);
770 if (value == NULL)
771 return NULL;
772 if (in_val->bv_len == 0) return value;
774 value->bv_len = in_val->bv_len;
775 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
776 in_val->bv_len);
777 return value;
781 Make a values list out of an array of (struct berval *)
783 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
784 const struct berval **in_vals)
786 struct berval **values;
787 int i;
789 if (!in_vals) return NULL;
790 for (i=0; in_vals[i]; i++)
791 ; /* count values */
792 values = talloc_zero_array(ctx, struct berval *, i+1);
793 if (!values) return NULL;
795 for (i=0; in_vals[i]; i++) {
796 values[i] = dup_berval(ctx, in_vals[i]);
798 return values;
802 UTF8-encode a values list out of an array of (char *)
804 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
806 char **values;
807 int i;
808 size_t size;
810 if (!in_vals) return NULL;
811 for (i=0; in_vals[i]; i++)
812 ; /* count values */
813 values = talloc_zero_array(ctx, char *, i+1);
814 if (!values) return NULL;
816 for (i=0; in_vals[i]; i++) {
817 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
818 TALLOC_FREE(values);
819 return NULL;
822 return values;
826 Pull a (char *) array out of a UTF8-encoded values list
828 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
830 char **values;
831 int i;
832 size_t converted_size;
834 if (!in_vals) return NULL;
835 for (i=0; in_vals[i]; i++)
836 ; /* count values */
837 values = talloc_zero_array(ctx, char *, i+1);
838 if (!values) return NULL;
840 for (i=0; in_vals[i]; i++) {
841 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
842 &converted_size)) {
843 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
844 "%s", strerror(errno)));
847 return values;
851 * Do a search with paged results. cookie must be null on the first
852 * call, and then returned on each subsequent call. It will be null
853 * again when the entire search is complete
854 * @param ads connection to ads server
855 * @param bind_path Base dn for the search
856 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
857 * @param expr Search expression - specified in local charset
858 * @param attrs Attributes to retrieve - specified in utf8 or ascii
859 * @param res ** which will contain results - free res* with ads_msgfree()
860 * @param count Number of entries retrieved on this page
861 * @param cookie The paged results cookie to be returned on subsequent calls
862 * @return status of search
864 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
865 const char *bind_path,
866 int scope, const char *expr,
867 const char **attrs, void *args,
868 LDAPMessage **res,
869 int *count, struct berval **cookie)
871 int rc, i, version;
872 char *utf8_expr, *utf8_path, **search_attrs = NULL;
873 size_t converted_size;
874 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
875 BerElement *cookie_be = NULL;
876 struct berval *cookie_bv= NULL;
877 BerElement *ext_be = NULL;
878 struct berval *ext_bv= NULL;
880 TALLOC_CTX *ctx;
881 ads_control *external_control = (ads_control *) args;
883 *res = NULL;
885 if (!(ctx = talloc_init("ads_do_paged_search_args")))
886 return ADS_ERROR(LDAP_NO_MEMORY);
888 /* 0 means the conversion worked but the result was empty
889 so we only fail if it's -1. In any case, it always
890 at least nulls out the dest */
891 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
892 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
894 rc = LDAP_NO_MEMORY;
895 goto done;
898 if (!attrs || !(*attrs))
899 search_attrs = NULL;
900 else {
901 /* This would be the utf8-encoded version...*/
902 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
903 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
904 rc = LDAP_NO_MEMORY;
905 goto done;
909 /* Paged results only available on ldap v3 or later */
910 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
911 if (version < LDAP_VERSION3) {
912 rc = LDAP_NOT_SUPPORTED;
913 goto done;
916 cookie_be = ber_alloc_t(LBER_USE_DER);
917 if (*cookie) {
918 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
919 ber_bvfree(*cookie); /* don't need it from last time */
920 *cookie = NULL;
921 } else {
922 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
924 ber_flatten(cookie_be, &cookie_bv);
925 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
926 PagedResults.ldctl_iscritical = (char) 1;
927 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
928 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
930 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
931 NoReferrals.ldctl_iscritical = (char) 0;
932 NoReferrals.ldctl_value.bv_len = 0;
933 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
935 if (external_control &&
936 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
937 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
939 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
940 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
942 /* win2k does not accept a ldctl_value beeing passed in */
944 if (external_control->val != 0) {
946 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
947 rc = LDAP_NO_MEMORY;
948 goto done;
951 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
952 rc = LDAP_NO_MEMORY;
953 goto done;
955 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
956 rc = LDAP_NO_MEMORY;
957 goto done;
960 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
961 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
963 } else {
964 ExternalCtrl.ldctl_value.bv_len = 0;
965 ExternalCtrl.ldctl_value.bv_val = NULL;
968 controls[0] = &NoReferrals;
969 controls[1] = &PagedResults;
970 controls[2] = &ExternalCtrl;
971 controls[3] = NULL;
973 } else {
974 controls[0] = &NoReferrals;
975 controls[1] = &PagedResults;
976 controls[2] = NULL;
979 /* we need to disable referrals as the openldap libs don't
980 handle them and paged results at the same time. Using them
981 together results in the result record containing the server
982 page control being removed from the result list (tridge/jmcd)
984 leaving this in despite the control that says don't generate
985 referrals, in case the server doesn't support it (jmcd)
987 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
989 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
990 search_attrs, 0, controls,
991 NULL, LDAP_NO_LIMIT,
992 (LDAPMessage **)res);
994 ber_free(cookie_be, 1);
995 ber_bvfree(cookie_bv);
997 if (rc) {
998 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
999 ldap_err2string(rc)));
1000 if (rc == LDAP_OTHER) {
1001 char *ldap_errmsg;
1002 int ret;
1004 ret = ldap_parse_result(ads->ldap.ld,
1005 *res,
1006 NULL,
1007 NULL,
1008 &ldap_errmsg,
1009 NULL,
1010 NULL,
1012 if (ret == LDAP_SUCCESS) {
1013 DEBUG(3, ("ldap_search_with_timeout(%s) "
1014 "error: %s\n", expr, ldap_errmsg));
1015 ldap_memfree(ldap_errmsg);
1018 goto done;
1021 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1022 NULL, &rcontrols, 0);
1024 if (!rcontrols) {
1025 goto done;
1028 for (i=0; rcontrols[i]; i++) {
1029 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1030 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1031 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1032 &cookie_bv);
1033 /* the berval is the cookie, but must be freed when
1034 it is all done */
1035 if (cookie_bv->bv_len) /* still more to do */
1036 *cookie=ber_bvdup(cookie_bv);
1037 else
1038 *cookie=NULL;
1039 ber_bvfree(cookie_bv);
1040 ber_free(cookie_be, 1);
1041 break;
1044 ldap_controls_free(rcontrols);
1046 done:
1047 talloc_destroy(ctx);
1049 if (ext_be) {
1050 ber_free(ext_be, 1);
1053 if (ext_bv) {
1054 ber_bvfree(ext_bv);
1057 if (rc != LDAP_SUCCESS && *res != NULL) {
1058 ads_msgfree(ads, *res);
1059 *res = NULL;
1062 /* if/when we decide to utf8-encode attrs, take out this next line */
1063 TALLOC_FREE(search_attrs);
1065 return ADS_ERROR(rc);
1068 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1069 int scope, const char *expr,
1070 const char **attrs, LDAPMessage **res,
1071 int *count, struct berval **cookie)
1073 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1078 * Get all results for a search. This uses ads_do_paged_search() to return
1079 * all entries in a large search.
1080 * @param ads connection to ads server
1081 * @param bind_path Base dn for the search
1082 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1083 * @param expr Search expression
1084 * @param attrs Attributes to retrieve
1085 * @param res ** which will contain results - free res* with ads_msgfree()
1086 * @return status of search
1088 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1089 int scope, const char *expr,
1090 const char **attrs, void *args,
1091 LDAPMessage **res)
1093 struct berval *cookie = NULL;
1094 int count = 0;
1095 ADS_STATUS status;
1097 *res = NULL;
1098 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1099 &count, &cookie);
1101 if (!ADS_ERR_OK(status))
1102 return status;
1104 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1105 while (cookie) {
1106 LDAPMessage *res2 = NULL;
1107 LDAPMessage *msg, *next;
1109 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1110 attrs, args, &res2, &count, &cookie);
1111 if (!ADS_ERR_OK(status)) {
1112 break;
1115 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1116 that this works on all ldap libs, but I have only tested with openldap */
1117 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1118 next = ads_next_message(ads, msg);
1119 ldap_add_result_entry((LDAPMessage **)res, msg);
1121 /* note that we do not free res2, as the memory is now
1122 part of the main returned list */
1124 #else
1125 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1126 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1127 #endif
1129 return status;
1132 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1133 int scope, const char *expr,
1134 const char **attrs, LDAPMessage **res)
1136 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1139 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1140 int scope, const char *expr,
1141 const char **attrs, uint32_t sd_flags,
1142 LDAPMessage **res)
1144 ads_control args;
1146 args.control = ADS_SD_FLAGS_OID;
1147 args.val = sd_flags;
1148 args.critical = True;
1150 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1155 * Run a function on all results for a search. Uses ads_do_paged_search() and
1156 * runs the function as each page is returned, using ads_process_results()
1157 * @param ads connection to ads server
1158 * @param bind_path Base dn for the search
1159 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1160 * @param expr Search expression - specified in local charset
1161 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1162 * @param fn Function which takes attr name, values list, and data_area
1163 * @param data_area Pointer which is passed to function on each call
1164 * @return status of search
1166 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1167 int scope, const char *expr, const char **attrs,
1168 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1169 void *data_area)
1171 struct berval *cookie = NULL;
1172 int count = 0;
1173 ADS_STATUS status;
1174 LDAPMessage *res;
1176 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1177 &count, &cookie);
1179 if (!ADS_ERR_OK(status)) return status;
1181 ads_process_results(ads, res, fn, data_area);
1182 ads_msgfree(ads, res);
1184 while (cookie) {
1185 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1186 &res, &count, &cookie);
1188 if (!ADS_ERR_OK(status)) break;
1190 ads_process_results(ads, res, fn, data_area);
1191 ads_msgfree(ads, res);
1194 return status;
1198 * Do a search with a timeout.
1199 * @param ads connection to ads server
1200 * @param bind_path Base dn for the search
1201 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1202 * @param expr Search expression
1203 * @param attrs Attributes to retrieve
1204 * @param res ** which will contain results - free res* with ads_msgfree()
1205 * @return status of search
1207 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1208 const char *expr,
1209 const char **attrs, LDAPMessage **res)
1211 int rc;
1212 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1213 size_t converted_size;
1214 TALLOC_CTX *ctx;
1216 *res = NULL;
1217 if (!(ctx = talloc_init("ads_do_search"))) {
1218 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1219 return ADS_ERROR(LDAP_NO_MEMORY);
1222 /* 0 means the conversion worked but the result was empty
1223 so we only fail if it's negative. In any case, it always
1224 at least nulls out the dest */
1225 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1226 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1228 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1229 rc = LDAP_NO_MEMORY;
1230 goto done;
1233 if (!attrs || !(*attrs))
1234 search_attrs = NULL;
1235 else {
1236 /* This would be the utf8-encoded version...*/
1237 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1238 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1240 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1241 rc = LDAP_NO_MEMORY;
1242 goto done;
1246 /* see the note in ads_do_paged_search - we *must* disable referrals */
1247 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1249 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1250 search_attrs, 0, NULL, NULL,
1251 LDAP_NO_LIMIT,
1252 (LDAPMessage **)res);
1254 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1255 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1256 rc = 0;
1259 done:
1260 talloc_destroy(ctx);
1261 /* if/when we decide to utf8-encode attrs, take out this next line */
1262 TALLOC_FREE(search_attrs);
1263 return ADS_ERROR(rc);
1266 * Do a general ADS search
1267 * @param ads connection to ads server
1268 * @param res ** which will contain results - free res* with ads_msgfree()
1269 * @param expr Search expression
1270 * @param attrs Attributes to retrieve
1271 * @return status of search
1273 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1274 const char *expr, const char **attrs)
1276 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1277 expr, attrs, res);
1281 * Do a search on a specific DistinguishedName
1282 * @param ads connection to ads server
1283 * @param res ** which will contain results - free res* with ads_msgfree()
1284 * @param dn DistinguishName to search
1285 * @param attrs Attributes to retrieve
1286 * @return status of search
1288 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1289 const char *dn, const char **attrs)
1291 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1292 attrs, res);
1296 * Free up memory from a ads_search
1297 * @param ads connection to ads server
1298 * @param msg Search results to free
1300 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1302 if (!msg) return;
1303 ldap_msgfree(msg);
1307 * Get a dn from search results
1308 * @param ads connection to ads server
1309 * @param msg Search result
1310 * @return dn string
1312 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1314 char *utf8_dn, *unix_dn;
1315 size_t converted_size;
1317 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1319 if (!utf8_dn) {
1320 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1321 return NULL;
1324 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1325 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1326 utf8_dn ));
1327 return NULL;
1329 ldap_memfree(utf8_dn);
1330 return unix_dn;
1334 * Get the parent from a dn
1335 * @param dn the dn to return the parent from
1336 * @return parent dn string
1338 char *ads_parent_dn(const char *dn)
1340 char *p;
1342 if (dn == NULL) {
1343 return NULL;
1346 p = strchr(dn, ',');
1348 if (p == NULL) {
1349 return NULL;
1352 return p+1;
1356 * Find a machine account given a hostname
1357 * @param ads connection to ads server
1358 * @param res ** which will contain results - free res* with ads_msgfree()
1359 * @param host Hostname to search for
1360 * @return status of search
1362 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1363 const char *machine)
1365 ADS_STATUS status;
1366 char *expr;
1367 const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1369 *res = NULL;
1371 /* the easiest way to find a machine account anywhere in the tree
1372 is to look for hostname$ */
1373 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1374 DEBUG(1, ("asprintf failed!\n"));
1375 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1378 status = ads_search(ads, res, expr, attrs);
1379 SAFE_FREE(expr);
1380 return status;
1384 * Initialize a list of mods to be used in a modify request
1385 * @param ctx An initialized TALLOC_CTX
1386 * @return allocated ADS_MODLIST
1388 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1390 #define ADS_MODLIST_ALLOC_SIZE 10
1391 LDAPMod **mods;
1393 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1394 /* -1 is safety to make sure we don't go over the end.
1395 need to reset it to NULL before doing ldap modify */
1396 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1398 return (ADS_MODLIST)mods;
1403 add an attribute to the list, with values list already constructed
1405 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1406 int mod_op, const char *name,
1407 const void *_invals)
1409 int curmod;
1410 LDAPMod **modlist = (LDAPMod **) *mods;
1411 struct berval **ber_values = NULL;
1412 char **char_values = NULL;
1414 if (!_invals) {
1415 mod_op = LDAP_MOD_DELETE;
1416 } else {
1417 if (mod_op & LDAP_MOD_BVALUES) {
1418 const struct berval **b;
1419 b = discard_const_p(const struct berval *, _invals);
1420 ber_values = ads_dup_values(ctx, b);
1421 } else {
1422 const char **c;
1423 c = discard_const_p(const char *, _invals);
1424 char_values = ads_push_strvals(ctx, c);
1428 /* find the first empty slot */
1429 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1430 curmod++);
1431 if (modlist[curmod] == (LDAPMod *) -1) {
1432 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1433 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1434 return ADS_ERROR(LDAP_NO_MEMORY);
1435 memset(&modlist[curmod], 0,
1436 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1437 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1438 *mods = (ADS_MODLIST)modlist;
1441 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1442 return ADS_ERROR(LDAP_NO_MEMORY);
1443 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1444 if (mod_op & LDAP_MOD_BVALUES) {
1445 modlist[curmod]->mod_bvalues = ber_values;
1446 } else if (mod_op & LDAP_MOD_DELETE) {
1447 modlist[curmod]->mod_values = NULL;
1448 } else {
1449 modlist[curmod]->mod_values = char_values;
1452 modlist[curmod]->mod_op = mod_op;
1453 return ADS_ERROR(LDAP_SUCCESS);
1457 * Add a single string value to a mod list
1458 * @param ctx An initialized TALLOC_CTX
1459 * @param mods An initialized ADS_MODLIST
1460 * @param name The attribute name to add
1461 * @param val The value to add - NULL means DELETE
1462 * @return ADS STATUS indicating success of add
1464 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1465 const char *name, const char *val)
1467 const char *values[2];
1469 values[0] = val;
1470 values[1] = NULL;
1472 if (!val)
1473 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1474 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1478 * Add an array of string values to a mod list
1479 * @param ctx An initialized TALLOC_CTX
1480 * @param mods An initialized ADS_MODLIST
1481 * @param name The attribute name to add
1482 * @param vals The array of string values to add - NULL means DELETE
1483 * @return ADS STATUS indicating success of add
1485 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1486 const char *name, const char **vals)
1488 if (!vals)
1489 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1490 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1491 name, (const void **) vals);
1494 #if 0
1496 * Add a single ber-encoded value to a mod list
1497 * @param ctx An initialized TALLOC_CTX
1498 * @param mods An initialized ADS_MODLIST
1499 * @param name The attribute name to add
1500 * @param val The value to add - NULL means DELETE
1501 * @return ADS STATUS indicating success of add
1503 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1504 const char *name, const struct berval *val)
1506 const struct berval *values[2];
1508 values[0] = val;
1509 values[1] = NULL;
1510 if (!val)
1511 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1512 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1513 name, (const void **) values);
1515 #endif
1517 static void ads_print_error(int ret, LDAP *ld)
1519 if (ret != 0) {
1520 char *ld_error = NULL;
1521 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1522 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1523 ldap_err2string(ret), ld_error));
1524 SAFE_FREE(ld_error);
1529 * Perform an ldap modify
1530 * @param ads connection to ads server
1531 * @param mod_dn DistinguishedName to modify
1532 * @param mods list of modifications to perform
1533 * @return status of modify
1535 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1537 int ret,i;
1538 char *utf8_dn = NULL;
1539 size_t converted_size;
1541 this control is needed to modify that contains a currently
1542 non-existent attribute (but allowable for the object) to run
1544 LDAPControl PermitModify = {
1545 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1546 {0, NULL},
1547 (char) 1};
1548 LDAPControl *controls[2];
1550 controls[0] = &PermitModify;
1551 controls[1] = NULL;
1553 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1554 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1557 /* find the end of the list, marked by NULL or -1 */
1558 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1559 /* make sure the end of the list is NULL */
1560 mods[i] = NULL;
1561 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1562 (LDAPMod **) mods, controls, NULL);
1563 ads_print_error(ret, ads->ldap.ld);
1564 TALLOC_FREE(utf8_dn);
1565 return ADS_ERROR(ret);
1569 * Perform an ldap add
1570 * @param ads connection to ads server
1571 * @param new_dn DistinguishedName to add
1572 * @param mods list of attributes and values for DN
1573 * @return status of add
1575 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1577 int ret, i;
1578 char *utf8_dn = NULL;
1579 size_t converted_size;
1581 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1582 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1583 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1586 /* find the end of the list, marked by NULL or -1 */
1587 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1588 /* make sure the end of the list is NULL */
1589 mods[i] = NULL;
1591 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1592 ads_print_error(ret, ads->ldap.ld);
1593 TALLOC_FREE(utf8_dn);
1594 return ADS_ERROR(ret);
1598 * Delete a DistinguishedName
1599 * @param ads connection to ads server
1600 * @param new_dn DistinguishedName to delete
1601 * @return status of delete
1603 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1605 int ret;
1606 char *utf8_dn = NULL;
1607 size_t converted_size;
1608 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1609 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1610 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1613 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1614 ads_print_error(ret, ads->ldap.ld);
1615 TALLOC_FREE(utf8_dn);
1616 return ADS_ERROR(ret);
1620 * Build an org unit string
1621 * if org unit is Computers or blank then assume a container, otherwise
1622 * assume a / separated list of organisational units.
1623 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1624 * @param ads connection to ads server
1625 * @param org_unit Organizational unit
1626 * @return org unit string - caller must free
1628 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1630 char *ret = NULL;
1632 if (!org_unit || !*org_unit) {
1634 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1636 /* samba4 might not yet respond to a wellknownobject-query */
1637 return ret ? ret : SMB_STRDUP("cn=Computers");
1640 if (strequal(org_unit, "Computers")) {
1641 return SMB_STRDUP("cn=Computers");
1644 /* jmcd: removed "\\" from the separation chars, because it is
1645 needed as an escape for chars like '#' which are valid in an
1646 OU name */
1647 return ads_build_path(org_unit, "/", "ou=", 1);
1651 * Get a org unit string for a well-known GUID
1652 * @param ads connection to ads server
1653 * @param wknguid Well known GUID
1654 * @return org unit string - caller must free
1656 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1658 ADS_STATUS status;
1659 LDAPMessage *res = NULL;
1660 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1661 **bind_dn_exp = NULL;
1662 const char *attrs[] = {"distinguishedName", NULL};
1663 int new_ln, wkn_ln, bind_ln, i;
1665 if (wknguid == NULL) {
1666 return NULL;
1669 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1670 DEBUG(1, ("asprintf failed!\n"));
1671 return NULL;
1674 status = ads_search_dn(ads, &res, base, attrs);
1675 if (!ADS_ERR_OK(status)) {
1676 DEBUG(1,("Failed while searching for: %s\n", base));
1677 goto out;
1680 if (ads_count_replies(ads, res) != 1) {
1681 goto out;
1684 /* substitute the bind-path from the well-known-guid-search result */
1685 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1686 if (!wkn_dn) {
1687 goto out;
1690 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1691 if (!wkn_dn_exp) {
1692 goto out;
1695 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1696 if (!bind_dn_exp) {
1697 goto out;
1700 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1702 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1705 new_ln = wkn_ln - bind_ln;
1707 ret = SMB_STRDUP(wkn_dn_exp[0]);
1708 if (!ret) {
1709 goto out;
1712 for (i=1; i < new_ln; i++) {
1713 char *s = NULL;
1715 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1716 SAFE_FREE(ret);
1717 goto out;
1720 SAFE_FREE(ret);
1721 ret = SMB_STRDUP(s);
1722 free(s);
1723 if (!ret) {
1724 goto out;
1728 out:
1729 SAFE_FREE(base);
1730 ads_msgfree(ads, res);
1731 TALLOC_FREE(wkn_dn);
1732 if (wkn_dn_exp) {
1733 ldap_value_free(wkn_dn_exp);
1735 if (bind_dn_exp) {
1736 ldap_value_free(bind_dn_exp);
1739 return ret;
1743 * Adds (appends) an item to an attribute array, rather then
1744 * replacing the whole list
1745 * @param ctx An initialized TALLOC_CTX
1746 * @param mods An initialized ADS_MODLIST
1747 * @param name name of the ldap attribute to append to
1748 * @param vals an array of values to add
1749 * @return status of addition
1752 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1753 const char *name, const char **vals)
1755 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1756 (const void *) vals);
1760 * Determines the an account's current KVNO via an LDAP lookup
1761 * @param ads An initialized ADS_STRUCT
1762 * @param account_name the NT samaccountname.
1763 * @return the kvno for the account, or -1 in case of a failure.
1766 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1768 LDAPMessage *res = NULL;
1769 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1770 char *filter;
1771 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1772 char *dn_string = NULL;
1773 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1775 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1776 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1777 return kvno;
1779 ret = ads_search(ads, &res, filter, attrs);
1780 SAFE_FREE(filter);
1781 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1782 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1783 ads_msgfree(ads, res);
1784 return kvno;
1787 dn_string = ads_get_dn(ads, talloc_tos(), res);
1788 if (!dn_string) {
1789 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1790 ads_msgfree(ads, res);
1791 return kvno;
1793 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1794 TALLOC_FREE(dn_string);
1796 /* ---------------------------------------------------------
1797 * 0 is returned as a default KVNO from this point on...
1798 * This is done because Windows 2000 does not support key
1799 * version numbers. Chances are that a failure in the next
1800 * step is simply due to Windows 2000 being used for a
1801 * domain controller. */
1802 kvno = 0;
1804 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1805 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1806 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1807 ads_msgfree(ads, res);
1808 return kvno;
1811 /* Success */
1812 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1813 ads_msgfree(ads, res);
1814 return kvno;
1818 * Determines the computer account's current KVNO via an LDAP lookup
1819 * @param ads An initialized ADS_STRUCT
1820 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1821 * @return the kvno for the computer account, or -1 in case of a failure.
1824 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1826 char *computer_account = NULL;
1827 uint32_t kvno = -1;
1829 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1830 return kvno;
1833 kvno = ads_get_kvno(ads, computer_account);
1834 free(computer_account);
1836 return kvno;
1840 * This clears out all registered spn's for a given hostname
1841 * @param ads An initilaized ADS_STRUCT
1842 * @param machine_name the NetBIOS name of the computer.
1843 * @return 0 upon success, non-zero otherwise.
1846 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1848 TALLOC_CTX *ctx;
1849 LDAPMessage *res = NULL;
1850 ADS_MODLIST mods;
1851 const char *servicePrincipalName[1] = {NULL};
1852 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1853 char *dn_string = NULL;
1855 ret = ads_find_machine_acct(ads, &res, machine_name);
1856 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1857 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1859 ads_msgfree(ads, res);
1860 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1863 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1864 ctx = talloc_init("ads_clear_service_principal_names");
1865 if (!ctx) {
1866 ads_msgfree(ads, res);
1867 return ADS_ERROR(LDAP_NO_MEMORY);
1870 if (!(mods = ads_init_mods(ctx))) {
1871 talloc_destroy(ctx);
1872 ads_msgfree(ads, res);
1873 return ADS_ERROR(LDAP_NO_MEMORY);
1875 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1876 if (!ADS_ERR_OK(ret)) {
1877 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1878 ads_msgfree(ads, res);
1879 talloc_destroy(ctx);
1880 return ret;
1882 dn_string = ads_get_dn(ads, talloc_tos(), res);
1883 if (!dn_string) {
1884 talloc_destroy(ctx);
1885 ads_msgfree(ads, res);
1886 return ADS_ERROR(LDAP_NO_MEMORY);
1888 ret = ads_gen_mod(ads, dn_string, mods);
1889 TALLOC_FREE(dn_string);
1890 if (!ADS_ERR_OK(ret)) {
1891 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1892 machine_name));
1893 ads_msgfree(ads, res);
1894 talloc_destroy(ctx);
1895 return ret;
1898 ads_msgfree(ads, res);
1899 talloc_destroy(ctx);
1900 return ret;
1904 * @brief Search for an element in a string array.
1906 * @param[in] el_array The string array to search.
1908 * @param[in] num_el The number of elements in the string array.
1910 * @param[in] el The string to search.
1912 * @return True if found, false if not.
1914 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1916 size_t i;
1918 if (el_array == NULL || num_el == 0 || el == NULL) {
1919 return false;
1922 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1923 int cmp;
1925 cmp = strcasecmp_m(el_array[i], el);
1926 if (cmp == 0) {
1927 return true;
1931 return false;
1935 * @brief This gets the service principal names of an existing computer account.
1937 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1939 * @param[in] ads The ADS context to use.
1941 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1942 * identify the computer account.
1944 * @param[in] spn_array A pointer to store the array for SPNs.
1946 * @param[in] num_spns The number of principals stored in the array.
1948 * @return 0 on success, or a ADS error if a failure occurred.
1950 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1951 ADS_STRUCT *ads,
1952 const char *machine_name,
1953 char ***spn_array,
1954 size_t *num_spns)
1956 ADS_STATUS status;
1957 LDAPMessage *res = NULL;
1958 int count;
1960 status = ads_find_machine_acct(ads,
1961 &res,
1962 machine_name);
1963 if (!ADS_ERR_OK(status)) {
1964 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1965 machine_name));
1966 return status;
1969 count = ads_count_replies(ads, res);
1970 if (count != 1) {
1971 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1972 goto done;
1975 *spn_array = ads_pull_strings(ads,
1976 mem_ctx,
1977 res,
1978 "servicePrincipalName",
1979 num_spns);
1980 if (*spn_array == NULL) {
1981 DEBUG(1, ("Host account for %s does not have service principal "
1982 "names.\n",
1983 machine_name));
1984 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1985 goto done;
1988 done:
1989 ads_msgfree(ads, res);
1991 return status;
1995 * This adds a service principal name to an existing computer account
1996 * (found by hostname) in AD.
1997 * @param ads An initialized ADS_STRUCT
1998 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1999 * @param my_fqdn The fully qualified DNS name of the machine
2000 * @param spn A string of the service principal to add, i.e. 'host'
2001 * @return 0 upon sucess, or non-zero if a failure occurs
2004 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2005 const char *my_fqdn, const char *spn)
2007 ADS_STATUS ret;
2008 TALLOC_CTX *ctx;
2009 LDAPMessage *res = NULL;
2010 char *psp1, *psp2;
2011 ADS_MODLIST mods;
2012 char *dn_string = NULL;
2013 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2015 ret = ads_find_machine_acct(ads, &res, machine_name);
2016 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2017 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2018 machine_name));
2019 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2020 spn, machine_name, ads->config.realm));
2021 ads_msgfree(ads, res);
2022 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2025 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2026 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2027 ads_msgfree(ads, res);
2028 return ADS_ERROR(LDAP_NO_MEMORY);
2031 /* add short name spn */
2033 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2034 talloc_destroy(ctx);
2035 ads_msgfree(ads, res);
2036 return ADS_ERROR(LDAP_NO_MEMORY);
2038 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2039 ret = ADS_ERROR(LDAP_NO_MEMORY);
2040 goto out;
2042 servicePrincipalName[0] = psp1;
2044 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2045 psp1, machine_name));
2048 /* add fully qualified spn */
2050 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2051 ret = ADS_ERROR(LDAP_NO_MEMORY);
2052 goto out;
2054 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2055 ret = ADS_ERROR(LDAP_NO_MEMORY);
2056 goto out;
2058 servicePrincipalName[1] = psp2;
2060 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2061 psp2, machine_name));
2063 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2064 ret = ADS_ERROR(LDAP_NO_MEMORY);
2065 goto out;
2068 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2069 if (!ADS_ERR_OK(ret)) {
2070 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2071 goto out;
2074 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2075 ret = ADS_ERROR(LDAP_NO_MEMORY);
2076 goto out;
2079 ret = ads_gen_mod(ads, dn_string, mods);
2080 if (!ADS_ERR_OK(ret)) {
2081 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2082 goto out;
2085 out:
2086 TALLOC_FREE( ctx );
2087 ads_msgfree(ads, res);
2088 return ret;
2092 * adds a machine account to the ADS server
2093 * @param ads An intialized ADS_STRUCT
2094 * @param machine_name - the NetBIOS machine name of this account.
2095 * @param account_type A number indicating the type of account to create
2096 * @param org_unit The LDAP path in which to place this account
2097 * @return 0 upon success, or non-zero otherwise
2100 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2101 const char *machine_name,
2102 const char *org_unit,
2103 uint32_t etype_list)
2105 ADS_STATUS ret;
2106 char *samAccountName, *controlstr;
2107 TALLOC_CTX *ctx;
2108 ADS_MODLIST mods;
2109 char *machine_escaped = NULL;
2110 char *new_dn;
2111 const char *objectClass[] = {"top", "person", "organizationalPerson",
2112 "user", "computer", NULL};
2113 LDAPMessage *res = NULL;
2114 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2115 UF_DONT_EXPIRE_PASSWD |\
2116 UF_ACCOUNTDISABLE );
2117 uint32_t func_level = 0;
2119 ret = ads_domain_func_level(ads, &func_level);
2120 if (!ADS_ERR_OK(ret)) {
2121 return ret;
2124 if (!(ctx = talloc_init("ads_add_machine_acct")))
2125 return ADS_ERROR(LDAP_NO_MEMORY);
2127 ret = ADS_ERROR(LDAP_NO_MEMORY);
2129 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2130 if (!machine_escaped) {
2131 goto done;
2134 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2135 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2137 if ( !new_dn || !samAccountName ) {
2138 goto done;
2141 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2142 goto done;
2145 if (!(mods = ads_init_mods(ctx))) {
2146 goto done;
2149 ads_mod_str(ctx, &mods, "cn", machine_name);
2150 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2151 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2152 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2154 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2155 const char *etype_list_str;
2157 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2158 if (etype_list_str == NULL) {
2159 goto done;
2161 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2162 etype_list_str);
2165 ret = ads_gen_add(ads, new_dn, mods);
2167 done:
2168 SAFE_FREE(machine_escaped);
2169 ads_msgfree(ads, res);
2170 talloc_destroy(ctx);
2172 return ret;
2176 * move a machine account to another OU on the ADS server
2177 * @param ads - An intialized ADS_STRUCT
2178 * @param machine_name - the NetBIOS machine name of this account.
2179 * @param org_unit - The LDAP path in which to place this account
2180 * @param moved - whether we moved the machine account (optional)
2181 * @return 0 upon success, or non-zero otherwise
2184 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2185 const char *org_unit, bool *moved)
2187 ADS_STATUS rc;
2188 int ldap_status;
2189 LDAPMessage *res = NULL;
2190 char *filter = NULL;
2191 char *computer_dn = NULL;
2192 char *parent_dn;
2193 char *computer_rdn = NULL;
2194 bool need_move = False;
2196 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2197 rc = ADS_ERROR(LDAP_NO_MEMORY);
2198 goto done;
2201 /* Find pre-existing machine */
2202 rc = ads_search(ads, &res, filter, NULL);
2203 if (!ADS_ERR_OK(rc)) {
2204 goto done;
2207 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2208 if (!computer_dn) {
2209 rc = ADS_ERROR(LDAP_NO_MEMORY);
2210 goto done;
2213 parent_dn = ads_parent_dn(computer_dn);
2214 if (strequal(parent_dn, org_unit)) {
2215 goto done;
2218 need_move = True;
2220 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2221 rc = ADS_ERROR(LDAP_NO_MEMORY);
2222 goto done;
2225 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2226 org_unit, 1, NULL, NULL);
2227 rc = ADS_ERROR(ldap_status);
2229 done:
2230 ads_msgfree(ads, res);
2231 SAFE_FREE(filter);
2232 TALLOC_FREE(computer_dn);
2233 SAFE_FREE(computer_rdn);
2235 if (!ADS_ERR_OK(rc)) {
2236 need_move = False;
2239 if (moved) {
2240 *moved = need_move;
2243 return rc;
2247 dump a binary result from ldap
2249 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2251 int i, j;
2252 for (i=0; values[i]; i++) {
2253 printf("%s: ", field);
2254 for (j=0; j<values[i]->bv_len; j++) {
2255 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2257 printf("\n");
2261 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2263 int i;
2264 for (i=0; values[i]; i++) {
2265 NTSTATUS status;
2266 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2267 struct GUID guid;
2269 status = GUID_from_ndr_blob(&in, &guid);
2270 if (NT_STATUS_IS_OK(status)) {
2271 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2272 } else {
2273 printf("%s: INVALID GUID\n", field);
2279 dump a sid result from ldap
2281 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2283 int i;
2284 for (i=0; values[i]; i++) {
2285 struct dom_sid sid;
2286 fstring tmp;
2287 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2288 values[i]->bv_len, &sid)) {
2289 return;
2291 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2296 dump ntSecurityDescriptor
2298 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2300 TALLOC_CTX *frame = talloc_stackframe();
2301 struct security_descriptor *psd;
2302 NTSTATUS status;
2304 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2305 values[0]->bv_len, &psd);
2306 if (!NT_STATUS_IS_OK(status)) {
2307 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2308 nt_errstr(status)));
2309 TALLOC_FREE(frame);
2310 return;
2313 if (psd) {
2314 ads_disp_sd(ads, talloc_tos(), psd);
2317 TALLOC_FREE(frame);
2321 dump a string result from ldap
2323 static void dump_string(const char *field, char **values)
2325 int i;
2326 for (i=0; values[i]; i++) {
2327 printf("%s: %s\n", field, values[i]);
2332 dump a field from LDAP on stdout
2333 used for debugging
2336 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2338 const struct {
2339 const char *name;
2340 bool string;
2341 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2342 } handlers[] = {
2343 {"objectGUID", False, dump_guid},
2344 {"netbootGUID", False, dump_guid},
2345 {"nTSecurityDescriptor", False, dump_sd},
2346 {"dnsRecord", False, dump_binary},
2347 {"objectSid", False, dump_sid},
2348 {"tokenGroups", False, dump_sid},
2349 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2350 {"tokengroupsGlobalandUniversal", False, dump_sid},
2351 {"mS-DS-CreatorSID", False, dump_sid},
2352 {"msExchMailboxGuid", False, dump_guid},
2353 {NULL, True, NULL}
2355 int i;
2357 if (!field) { /* must be end of an entry */
2358 printf("\n");
2359 return False;
2362 for (i=0; handlers[i].name; i++) {
2363 if (strcasecmp_m(handlers[i].name, field) == 0) {
2364 if (!values) /* first time, indicate string or not */
2365 return handlers[i].string;
2366 handlers[i].handler(ads, field, (struct berval **) values);
2367 break;
2370 if (!handlers[i].name) {
2371 if (!values) /* first time, indicate string conversion */
2372 return True;
2373 dump_string(field, (char **)values);
2375 return False;
2379 * Dump a result from LDAP on stdout
2380 * used for debugging
2381 * @param ads connection to ads server
2382 * @param res Results to dump
2385 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2387 ads_process_results(ads, res, ads_dump_field, NULL);
2391 * Walk through results, calling a function for each entry found.
2392 * The function receives a field name, a berval * array of values,
2393 * and a data area passed through from the start. The function is
2394 * called once with null for field and values at the end of each
2395 * entry.
2396 * @param ads connection to ads server
2397 * @param res Results to process
2398 * @param fn Function for processing each result
2399 * @param data_area user-defined area to pass to function
2401 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2402 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2403 void *data_area)
2405 LDAPMessage *msg;
2406 TALLOC_CTX *ctx;
2407 size_t converted_size;
2409 if (!(ctx = talloc_init("ads_process_results")))
2410 return;
2412 for (msg = ads_first_entry(ads, res); msg;
2413 msg = ads_next_entry(ads, msg)) {
2414 char *utf8_field;
2415 BerElement *b;
2417 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2418 (LDAPMessage *)msg,&b);
2419 utf8_field;
2420 utf8_field=ldap_next_attribute(ads->ldap.ld,
2421 (LDAPMessage *)msg,b)) {
2422 struct berval **ber_vals;
2423 char **str_vals;
2424 char **utf8_vals;
2425 char *field;
2426 bool string;
2428 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2429 &converted_size))
2431 DEBUG(0,("ads_process_results: "
2432 "pull_utf8_talloc failed: %s",
2433 strerror(errno)));
2436 string = fn(ads, field, NULL, data_area);
2438 if (string) {
2439 const char **p;
2441 utf8_vals = ldap_get_values(ads->ldap.ld,
2442 (LDAPMessage *)msg, field);
2443 p = discard_const_p(const char *, utf8_vals);
2444 str_vals = ads_pull_strvals(ctx, p);
2445 fn(ads, field, (void **) str_vals, data_area);
2446 ldap_value_free(utf8_vals);
2447 } else {
2448 ber_vals = ldap_get_values_len(ads->ldap.ld,
2449 (LDAPMessage *)msg, field);
2450 fn(ads, field, (void **) ber_vals, data_area);
2452 ldap_value_free_len(ber_vals);
2454 ldap_memfree(utf8_field);
2456 ber_free(b, 0);
2457 talloc_free_children(ctx);
2458 fn(ads, NULL, NULL, data_area); /* completed an entry */
2461 talloc_destroy(ctx);
2465 * count how many replies are in a LDAPMessage
2466 * @param ads connection to ads server
2467 * @param res Results to count
2468 * @return number of replies
2470 int ads_count_replies(ADS_STRUCT *ads, void *res)
2472 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2476 * pull the first entry from a ADS result
2477 * @param ads connection to ads server
2478 * @param res Results of search
2479 * @return first entry from result
2481 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2483 return ldap_first_entry(ads->ldap.ld, res);
2487 * pull the next entry from a ADS result
2488 * @param ads connection to ads server
2489 * @param res Results of search
2490 * @return next entry from result
2492 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2494 return ldap_next_entry(ads->ldap.ld, res);
2498 * pull the first message from a ADS result
2499 * @param ads connection to ads server
2500 * @param res Results of search
2501 * @return first message from result
2503 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2505 return ldap_first_message(ads->ldap.ld, res);
2509 * pull the next message from a ADS result
2510 * @param ads connection to ads server
2511 * @param res Results of search
2512 * @return next message from result
2514 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2516 return ldap_next_message(ads->ldap.ld, res);
2520 * pull a single string from a ADS result
2521 * @param ads connection to ads server
2522 * @param mem_ctx TALLOC_CTX to use for allocating result string
2523 * @param msg Results of search
2524 * @param field Attribute to retrieve
2525 * @return Result string in talloc context
2527 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2528 const char *field)
2530 char **values;
2531 char *ret = NULL;
2532 char *ux_string;
2533 size_t converted_size;
2535 values = ldap_get_values(ads->ldap.ld, msg, field);
2536 if (!values)
2537 return NULL;
2539 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2540 &converted_size))
2542 ret = ux_string;
2544 ldap_value_free(values);
2545 return ret;
2549 * pull an array of strings from a ADS result
2550 * @param ads connection to ads server
2551 * @param mem_ctx TALLOC_CTX to use for allocating result string
2552 * @param msg Results of search
2553 * @param field Attribute to retrieve
2554 * @return Result strings in talloc context
2556 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2557 LDAPMessage *msg, const char *field,
2558 size_t *num_values)
2560 char **values;
2561 char **ret = NULL;
2562 int i;
2563 size_t converted_size;
2565 values = ldap_get_values(ads->ldap.ld, msg, field);
2566 if (!values)
2567 return NULL;
2569 *num_values = ldap_count_values(values);
2571 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2572 if (!ret) {
2573 ldap_value_free(values);
2574 return NULL;
2577 for (i=0;i<*num_values;i++) {
2578 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2579 &converted_size))
2581 ldap_value_free(values);
2582 return NULL;
2585 ret[i] = NULL;
2587 ldap_value_free(values);
2588 return ret;
2592 * pull an array of strings from a ADS result
2593 * (handle large multivalue attributes with range retrieval)
2594 * @param ads connection to ads server
2595 * @param mem_ctx TALLOC_CTX to use for allocating result string
2596 * @param msg Results of search
2597 * @param field Attribute to retrieve
2598 * @param current_strings strings returned by a previous call to this function
2599 * @param next_attribute The next query should ask for this attribute
2600 * @param num_values How many values did we get this time?
2601 * @param more_values Are there more values to get?
2602 * @return Result strings in talloc context
2604 char **ads_pull_strings_range(ADS_STRUCT *ads,
2605 TALLOC_CTX *mem_ctx,
2606 LDAPMessage *msg, const char *field,
2607 char **current_strings,
2608 const char **next_attribute,
2609 size_t *num_strings,
2610 bool *more_strings)
2612 char *attr;
2613 char *expected_range_attrib, *range_attr;
2614 BerElement *ptr = NULL;
2615 char **strings;
2616 char **new_strings;
2617 size_t num_new_strings;
2618 unsigned long int range_start;
2619 unsigned long int range_end;
2621 /* we might have been given the whole lot anyway */
2622 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2623 *more_strings = False;
2624 return strings;
2627 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2629 /* look for Range result */
2630 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2631 attr;
2632 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2633 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2634 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2635 range_attr = attr;
2636 break;
2638 ldap_memfree(attr);
2640 if (!attr) {
2641 ber_free(ptr, 0);
2642 /* nothing here - this field is just empty */
2643 *more_strings = False;
2644 return NULL;
2647 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2648 &range_start, &range_end) == 2) {
2649 *more_strings = True;
2650 } else {
2651 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2652 &range_start) == 1) {
2653 *more_strings = False;
2654 } else {
2655 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2656 range_attr));
2657 ldap_memfree(range_attr);
2658 *more_strings = False;
2659 return NULL;
2663 if ((*num_strings) != range_start) {
2664 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2665 " - aborting range retreival\n",
2666 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2667 ldap_memfree(range_attr);
2668 *more_strings = False;
2669 return NULL;
2672 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2674 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2675 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2676 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2677 range_attr, (unsigned long int)range_end - range_start + 1,
2678 (unsigned long int)num_new_strings));
2679 ldap_memfree(range_attr);
2680 *more_strings = False;
2681 return NULL;
2684 strings = talloc_realloc(mem_ctx, current_strings, char *,
2685 *num_strings + num_new_strings);
2687 if (strings == NULL) {
2688 ldap_memfree(range_attr);
2689 *more_strings = False;
2690 return NULL;
2693 if (new_strings && num_new_strings) {
2694 memcpy(&strings[*num_strings], new_strings,
2695 sizeof(*new_strings) * num_new_strings);
2698 (*num_strings) += num_new_strings;
2700 if (*more_strings) {
2701 *next_attribute = talloc_asprintf(mem_ctx,
2702 "%s;range=%d-*",
2703 field,
2704 (int)*num_strings);
2706 if (!*next_attribute) {
2707 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2708 ldap_memfree(range_attr);
2709 *more_strings = False;
2710 return NULL;
2714 ldap_memfree(range_attr);
2716 return strings;
2720 * pull a single uint32_t from a ADS result
2721 * @param ads connection to ads server
2722 * @param msg Results of search
2723 * @param field Attribute to retrieve
2724 * @param v Pointer to int to store result
2725 * @return boolean inidicating success
2727 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2728 uint32_t *v)
2730 char **values;
2732 values = ldap_get_values(ads->ldap.ld, msg, field);
2733 if (!values)
2734 return False;
2735 if (!values[0]) {
2736 ldap_value_free(values);
2737 return False;
2740 *v = atoi(values[0]);
2741 ldap_value_free(values);
2742 return True;
2746 * pull a single objectGUID from an ADS result
2747 * @param ads connection to ADS server
2748 * @param msg results of search
2749 * @param guid 37-byte area to receive text guid
2750 * @return boolean indicating success
2752 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2754 DATA_BLOB blob;
2755 NTSTATUS status;
2757 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2758 &blob)) {
2759 return false;
2762 status = GUID_from_ndr_blob(&blob, guid);
2763 talloc_free(blob.data);
2764 return NT_STATUS_IS_OK(status);
2769 * pull a single struct dom_sid from a ADS result
2770 * @param ads connection to ads server
2771 * @param msg Results of search
2772 * @param field Attribute to retrieve
2773 * @param sid Pointer to sid to store result
2774 * @return boolean inidicating success
2776 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2777 struct dom_sid *sid)
2779 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2783 * pull an array of struct dom_sids from a ADS result
2784 * @param ads connection to ads server
2785 * @param mem_ctx TALLOC_CTX for allocating sid array
2786 * @param msg Results of search
2787 * @param field Attribute to retrieve
2788 * @param sids pointer to sid array to allocate
2789 * @return the count of SIDs pulled
2791 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2792 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2794 struct berval **values;
2795 bool ret;
2796 int count, i;
2798 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2800 if (!values)
2801 return 0;
2803 for (i=0; values[i]; i++)
2804 /* nop */ ;
2806 if (i) {
2807 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2808 if (!(*sids)) {
2809 ldap_value_free_len(values);
2810 return 0;
2812 } else {
2813 (*sids) = NULL;
2816 count = 0;
2817 for (i=0; values[i]; i++) {
2818 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2819 values[i]->bv_len, &(*sids)[count]);
2820 if (ret) {
2821 DEBUG(10, ("pulling SID: %s\n",
2822 sid_string_dbg(&(*sids)[count])));
2823 count++;
2827 ldap_value_free_len(values);
2828 return count;
2832 * pull a struct security_descriptor from a ADS result
2833 * @param ads connection to ads server
2834 * @param mem_ctx TALLOC_CTX for allocating sid array
2835 * @param msg Results of search
2836 * @param field Attribute to retrieve
2837 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2838 * @return boolean inidicating success
2840 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2841 LDAPMessage *msg, const char *field,
2842 struct security_descriptor **sd)
2844 struct berval **values;
2845 bool ret = true;
2847 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2849 if (!values) return false;
2851 if (values[0]) {
2852 NTSTATUS status;
2853 status = unmarshall_sec_desc(mem_ctx,
2854 (uint8_t *)values[0]->bv_val,
2855 values[0]->bv_len, sd);
2856 if (!NT_STATUS_IS_OK(status)) {
2857 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2858 nt_errstr(status)));
2859 ret = false;
2863 ldap_value_free_len(values);
2864 return ret;
2868 * in order to support usernames longer than 21 characters we need to
2869 * use both the sAMAccountName and the userPrincipalName attributes
2870 * It seems that not all users have the userPrincipalName attribute set
2872 * @param ads connection to ads server
2873 * @param mem_ctx TALLOC_CTX for allocating sid array
2874 * @param msg Results of search
2875 * @return the username
2877 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2878 LDAPMessage *msg)
2880 #if 0 /* JERRY */
2881 char *ret, *p;
2883 /* lookup_name() only works on the sAMAccountName to
2884 returning the username portion of userPrincipalName
2885 breaks winbindd_getpwnam() */
2887 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2888 if (ret && (p = strchr_m(ret, '@'))) {
2889 *p = 0;
2890 return ret;
2892 #endif
2893 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2898 * find the update serial number - this is the core of the ldap cache
2899 * @param ads connection to ads server
2900 * @param ads connection to ADS server
2901 * @param usn Pointer to retrieved update serial number
2902 * @return status of search
2904 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2906 const char *attrs[] = {"highestCommittedUSN", NULL};
2907 ADS_STATUS status;
2908 LDAPMessage *res;
2910 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2911 if (!ADS_ERR_OK(status))
2912 return status;
2914 if (ads_count_replies(ads, res) != 1) {
2915 ads_msgfree(ads, res);
2916 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2919 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2920 ads_msgfree(ads, res);
2921 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2924 ads_msgfree(ads, res);
2925 return ADS_SUCCESS;
2928 /* parse a ADS timestring - typical string is
2929 '20020917091222.0Z0' which means 09:12.22 17th September
2930 2002, timezone 0 */
2931 static time_t ads_parse_time(const char *str)
2933 struct tm tm;
2935 ZERO_STRUCT(tm);
2937 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2938 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2939 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2940 return 0;
2942 tm.tm_year -= 1900;
2943 tm.tm_mon -= 1;
2945 return timegm(&tm);
2948 /********************************************************************
2949 ********************************************************************/
2951 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2953 const char *attrs[] = {"currentTime", NULL};
2954 ADS_STATUS status;
2955 LDAPMessage *res;
2956 char *timestr;
2957 TALLOC_CTX *ctx;
2958 ADS_STRUCT *ads_s = ads;
2960 if (!(ctx = talloc_init("ads_current_time"))) {
2961 return ADS_ERROR(LDAP_NO_MEMORY);
2964 /* establish a new ldap tcp session if necessary */
2966 if ( !ads->ldap.ld ) {
2967 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2968 ads->server.ldap_server )) == NULL )
2970 status = ADS_ERROR(LDAP_NO_MEMORY);
2971 goto done;
2973 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2974 status = ads_connect( ads_s );
2975 if ( !ADS_ERR_OK(status))
2976 goto done;
2979 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2980 if (!ADS_ERR_OK(status)) {
2981 goto done;
2984 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2985 if (!timestr) {
2986 ads_msgfree(ads_s, res);
2987 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2988 goto done;
2991 /* but save the time and offset in the original ADS_STRUCT */
2993 ads->config.current_time = ads_parse_time(timestr);
2995 if (ads->config.current_time != 0) {
2996 ads->auth.time_offset = ads->config.current_time - time(NULL);
2997 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3000 ads_msgfree(ads, res);
3002 status = ADS_SUCCESS;
3004 done:
3005 /* free any temporary ads connections */
3006 if ( ads_s != ads ) {
3007 ads_destroy( &ads_s );
3009 talloc_destroy(ctx);
3011 return status;
3014 /********************************************************************
3015 ********************************************************************/
3017 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3019 const char *attrs[] = {"domainFunctionality", NULL};
3020 ADS_STATUS status;
3021 LDAPMessage *res;
3022 ADS_STRUCT *ads_s = ads;
3024 *val = DS_DOMAIN_FUNCTION_2000;
3026 /* establish a new ldap tcp session if necessary */
3028 if ( !ads->ldap.ld ) {
3029 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3030 ads->server.ldap_server )) == NULL )
3032 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3033 goto done;
3035 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3036 status = ads_connect( ads_s );
3037 if ( !ADS_ERR_OK(status))
3038 goto done;
3041 /* If the attribute does not exist assume it is a Windows 2000
3042 functional domain */
3044 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3045 if (!ADS_ERR_OK(status)) {
3046 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3047 status = ADS_SUCCESS;
3049 goto done;
3052 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3053 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3055 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3058 ads_msgfree(ads, res);
3060 done:
3061 /* free any temporary ads connections */
3062 if ( ads_s != ads ) {
3063 ads_destroy( &ads_s );
3066 return status;
3070 * find the domain sid for our domain
3071 * @param ads connection to ads server
3072 * @param sid Pointer to domain sid
3073 * @return status of search
3075 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3077 const char *attrs[] = {"objectSid", NULL};
3078 LDAPMessage *res;
3079 ADS_STATUS rc;
3081 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3082 attrs, &res);
3083 if (!ADS_ERR_OK(rc)) return rc;
3084 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3085 ads_msgfree(ads, res);
3086 return ADS_ERROR_SYSTEM(ENOENT);
3088 ads_msgfree(ads, res);
3090 return ADS_SUCCESS;
3094 * find our site name
3095 * @param ads connection to ads server
3096 * @param mem_ctx Pointer to talloc context
3097 * @param site_name Pointer to the sitename
3098 * @return status of search
3100 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3102 ADS_STATUS status;
3103 LDAPMessage *res;
3104 const char *dn, *service_name;
3105 const char *attrs[] = { "dsServiceName", NULL };
3107 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3108 if (!ADS_ERR_OK(status)) {
3109 return status;
3112 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3113 if (service_name == NULL) {
3114 ads_msgfree(ads, res);
3115 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3118 ads_msgfree(ads, res);
3120 /* go up three levels */
3121 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3122 if (dn == NULL) {
3123 return ADS_ERROR(LDAP_NO_MEMORY);
3126 *site_name = talloc_strdup(mem_ctx, dn);
3127 if (*site_name == NULL) {
3128 return ADS_ERROR(LDAP_NO_MEMORY);
3131 return status;
3133 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3138 * find the site dn where a machine resides
3139 * @param ads connection to ads server
3140 * @param mem_ctx Pointer to talloc context
3141 * @param computer_name name of the machine
3142 * @param site_name Pointer to the sitename
3143 * @return status of search
3145 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3147 ADS_STATUS status;
3148 LDAPMessage *res;
3149 const char *parent, *filter;
3150 char *config_context = NULL;
3151 char *dn;
3153 /* shortcut a query */
3154 if (strequal(computer_name, ads->config.ldap_server_name)) {
3155 return ads_site_dn(ads, mem_ctx, site_dn);
3158 status = ads_config_path(ads, mem_ctx, &config_context);
3159 if (!ADS_ERR_OK(status)) {
3160 return status;
3163 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3164 if (filter == NULL) {
3165 return ADS_ERROR(LDAP_NO_MEMORY);
3168 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3169 filter, NULL, &res);
3170 if (!ADS_ERR_OK(status)) {
3171 return status;
3174 if (ads_count_replies(ads, res) != 1) {
3175 ads_msgfree(ads, res);
3176 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3179 dn = ads_get_dn(ads, mem_ctx, res);
3180 if (dn == NULL) {
3181 ads_msgfree(ads, res);
3182 return ADS_ERROR(LDAP_NO_MEMORY);
3185 /* go up three levels */
3186 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3187 if (parent == NULL) {
3188 ads_msgfree(ads, res);
3189 TALLOC_FREE(dn);
3190 return ADS_ERROR(LDAP_NO_MEMORY);
3193 *site_dn = talloc_strdup(mem_ctx, parent);
3194 if (*site_dn == NULL) {
3195 ads_msgfree(ads, res);
3196 TALLOC_FREE(dn);
3197 return ADS_ERROR(LDAP_NO_MEMORY);
3200 TALLOC_FREE(dn);
3201 ads_msgfree(ads, res);
3203 return status;
3207 * get the upn suffixes for a domain
3208 * @param ads connection to ads server
3209 * @param mem_ctx Pointer to talloc context
3210 * @param suffixes Pointer to an array of suffixes
3211 * @param num_suffixes Pointer to the number of suffixes
3212 * @return status of search
3214 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3216 ADS_STATUS status;
3217 LDAPMessage *res;
3218 const char *base;
3219 char *config_context = NULL;
3220 const char *attrs[] = { "uPNSuffixes", NULL };
3222 status = ads_config_path(ads, mem_ctx, &config_context);
3223 if (!ADS_ERR_OK(status)) {
3224 return status;
3227 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3228 if (base == NULL) {
3229 return ADS_ERROR(LDAP_NO_MEMORY);
3232 status = ads_search_dn(ads, &res, base, attrs);
3233 if (!ADS_ERR_OK(status)) {
3234 return status;
3237 if (ads_count_replies(ads, res) != 1) {
3238 ads_msgfree(ads, res);
3239 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3242 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3243 if ((*suffixes) == NULL) {
3244 ads_msgfree(ads, res);
3245 return ADS_ERROR(LDAP_NO_MEMORY);
3248 ads_msgfree(ads, res);
3250 return status;
3254 * get the joinable ous for a domain
3255 * @param ads connection to ads server
3256 * @param mem_ctx Pointer to talloc context
3257 * @param ous Pointer to an array of ous
3258 * @param num_ous Pointer to the number of ous
3259 * @return status of search
3261 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3262 TALLOC_CTX *mem_ctx,
3263 char ***ous,
3264 size_t *num_ous)
3266 ADS_STATUS status;
3267 LDAPMessage *res = NULL;
3268 LDAPMessage *msg = NULL;
3269 const char *attrs[] = { "dn", NULL };
3270 int count = 0;
3272 status = ads_search(ads, &res,
3273 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3274 attrs);
3275 if (!ADS_ERR_OK(status)) {
3276 return status;
3279 count = ads_count_replies(ads, res);
3280 if (count < 1) {
3281 ads_msgfree(ads, res);
3282 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3285 for (msg = ads_first_entry(ads, res); msg;
3286 msg = ads_next_entry(ads, msg)) {
3287 const char **p = discard_const_p(const char *, *ous);
3288 char *dn = NULL;
3290 dn = ads_get_dn(ads, talloc_tos(), msg);
3291 if (!dn) {
3292 ads_msgfree(ads, res);
3293 return ADS_ERROR(LDAP_NO_MEMORY);
3296 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3297 TALLOC_FREE(dn);
3298 ads_msgfree(ads, res);
3299 return ADS_ERROR(LDAP_NO_MEMORY);
3302 TALLOC_FREE(dn);
3303 *ous = discard_const_p(char *, p);
3306 ads_msgfree(ads, res);
3308 return status;
3313 * pull a struct dom_sid from an extended dn string
3314 * @param mem_ctx TALLOC_CTX
3315 * @param extended_dn string
3316 * @param flags string type of extended_dn
3317 * @param sid pointer to a struct dom_sid
3318 * @return NT_STATUS_OK on success,
3319 * NT_INVALID_PARAMETER on error,
3320 * NT_STATUS_NOT_FOUND if no SID present
3322 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3323 const char *extended_dn,
3324 enum ads_extended_dn_flags flags,
3325 struct dom_sid *sid)
3327 char *p, *q, *dn;
3329 if (!extended_dn) {
3330 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3333 /* otherwise extended_dn gets stripped off */
3334 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3335 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3338 * ADS_EXTENDED_DN_HEX_STRING:
3339 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3341 * ADS_EXTENDED_DN_STRING (only with w2k3):
3342 * <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
3344 * Object with no SID, such as an Exchange Public Folder
3345 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3348 p = strchr(dn, ';');
3349 if (!p) {
3350 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3353 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3354 DEBUG(5,("No SID present in extended dn\n"));
3355 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3358 p += strlen(";<SID=");
3360 q = strchr(p, '>');
3361 if (!q) {
3362 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3365 *q = '\0';
3367 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3369 switch (flags) {
3371 case ADS_EXTENDED_DN_STRING:
3372 if (!string_to_sid(sid, p)) {
3373 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3375 break;
3376 case ADS_EXTENDED_DN_HEX_STRING: {
3377 fstring buf;
3378 size_t buf_len;
3380 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3381 if (buf_len == 0) {
3382 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3385 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3386 DEBUG(10,("failed to parse sid\n"));
3387 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3389 break;
3391 default:
3392 DEBUG(10,("unknown extended dn format\n"));
3393 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3396 return ADS_ERROR_NT(NT_STATUS_OK);
3399 /********************************************************************
3400 ********************************************************************/
3402 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3404 LDAPMessage *res = NULL;
3405 ADS_STATUS status;
3406 int count = 0;
3407 char *name = NULL;
3409 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3410 if (!ADS_ERR_OK(status)) {
3411 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3412 lp_netbios_name()));
3413 goto out;
3416 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3417 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3418 goto out;
3421 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3422 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3425 out:
3426 ads_msgfree(ads, res);
3428 return name;
3431 /********************************************************************
3432 ********************************************************************/
3434 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3436 LDAPMessage *res = NULL;
3437 ADS_STATUS status;
3438 int count = 0;
3439 char *name = NULL;
3441 status = ads_find_machine_acct(ads, &res, machine_name);
3442 if (!ADS_ERR_OK(status)) {
3443 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3444 lp_netbios_name()));
3445 goto out;
3448 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3449 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3450 goto out;
3453 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3454 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3457 out:
3458 ads_msgfree(ads, res);
3460 return name;
3463 /********************************************************************
3464 ********************************************************************/
3466 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3468 LDAPMessage *res = NULL;
3469 ADS_STATUS status;
3470 int count = 0;
3471 char *name = NULL;
3473 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3474 if (!ADS_ERR_OK(status)) {
3475 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3476 lp_netbios_name()));
3477 goto out;
3480 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3481 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3482 goto out;
3485 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3486 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3489 out:
3490 ads_msgfree(ads, res);
3492 return name;
3495 #if 0
3497 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3500 * Join a machine to a realm
3501 * Creates the machine account and sets the machine password
3502 * @param ads connection to ads server
3503 * @param machine name of host to add
3504 * @param org_unit Organizational unit to place machine in
3505 * @return status of join
3507 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3508 uint32_t account_type, const char *org_unit)
3510 ADS_STATUS status;
3511 LDAPMessage *res = NULL;
3512 char *machine;
3514 /* machine name must be lowercase */
3515 machine = SMB_STRDUP(machine_name);
3516 strlower_m(machine);
3519 status = ads_find_machine_acct(ads, (void **)&res, machine);
3520 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3521 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3522 status = ads_leave_realm(ads, machine);
3523 if (!ADS_ERR_OK(status)) {
3524 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3525 machine, ads->config.realm));
3526 return status;
3530 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3531 if (!ADS_ERR_OK(status)) {
3532 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3533 SAFE_FREE(machine);
3534 return status;
3537 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3538 if (!ADS_ERR_OK(status)) {
3539 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3540 SAFE_FREE(machine);
3541 return status;
3544 SAFE_FREE(machine);
3545 ads_msgfree(ads, res);
3547 return status;
3549 #endif
3552 * Delete a machine from the realm
3553 * @param ads connection to ads server
3554 * @param hostname Machine to remove
3555 * @return status of delete
3557 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3559 ADS_STATUS status;
3560 void *msg;
3561 LDAPMessage *res;
3562 char *hostnameDN, *host;
3563 int rc;
3564 LDAPControl ldap_control;
3565 LDAPControl * pldap_control[2] = {NULL, NULL};
3567 pldap_control[0] = &ldap_control;
3568 memset(&ldap_control, 0, sizeof(LDAPControl));
3569 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3571 /* hostname must be lowercase */
3572 host = SMB_STRDUP(hostname);
3573 if (!strlower_m(host)) {
3574 SAFE_FREE(host);
3575 return ADS_ERROR_SYSTEM(EINVAL);
3578 status = ads_find_machine_acct(ads, &res, host);
3579 if (!ADS_ERR_OK(status)) {
3580 DEBUG(0, ("Host account for %s does not exist.\n", host));
3581 SAFE_FREE(host);
3582 return status;
3585 msg = ads_first_entry(ads, res);
3586 if (!msg) {
3587 SAFE_FREE(host);
3588 return ADS_ERROR_SYSTEM(ENOENT);
3591 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3592 if (hostnameDN == NULL) {
3593 SAFE_FREE(host);
3594 return ADS_ERROR_SYSTEM(ENOENT);
3597 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3598 if (rc) {
3599 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3600 }else {
3601 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3604 if (rc != LDAP_SUCCESS) {
3605 const char *attrs[] = { "cn", NULL };
3606 LDAPMessage *msg_sub;
3608 /* we only search with scope ONE, we do not expect any further
3609 * objects to be created deeper */
3611 status = ads_do_search_retry(ads, hostnameDN,
3612 LDAP_SCOPE_ONELEVEL,
3613 "(objectclass=*)", attrs, &res);
3615 if (!ADS_ERR_OK(status)) {
3616 SAFE_FREE(host);
3617 TALLOC_FREE(hostnameDN);
3618 return status;
3621 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3622 msg_sub = ads_next_entry(ads, msg_sub)) {
3624 char *dn = NULL;
3626 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3627 SAFE_FREE(host);
3628 TALLOC_FREE(hostnameDN);
3629 return ADS_ERROR(LDAP_NO_MEMORY);
3632 status = ads_del_dn(ads, dn);
3633 if (!ADS_ERR_OK(status)) {
3634 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3635 SAFE_FREE(host);
3636 TALLOC_FREE(dn);
3637 TALLOC_FREE(hostnameDN);
3638 return status;
3641 TALLOC_FREE(dn);
3644 /* there should be no subordinate objects anymore */
3645 status = ads_do_search_retry(ads, hostnameDN,
3646 LDAP_SCOPE_ONELEVEL,
3647 "(objectclass=*)", attrs, &res);
3649 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3650 SAFE_FREE(host);
3651 TALLOC_FREE(hostnameDN);
3652 return status;
3655 /* delete hostnameDN now */
3656 status = ads_del_dn(ads, hostnameDN);
3657 if (!ADS_ERR_OK(status)) {
3658 SAFE_FREE(host);
3659 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3660 TALLOC_FREE(hostnameDN);
3661 return status;
3665 TALLOC_FREE(hostnameDN);
3667 status = ads_find_machine_acct(ads, &res, host);
3668 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3669 DEBUG(3, ("Failed to remove host account.\n"));
3670 SAFE_FREE(host);
3671 return status;
3674 SAFE_FREE(host);
3675 return status;
3679 * pull all token-sids from an LDAP dn
3680 * @param ads connection to ads server
3681 * @param mem_ctx TALLOC_CTX for allocating sid array
3682 * @param dn of LDAP object
3683 * @param user_sid pointer to struct dom_sid (objectSid)
3684 * @param primary_group_sid pointer to struct dom_sid (self composed)
3685 * @param sids pointer to sid array to allocate
3686 * @param num_sids counter of SIDs pulled
3687 * @return status of token query
3689 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3690 TALLOC_CTX *mem_ctx,
3691 const char *dn,
3692 struct dom_sid *user_sid,
3693 struct dom_sid *primary_group_sid,
3694 struct dom_sid **sids,
3695 size_t *num_sids)
3697 ADS_STATUS status;
3698 LDAPMessage *res = NULL;
3699 int count = 0;
3700 size_t tmp_num_sids;
3701 struct dom_sid *tmp_sids;
3702 struct dom_sid tmp_user_sid;
3703 struct dom_sid tmp_primary_group_sid;
3704 uint32_t pgid;
3705 const char *attrs[] = {
3706 "objectSid",
3707 "tokenGroups",
3708 "primaryGroupID",
3709 NULL
3712 status = ads_search_retry_dn(ads, &res, dn, attrs);
3713 if (!ADS_ERR_OK(status)) {
3714 return status;
3717 count = ads_count_replies(ads, res);
3718 if (count != 1) {
3719 ads_msgfree(ads, res);
3720 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3723 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3724 ads_msgfree(ads, res);
3725 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3728 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3729 ads_msgfree(ads, res);
3730 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3734 /* hack to compose the primary group sid without knowing the
3735 * domsid */
3737 struct dom_sid domsid;
3739 sid_copy(&domsid, &tmp_user_sid);
3741 if (!sid_split_rid(&domsid, NULL)) {
3742 ads_msgfree(ads, res);
3743 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3746 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3747 ads_msgfree(ads, res);
3748 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3752 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3754 if (tmp_num_sids == 0 || !tmp_sids) {
3755 ads_msgfree(ads, res);
3756 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3759 if (num_sids) {
3760 *num_sids = tmp_num_sids;
3763 if (sids) {
3764 *sids = tmp_sids;
3767 if (user_sid) {
3768 *user_sid = tmp_user_sid;
3771 if (primary_group_sid) {
3772 *primary_group_sid = tmp_primary_group_sid;
3775 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3777 ads_msgfree(ads, res);
3778 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3782 * Find a sAMAccoutName in LDAP
3783 * @param ads connection to ads server
3784 * @param mem_ctx TALLOC_CTX for allocating sid array
3785 * @param samaccountname to search
3786 * @param uac_ret uint32_t pointer userAccountControl attribute value
3787 * @param dn_ret pointer to dn
3788 * @return status of token query
3790 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3791 TALLOC_CTX *mem_ctx,
3792 const char *samaccountname,
3793 uint32_t *uac_ret,
3794 const char **dn_ret)
3796 ADS_STATUS status;
3797 const char *attrs[] = { "userAccountControl", NULL };
3798 const char *filter;
3799 LDAPMessage *res = NULL;
3800 char *dn = NULL;
3801 uint32_t uac = 0;
3803 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3804 samaccountname);
3805 if (filter == NULL) {
3806 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3807 goto out;
3810 status = ads_do_search_all(ads, ads->config.bind_path,
3811 LDAP_SCOPE_SUBTREE,
3812 filter, attrs, &res);
3814 if (!ADS_ERR_OK(status)) {
3815 goto out;
3818 if (ads_count_replies(ads, res) != 1) {
3819 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3820 goto out;
3823 dn = ads_get_dn(ads, talloc_tos(), res);
3824 if (dn == NULL) {
3825 status = ADS_ERROR(LDAP_NO_MEMORY);
3826 goto out;
3829 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3830 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3831 goto out;
3834 if (uac_ret) {
3835 *uac_ret = uac;
3838 if (dn_ret) {
3839 *dn_ret = talloc_strdup(mem_ctx, dn);
3840 if (!*dn_ret) {
3841 status = ADS_ERROR(LDAP_NO_MEMORY);
3842 goto out;
3845 out:
3846 TALLOC_FREE(dn);
3847 ads_msgfree(ads, res);
3849 return status;
3853 * find our configuration path
3854 * @param ads connection to ads server
3855 * @param mem_ctx Pointer to talloc context
3856 * @param config_path Pointer to the config path
3857 * @return status of search
3859 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3860 TALLOC_CTX *mem_ctx,
3861 char **config_path)
3863 ADS_STATUS status;
3864 LDAPMessage *res = NULL;
3865 const char *config_context = NULL;
3866 const char *attrs[] = { "configurationNamingContext", NULL };
3868 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3869 "(objectclass=*)", attrs, &res);
3870 if (!ADS_ERR_OK(status)) {
3871 return status;
3874 config_context = ads_pull_string(ads, mem_ctx, res,
3875 "configurationNamingContext");
3876 ads_msgfree(ads, res);
3877 if (!config_context) {
3878 return ADS_ERROR(LDAP_NO_MEMORY);
3881 if (config_path) {
3882 *config_path = talloc_strdup(mem_ctx, config_context);
3883 if (!*config_path) {
3884 return ADS_ERROR(LDAP_NO_MEMORY);
3888 return ADS_ERROR(LDAP_SUCCESS);
3892 * find the displayName of an extended right
3893 * @param ads connection to ads server
3894 * @param config_path The config path
3895 * @param mem_ctx Pointer to talloc context
3896 * @param GUID struct of the rightsGUID
3897 * @return status of search
3899 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3900 const char *config_path,
3901 TALLOC_CTX *mem_ctx,
3902 const struct GUID *rights_guid)
3904 ADS_STATUS rc;
3905 LDAPMessage *res = NULL;
3906 char *expr = NULL;
3907 const char *attrs[] = { "displayName", NULL };
3908 const char *result = NULL;
3909 const char *path;
3911 if (!ads || !mem_ctx || !rights_guid) {
3912 goto done;
3915 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3916 GUID_string(mem_ctx, rights_guid));
3917 if (!expr) {
3918 goto done;
3921 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3922 if (!path) {
3923 goto done;
3926 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3927 expr, attrs, &res);
3928 if (!ADS_ERR_OK(rc)) {
3929 goto done;
3932 if (ads_count_replies(ads, res) != 1) {
3933 goto done;
3936 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3938 done:
3939 ads_msgfree(ads, res);
3940 return result;
3944 * verify or build and verify an account ou
3945 * @param mem_ctx Pointer to talloc context
3946 * @param ads connection to ads server
3947 * @param account_ou
3948 * @return status of search
3951 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3952 ADS_STRUCT *ads,
3953 const char **account_ou)
3955 char **exploded_dn;
3956 const char *name;
3957 char *ou_string;
3959 if (account_ou == NULL) {
3960 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3963 if (*account_ou != NULL) {
3964 exploded_dn = ldap_explode_dn(*account_ou, 0);
3965 if (exploded_dn) {
3966 ldap_value_free(exploded_dn);
3967 return ADS_SUCCESS;
3971 ou_string = ads_ou_string(ads, *account_ou);
3972 if (!ou_string) {
3973 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3976 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3977 ads->config.bind_path);
3978 SAFE_FREE(ou_string);
3980 if (!name) {
3981 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3984 exploded_dn = ldap_explode_dn(name, 0);
3985 if (!exploded_dn) {
3986 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3988 ldap_value_free(exploded_dn);
3990 *account_ou = name;
3991 return ADS_SUCCESS;
3994 #endif