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/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
32 #include "lib/param/loadparm.h"
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm
;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum
)
63 LDAP
*ldap_open_with_timeout(const char *server
,
64 struct sockaddr_storage
*ss
,
65 int port
, unsigned int to
)
69 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
70 "%u seconds\n", server
, port
, to
));
72 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
73 /* Only use this private LDAP function if we are in make test,
74 * as this is the best way to get the emulated TCP socket into
76 if (socket_wrapper_dir() != NULL
) {
81 status
= open_socket_out(ss
, port
, to
, &fd
);
83 if (!NT_STATUS_IS_OK(status
)) {
87 #ifndef LDAP_PROTO_TCP
88 #define LDAP_PROTO_TCP 1
90 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
94 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
97 if (ldap_err
!= LDAP_SUCCESS
) {
107 CatchSignal(SIGALRM
, gotalarm_sig
);
109 /* End setup timeout. */
112 ldp
= ldap_open(server
, port
);
115 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
116 server
, port
, strerror(errno
)));
118 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server
, port
));
122 /* Teardown timeout. */
124 CatchSignal(SIGALRM
, SIG_IGN
);
130 static int ldap_search_with_timeout(LDAP
*ld
,
131 LDAP_CONST
char *base
,
133 LDAP_CONST
char *filter
,
136 LDAPControl
**sctrls
,
137 LDAPControl
**cctrls
,
141 int to
= lp_ldap_timeout();
142 struct timeval timeout
;
143 struct timeval
*timeout_ptr
= NULL
;
146 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
152 timeout_ptr
= &timeout
;
154 /* Setup alarm timeout. */
155 CatchSignal(SIGALRM
, gotalarm_sig
);
156 /* Make the alarm time one second beyond
157 the timout we're setting for the
158 remote search timeout, to allow that
159 to fire in preference. */
161 /* End setup timeout. */
165 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
166 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
170 /* Teardown alarm timeout. */
171 CatchSignal(SIGALRM
, SIG_IGN
);
176 return LDAP_TIMELIMIT_EXCEEDED
;
179 * A bug in OpenLDAP means ldap_search_ext_s can return
180 * LDAP_SUCCESS but with a NULL res pointer. Cope with
181 * this. See bug #6279 for details. JRA.
185 return LDAP_TIMELIMIT_EXCEEDED
;
191 /**********************************************
192 Do client and server sitename match ?
193 **********************************************/
195 bool ads_sitename_match(ADS_STRUCT
*ads
)
197 if (ads
->config
.server_site_name
== NULL
&&
198 ads
->config
.client_site_name
== NULL
) {
199 DEBUG(10,("ads_sitename_match: both null\n"));
202 if (ads
->config
.server_site_name
&&
203 ads
->config
.client_site_name
&&
204 strequal(ads
->config
.server_site_name
,
205 ads
->config
.client_site_name
)) {
206 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
209 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
210 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
211 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
215 /**********************************************
216 Is this the closest DC ?
217 **********************************************/
219 bool ads_closest_dc(ADS_STRUCT
*ads
)
221 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
222 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
226 /* not sure if this can ever happen */
227 if (ads_sitename_match(ads
)) {
228 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
232 if (ads
->config
.client_site_name
== NULL
) {
233 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
237 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
238 ads
->config
.ldap_server_name
));
245 try a connection to a given ldap server, returning True and setting the servers IP
246 in the ads struct if successful
248 static bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
, bool gc
)
250 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
;
251 TALLOC_CTX
*frame
= talloc_stackframe();
253 struct sockaddr_storage ss
;
254 char addr
[INET6_ADDRSTRLEN
];
256 if (!server
|| !*server
) {
261 if (!resolve_name(server
, &ss
, 0x20, true)) {
262 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
267 print_sockaddr(addr
, sizeof(addr
), &ss
);
269 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
270 addr
, ads
->server
.realm
));
272 ZERO_STRUCT( cldap_reply
);
274 if ( !ads_cldap_netlogon_5(frame
, &ss
, ads
->server
.realm
, &cldap_reply
) ) {
275 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr
));
280 /* Check the CLDAP reply flags */
282 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
283 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
289 /* Fill in the ads->config values */
291 SAFE_FREE(ads
->config
.realm
);
292 SAFE_FREE(ads
->config
.bind_path
);
293 SAFE_FREE(ads
->config
.ldap_server_name
);
294 SAFE_FREE(ads
->config
.server_site_name
);
295 SAFE_FREE(ads
->config
.client_site_name
);
296 SAFE_FREE(ads
->server
.workgroup
);
298 ads
->config
.flags
= cldap_reply
.server_type
;
299 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
300 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
301 strupper_m(ads
->config
.realm
);
302 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
303 if (*cldap_reply
.server_site
) {
304 ads
->config
.server_site_name
=
305 SMB_STRDUP(cldap_reply
.server_site
);
307 if (*cldap_reply
.client_site
) {
308 ads
->config
.client_site_name
=
309 SMB_STRDUP(cldap_reply
.client_site
);
311 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain_name
);
313 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
316 /* Store our site name. */
317 sitename_store( cldap_reply
.domain_name
, cldap_reply
.client_site
);
318 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
328 /**********************************************************************
329 Try to find an AD dc using our internal name resolution routines
330 Try the realm first and then then workgroup name if netbios is not
332 **********************************************************************/
334 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
336 const char *c_domain
;
339 struct ip_service
*ip_list
;
342 bool got_realm
= False
;
343 bool use_own_domain
= False
;
345 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
347 /* if the realm and workgroup are both empty, assume they are ours */
350 c_realm
= ads
->server
.realm
;
352 if ( !c_realm
|| !*c_realm
) {
353 /* special case where no realm and no workgroup means our own */
354 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
355 use_own_domain
= True
;
356 c_realm
= lp_realm();
360 if (c_realm
&& *c_realm
)
363 /* we need to try once with the realm name and fallback to the
364 netbios domain name if we fail (if netbios has not been disabled */
366 if ( !got_realm
&& !lp_disable_netbios() ) {
367 c_realm
= ads
->server
.workgroup
;
368 if (!c_realm
|| !*c_realm
) {
369 if ( use_own_domain
)
370 c_realm
= lp_workgroup();
374 if ( !c_realm
|| !*c_realm
) {
375 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
377 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
380 if ( use_own_domain
) {
381 c_domain
= lp_workgroup();
383 c_domain
= ads
->server
.workgroup
;
390 * In case of LDAP we use get_dc_name() as that
391 * creates the custom krb5.conf file
393 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
395 struct sockaddr_storage ip_out
;
397 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
398 (got_realm
? "realm" : "domain"), realm
));
400 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
402 * we call ads_try_connect() to fill in the
403 * ads->config details
405 if (ads_try_connect(ads
, srv_name
, false)) {
410 return NT_STATUS_NO_LOGON_SERVERS
;
413 sitename
= sitename_fetch(realm
);
417 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
418 (got_realm
? "realm" : "domain"), realm
));
420 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
421 if (!NT_STATUS_IS_OK(status
)) {
422 /* fall back to netbios if we can */
423 if ( got_realm
&& !lp_disable_netbios() ) {
432 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
433 for ( i
=0; i
<count
; i
++ ) {
434 char server
[INET6_ADDRSTRLEN
];
436 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
438 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
442 /* realm in this case is a workgroup name. We need
443 to ignore any IP addresses in the negative connection
444 cache that match ip addresses returned in the ad realm
445 case. It sucks that I have to reproduce the logic above... */
446 c_realm
= ads
->server
.realm
;
447 if ( !c_realm
|| !*c_realm
) {
448 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
449 c_realm
= lp_realm();
452 if (c_realm
&& *c_realm
&&
453 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
454 /* Ensure we add the workgroup name for this
455 IP address as negative too. */
456 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
461 if ( ads_try_connect(ads
, server
, false) ) {
467 /* keep track of failures */
468 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
473 /* In case we failed to contact one of our closest DC on our site we
474 * need to try to find another DC, retry with a site-less SRV DNS query
478 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
479 "trying to find another DC\n", sitename
));
481 namecache_delete(realm
, 0x1C);
485 return NT_STATUS_NO_LOGON_SERVERS
;
488 /*********************************************************************
489 *********************************************************************/
491 static NTSTATUS
ads_lookup_site(void)
493 ADS_STRUCT
*ads
= NULL
;
494 ADS_STATUS ads_status
;
495 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
497 ads
= ads_init(lp_realm(), NULL
, NULL
);
499 return NT_STATUS_NO_MEMORY
;
502 /* The NO_BIND here will find a DC and set the client site
503 but not establish the TCP connection */
505 ads
->auth
.flags
= ADS_AUTH_NO_BIND
;
506 ads_status
= ads_connect(ads
);
507 if (!ADS_ERR_OK(ads_status
)) {
508 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
509 ads_errstr(ads_status
)));
511 nt_status
= ads_ntstatus(ads_status
);
520 /*********************************************************************
521 *********************************************************************/
523 static const char* host_dns_domain(const char *fqdn
)
525 const char *p
= fqdn
;
527 /* go to next char following '.' */
529 if ((p
= strchr_m(fqdn
, '.')) != NULL
) {
538 * Connect to the Global Catalog server
539 * @param ads Pointer to an existing ADS_STRUCT
540 * @return status of connection
542 * Simple wrapper around ads_connect() that fills in the
543 * GC ldap server information
546 ADS_STATUS
ads_connect_gc(ADS_STRUCT
*ads
)
548 TALLOC_CTX
*frame
= talloc_stackframe();
549 struct dns_rr_srv
*gcs_list
;
551 const char *realm
= ads
->server
.realm
;
552 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
553 ADS_STATUS ads_status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
556 char *sitename
= NULL
;
557 const char *dns_hosts_file
;
562 if ((sitename
= sitename_fetch(realm
)) == NULL
) {
564 sitename
= sitename_fetch(realm
);
567 dns_hosts_file
= lp_parm_const_string(-1, "resolv", "host file", NULL
);
569 /* We try once with a sitename and once without
570 (unless we don't have a sitename and then we're
573 if (sitename
== NULL
)
576 nt_status
= ads_dns_query_gcs(frame
, dns_hosts_file
,
578 &gcs_list
, &num_gcs
);
582 if (!NT_STATUS_IS_OK(nt_status
)) {
583 ads_status
= ADS_ERROR_NT(nt_status
);
587 /* Loop until we get a successful connection or have gone
588 through them all. When connecting a GC server, make sure that
589 the realm is the server's DNS name and not the forest root */
591 for (i
=0; i
<num_gcs
; i
++) {
592 ads
->server
.gc
= true;
593 ads
->server
.ldap_server
= SMB_STRDUP(gcs_list
[i
].hostname
);
594 ads
->server
.realm
= SMB_STRDUP(host_dns_domain(ads
->server
.ldap_server
));
595 ads_status
= ads_connect(ads
);
596 if (ADS_ERR_OK(ads_status
)) {
597 /* Reset the bind_dn to "". A Global Catalog server
598 may host multiple domain trees in a forest.
599 Windows 2003 GC server will accept "" as the search
600 path to imply search all domain trees in the forest */
602 SAFE_FREE(ads
->config
.bind_path
);
603 ads
->config
.bind_path
= SMB_STRDUP("");
608 SAFE_FREE(ads
->server
.ldap_server
);
609 SAFE_FREE(ads
->server
.realm
);
612 TALLOC_FREE(gcs_list
);
618 talloc_destroy(frame
);
625 * Connect to the LDAP server
626 * @param ads Pointer to an existing ADS_STRUCT
627 * @return status of connection
629 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
631 int version
= LDAP_VERSION3
;
634 char addr
[INET6_ADDRSTRLEN
];
636 ZERO_STRUCT(ads
->ldap
);
637 ads
->ldap
.last_attempt
= time_mono(NULL
);
638 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
640 /* try with a user specified server */
642 if (DEBUGLEVEL
>= 11) {
643 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
644 DEBUG(11,("ads_connect: entering\n"));
645 DEBUGADD(11,("%s\n", s
));
649 if (ads
->server
.ldap_server
)
651 if (ads_try_connect(ads
, ads
->server
.ldap_server
, ads
->server
.gc
)) {
655 /* The choice of which GC use is handled one level up in
656 ads_connect_gc(). If we continue on from here with
657 ads_find_dc() we will get GC searches on port 389 which
658 doesn't work. --jerry */
660 if (ads
->server
.gc
== true) {
661 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
665 ntstatus
= ads_find_dc(ads
);
666 if (NT_STATUS_IS_OK(ntstatus
)) {
670 status
= ADS_ERROR_NT(ntstatus
);
675 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
676 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
678 if (!ads
->auth
.user_name
) {
679 /* Must use the userPrincipalName value here or sAMAccountName
680 and not servicePrincipalName; found by Guenther Deschner */
682 if (asprintf(&ads
->auth
.user_name
, "%s$", lp_netbios_name() ) == -1) {
683 DEBUG(0,("ads_connect: asprintf fail.\n"));
684 ads
->auth
.user_name
= NULL
;
688 if (!ads
->auth
.realm
) {
689 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
692 if (!ads
->auth
.kdc_server
) {
693 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
694 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
697 /* If the caller() requested no LDAP bind, then we are done */
699 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
700 status
= ADS_SUCCESS
;
704 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
705 if (!ads
->ldap
.mem_ctx
) {
706 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
710 /* Otherwise setup the TCP LDAP session */
712 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
714 ads
->ldap
.port
, lp_ldap_timeout());
715 if (ads
->ldap
.ld
== NULL
) {
716 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
719 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
721 /* cache the successful connection for workgroup and realm */
722 if (ads_closest_dc(ads
)) {
723 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
724 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
727 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
729 if ( lp_ldap_ssl_ads() ) {
730 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
731 if (!ADS_ERR_OK(status
)) {
736 /* fill in the current time and offsets */
738 status
= ads_current_time( ads
);
739 if ( !ADS_ERR_OK(status
) ) {
743 /* Now do the bind */
745 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
746 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
750 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
751 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
755 status
= ads_sasl_bind(ads
);
758 if (DEBUGLEVEL
>= 11) {
759 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
760 DEBUG(11,("ads_connect: leaving with: %s\n",
761 ads_errstr(status
)));
762 DEBUGADD(11,("%s\n", s
));
770 * Connect to the LDAP server using given credentials
771 * @param ads Pointer to an existing ADS_STRUCT
772 * @return status of connection
774 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
776 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
778 return ads_connect(ads
);
782 * Disconnect the LDAP server
783 * @param ads Pointer to an existing ADS_STRUCT
785 void ads_disconnect(ADS_STRUCT
*ads
)
788 ldap_unbind(ads
->ldap
.ld
);
791 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
792 ads
->ldap
.wrap_ops
->disconnect(ads
);
794 if (ads
->ldap
.mem_ctx
) {
795 talloc_free(ads
->ldap
.mem_ctx
);
797 ZERO_STRUCT(ads
->ldap
);
801 Duplicate a struct berval into talloc'ed memory
803 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
805 struct berval
*value
;
807 if (!in_val
) return NULL
;
809 value
= talloc_zero(ctx
, struct berval
);
812 if (in_val
->bv_len
== 0) return value
;
814 value
->bv_len
= in_val
->bv_len
;
815 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
821 Make a values list out of an array of (struct berval *)
823 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
824 const struct berval
**in_vals
)
826 struct berval
**values
;
829 if (!in_vals
) return NULL
;
830 for (i
=0; in_vals
[i
]; i
++)
832 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
833 if (!values
) return NULL
;
835 for (i
=0; in_vals
[i
]; i
++) {
836 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
842 UTF8-encode a values list out of an array of (char *)
844 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
850 if (!in_vals
) return NULL
;
851 for (i
=0; in_vals
[i
]; i
++)
853 values
= talloc_zero_array(ctx
, char *, i
+1);
854 if (!values
) return NULL
;
856 for (i
=0; in_vals
[i
]; i
++) {
857 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
866 Pull a (char *) array out of a UTF8-encoded values list
868 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
872 size_t converted_size
;
874 if (!in_vals
) return NULL
;
875 for (i
=0; in_vals
[i
]; i
++)
877 values
= talloc_zero_array(ctx
, char *, i
+1);
878 if (!values
) return NULL
;
880 for (i
=0; in_vals
[i
]; i
++) {
881 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
883 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
884 "%s", strerror(errno
)));
891 * Do a search with paged results. cookie must be null on the first
892 * call, and then returned on each subsequent call. It will be null
893 * again when the entire search is complete
894 * @param ads connection to ads server
895 * @param bind_path Base dn for the search
896 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
897 * @param expr Search expression - specified in local charset
898 * @param attrs Attributes to retrieve - specified in utf8 or ascii
899 * @param res ** which will contain results - free res* with ads_msgfree()
900 * @param count Number of entries retrieved on this page
901 * @param cookie The paged results cookie to be returned on subsequent calls
902 * @return status of search
904 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
905 const char *bind_path
,
906 int scope
, const char *expr
,
907 const char **attrs
, void *args
,
909 int *count
, struct berval
**cookie
)
912 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
913 size_t converted_size
;
914 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
915 BerElement
*cookie_be
= NULL
;
916 struct berval
*cookie_bv
= NULL
;
917 BerElement
*ext_be
= NULL
;
918 struct berval
*ext_bv
= NULL
;
921 ads_control
*external_control
= (ads_control
*) args
;
925 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
926 return ADS_ERROR(LDAP_NO_MEMORY
);
928 /* 0 means the conversion worked but the result was empty
929 so we only fail if it's -1. In any case, it always
930 at least nulls out the dest */
931 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
932 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
938 if (!attrs
|| !(*attrs
))
941 /* This would be the utf8-encoded version...*/
942 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
943 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
949 /* Paged results only available on ldap v3 or later */
950 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
951 if (version
< LDAP_VERSION3
) {
952 rc
= LDAP_NOT_SUPPORTED
;
956 cookie_be
= ber_alloc_t(LBER_USE_DER
);
958 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
959 ber_bvfree(*cookie
); /* don't need it from last time */
962 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
964 ber_flatten(cookie_be
, &cookie_bv
);
965 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
966 PagedResults
.ldctl_iscritical
= (char) 1;
967 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
968 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
970 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
971 NoReferrals
.ldctl_iscritical
= (char) 0;
972 NoReferrals
.ldctl_value
.bv_len
= 0;
973 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
975 if (external_control
&&
976 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
977 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
979 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
980 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
982 /* win2k does not accept a ldctl_value beeing passed in */
984 if (external_control
->val
!= 0) {
986 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
991 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
995 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1000 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1001 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1004 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1005 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1008 controls
[0] = &NoReferrals
;
1009 controls
[1] = &PagedResults
;
1010 controls
[2] = &ExternalCtrl
;
1014 controls
[0] = &NoReferrals
;
1015 controls
[1] = &PagedResults
;
1019 /* we need to disable referrals as the openldap libs don't
1020 handle them and paged results at the same time. Using them
1021 together results in the result record containing the server
1022 page control being removed from the result list (tridge/jmcd)
1024 leaving this in despite the control that says don't generate
1025 referrals, in case the server doesn't support it (jmcd)
1027 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1029 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1030 search_attrs
, 0, controls
,
1031 NULL
, LDAP_NO_LIMIT
,
1032 (LDAPMessage
**)res
);
1034 ber_free(cookie_be
, 1);
1035 ber_bvfree(cookie_bv
);
1038 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1039 ldap_err2string(rc
)));
1043 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1044 NULL
, &rcontrols
, 0);
1050 for (i
=0; rcontrols
[i
]; i
++) {
1051 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1052 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1053 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1055 /* the berval is the cookie, but must be freed when
1057 if (cookie_bv
->bv_len
) /* still more to do */
1058 *cookie
=ber_bvdup(cookie_bv
);
1061 ber_bvfree(cookie_bv
);
1062 ber_free(cookie_be
, 1);
1066 ldap_controls_free(rcontrols
);
1069 talloc_destroy(ctx
);
1072 ber_free(ext_be
, 1);
1079 /* if/when we decide to utf8-encode attrs, take out this next line */
1080 TALLOC_FREE(search_attrs
);
1082 return ADS_ERROR(rc
);
1085 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1086 int scope
, const char *expr
,
1087 const char **attrs
, LDAPMessage
**res
,
1088 int *count
, struct berval
**cookie
)
1090 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1095 * Get all results for a search. This uses ads_do_paged_search() to return
1096 * all entries in a large search.
1097 * @param ads connection to ads server
1098 * @param bind_path Base dn for the search
1099 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1100 * @param expr Search expression
1101 * @param attrs Attributes to retrieve
1102 * @param res ** which will contain results - free res* with ads_msgfree()
1103 * @return status of search
1105 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1106 int scope
, const char *expr
,
1107 const char **attrs
, void *args
,
1110 struct berval
*cookie
= NULL
;
1115 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1118 if (!ADS_ERR_OK(status
))
1121 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1123 LDAPMessage
*res2
= NULL
;
1125 LDAPMessage
*msg
, *next
;
1127 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1128 attrs
, args
, &res2
, &count
, &cookie
);
1130 if (!ADS_ERR_OK(status2
)) break;
1132 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1133 that this works on all ldap libs, but I have only tested with openldap */
1134 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1135 next
= ads_next_message(ads
, msg
);
1136 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1138 /* note that we do not free res2, as the memory is now
1139 part of the main returned list */
1142 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1143 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1149 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1150 int scope
, const char *expr
,
1151 const char **attrs
, LDAPMessage
**res
)
1153 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1156 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1157 int scope
, const char *expr
,
1158 const char **attrs
, uint32 sd_flags
,
1163 args
.control
= ADS_SD_FLAGS_OID
;
1164 args
.val
= sd_flags
;
1165 args
.critical
= True
;
1167 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1172 * Run a function on all results for a search. Uses ads_do_paged_search() and
1173 * runs the function as each page is returned, using ads_process_results()
1174 * @param ads connection to ads server
1175 * @param bind_path Base dn for the search
1176 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1177 * @param expr Search expression - specified in local charset
1178 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1179 * @param fn Function which takes attr name, values list, and data_area
1180 * @param data_area Pointer which is passed to function on each call
1181 * @return status of search
1183 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1184 int scope
, const char *expr
, const char **attrs
,
1185 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1188 struct berval
*cookie
= NULL
;
1193 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1196 if (!ADS_ERR_OK(status
)) return status
;
1198 ads_process_results(ads
, res
, fn
, data_area
);
1199 ads_msgfree(ads
, res
);
1202 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1203 &res
, &count
, &cookie
);
1205 if (!ADS_ERR_OK(status
)) break;
1207 ads_process_results(ads
, res
, fn
, data_area
);
1208 ads_msgfree(ads
, res
);
1215 * Do a search with a timeout.
1216 * @param ads connection to ads server
1217 * @param bind_path Base dn for the search
1218 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1219 * @param expr Search expression
1220 * @param attrs Attributes to retrieve
1221 * @param res ** which will contain results - free res* with ads_msgfree()
1222 * @return status of search
1224 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1226 const char **attrs
, LDAPMessage
**res
)
1229 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1230 size_t converted_size
;
1234 if (!(ctx
= talloc_init("ads_do_search"))) {
1235 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1236 return ADS_ERROR(LDAP_NO_MEMORY
);
1239 /* 0 means the conversion worked but the result was empty
1240 so we only fail if it's negative. In any case, it always
1241 at least nulls out the dest */
1242 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1243 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1245 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1246 rc
= LDAP_NO_MEMORY
;
1250 if (!attrs
|| !(*attrs
))
1251 search_attrs
= NULL
;
1253 /* This would be the utf8-encoded version...*/
1254 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1255 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1257 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1258 rc
= LDAP_NO_MEMORY
;
1263 /* see the note in ads_do_paged_search - we *must* disable referrals */
1264 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1266 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1267 search_attrs
, 0, NULL
, NULL
,
1269 (LDAPMessage
**)res
);
1271 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1272 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1277 talloc_destroy(ctx
);
1278 /* if/when we decide to utf8-encode attrs, take out this next line */
1279 TALLOC_FREE(search_attrs
);
1280 return ADS_ERROR(rc
);
1283 * Do a general ADS search
1284 * @param ads connection to ads server
1285 * @param res ** which will contain results - free res* with ads_msgfree()
1286 * @param expr Search expression
1287 * @param attrs Attributes to retrieve
1288 * @return status of search
1290 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1291 const char *expr
, const char **attrs
)
1293 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1298 * Do a search on a specific DistinguishedName
1299 * @param ads connection to ads server
1300 * @param res ** which will contain results - free res* with ads_msgfree()
1301 * @param dn DistinguishName to search
1302 * @param attrs Attributes to retrieve
1303 * @return status of search
1305 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1306 const char *dn
, const char **attrs
)
1308 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1313 * Free up memory from a ads_search
1314 * @param ads connection to ads server
1315 * @param msg Search results to free
1317 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1324 * Get a dn from search results
1325 * @param ads connection to ads server
1326 * @param msg Search result
1329 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1331 char *utf8_dn
, *unix_dn
;
1332 size_t converted_size
;
1334 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1337 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1341 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1342 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1346 ldap_memfree(utf8_dn
);
1351 * Get the parent from a dn
1352 * @param dn the dn to return the parent from
1353 * @return parent dn string
1355 char *ads_parent_dn(const char *dn
)
1363 p
= strchr(dn
, ',');
1373 * Find a machine account given a hostname
1374 * @param ads connection to ads server
1375 * @param res ** which will contain results - free res* with ads_msgfree()
1376 * @param host Hostname to search for
1377 * @return status of search
1379 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1380 const char *machine
)
1384 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1388 /* the easiest way to find a machine account anywhere in the tree
1389 is to look for hostname$ */
1390 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1391 DEBUG(1, ("asprintf failed!\n"));
1392 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1395 status
= ads_search(ads
, res
, expr
, attrs
);
1401 * Initialize a list of mods to be used in a modify request
1402 * @param ctx An initialized TALLOC_CTX
1403 * @return allocated ADS_MODLIST
1405 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1407 #define ADS_MODLIST_ALLOC_SIZE 10
1410 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1411 /* -1 is safety to make sure we don't go over the end.
1412 need to reset it to NULL before doing ldap modify */
1413 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1415 return (ADS_MODLIST
)mods
;
1420 add an attribute to the list, with values list already constructed
1422 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1423 int mod_op
, const char *name
,
1424 const void *_invals
)
1426 const void **invals
= (const void **)_invals
;
1428 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1429 struct berval
**ber_values
= NULL
;
1430 char **char_values
= NULL
;
1433 mod_op
= LDAP_MOD_DELETE
;
1435 if (mod_op
& LDAP_MOD_BVALUES
)
1436 ber_values
= ads_dup_values(ctx
,
1437 (const struct berval
**)invals
);
1439 char_values
= ads_push_strvals(ctx
,
1440 (const char **) invals
);
1443 /* find the first empty slot */
1444 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1446 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1447 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1448 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1449 return ADS_ERROR(LDAP_NO_MEMORY
);
1450 memset(&modlist
[curmod
], 0,
1451 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1452 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1453 *mods
= (ADS_MODLIST
)modlist
;
1456 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1457 return ADS_ERROR(LDAP_NO_MEMORY
);
1458 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1459 if (mod_op
& LDAP_MOD_BVALUES
) {
1460 modlist
[curmod
]->mod_bvalues
= ber_values
;
1461 } else if (mod_op
& LDAP_MOD_DELETE
) {
1462 modlist
[curmod
]->mod_values
= NULL
;
1464 modlist
[curmod
]->mod_values
= char_values
;
1467 modlist
[curmod
]->mod_op
= mod_op
;
1468 return ADS_ERROR(LDAP_SUCCESS
);
1472 * Add a single string value to a mod list
1473 * @param ctx An initialized TALLOC_CTX
1474 * @param mods An initialized ADS_MODLIST
1475 * @param name The attribute name to add
1476 * @param val The value to add - NULL means DELETE
1477 * @return ADS STATUS indicating success of add
1479 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1480 const char *name
, const char *val
)
1482 const char *values
[2];
1488 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1489 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1493 * Add an array of string values to a mod list
1494 * @param ctx An initialized TALLOC_CTX
1495 * @param mods An initialized ADS_MODLIST
1496 * @param name The attribute name to add
1497 * @param vals The array of string values to add - NULL means DELETE
1498 * @return ADS STATUS indicating success of add
1500 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1501 const char *name
, const char **vals
)
1504 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1505 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1506 name
, (const void **) vals
);
1511 * Add a single ber-encoded value to a mod list
1512 * @param ctx An initialized TALLOC_CTX
1513 * @param mods An initialized ADS_MODLIST
1514 * @param name The attribute name to add
1515 * @param val The value to add - NULL means DELETE
1516 * @return ADS STATUS indicating success of add
1518 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1519 const char *name
, const struct berval
*val
)
1521 const struct berval
*values
[2];
1526 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1527 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1528 name
, (const void **) values
);
1533 * Perform an ldap modify
1534 * @param ads connection to ads server
1535 * @param mod_dn DistinguishedName to modify
1536 * @param mods list of modifications to perform
1537 * @return status of modify
1539 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1542 char *utf8_dn
= NULL
;
1543 size_t converted_size
;
1545 this control is needed to modify that contains a currently
1546 non-existent attribute (but allowable for the object) to run
1548 LDAPControl PermitModify
= {
1549 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1552 LDAPControl
*controls
[2];
1554 controls
[0] = &PermitModify
;
1557 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1558 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1561 /* find the end of the list, marked by NULL or -1 */
1562 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1563 /* make sure the end of the list is NULL */
1565 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1566 (LDAPMod
**) mods
, controls
, NULL
);
1567 TALLOC_FREE(utf8_dn
);
1568 return ADS_ERROR(ret
);
1572 * Perform an ldap add
1573 * @param ads connection to ads server
1574 * @param new_dn DistinguishedName to add
1575 * @param mods list of attributes and values for DN
1576 * @return status of add
1578 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1581 char *utf8_dn
= NULL
;
1582 size_t converted_size
;
1584 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1585 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1586 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1589 /* find the end of the list, marked by NULL or -1 */
1590 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1591 /* make sure the end of the list is NULL */
1594 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1595 TALLOC_FREE(utf8_dn
);
1596 return ADS_ERROR(ret
);
1600 * Delete a DistinguishedName
1601 * @param ads connection to ads server
1602 * @param new_dn DistinguishedName to delete
1603 * @return status of delete
1605 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1608 char *utf8_dn
= NULL
;
1609 size_t converted_size
;
1610 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1611 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1612 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1615 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1616 TALLOC_FREE(utf8_dn
);
1617 return ADS_ERROR(ret
);
1621 * Build an org unit string
1622 * if org unit is Computers or blank then assume a container, otherwise
1623 * assume a / separated list of organisational units.
1624 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1625 * @param ads connection to ads server
1626 * @param org_unit Organizational unit
1627 * @return org unit string - caller must free
1629 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1633 if (!org_unit
|| !*org_unit
) {
1635 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1637 /* samba4 might not yet respond to a wellknownobject-query */
1638 return ret
? ret
: SMB_STRDUP("cn=Computers");
1641 if (strequal(org_unit
, "Computers")) {
1642 return SMB_STRDUP("cn=Computers");
1645 /* jmcd: removed "\\" from the separation chars, because it is
1646 needed as an escape for chars like '#' which are valid in an
1648 return ads_build_path(org_unit
, "/", "ou=", 1);
1652 * Get a org unit string for a well-known GUID
1653 * @param ads connection to ads server
1654 * @param wknguid Well known GUID
1655 * @return org unit string - caller must free
1657 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1660 LDAPMessage
*res
= NULL
;
1661 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1662 **bind_dn_exp
= NULL
;
1663 const char *attrs
[] = {"distinguishedName", NULL
};
1664 int new_ln
, wkn_ln
, bind_ln
, i
;
1666 if (wknguid
== NULL
) {
1670 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1671 DEBUG(1, ("asprintf failed!\n"));
1675 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1676 if (!ADS_ERR_OK(status
)) {
1677 DEBUG(1,("Failed while searching for: %s\n", base
));
1681 if (ads_count_replies(ads
, res
) != 1) {
1685 /* substitute the bind-path from the well-known-guid-search result */
1686 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1691 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1696 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1701 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1703 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1706 new_ln
= wkn_ln
- bind_ln
;
1708 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1713 for (i
=1; i
< new_ln
; i
++) {
1716 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1722 ret
= SMB_STRDUP(s
);
1731 ads_msgfree(ads
, res
);
1732 TALLOC_FREE(wkn_dn
);
1734 ldap_value_free(wkn_dn_exp
);
1737 ldap_value_free(bind_dn_exp
);
1744 * Adds (appends) an item to an attribute array, rather then
1745 * replacing the whole list
1746 * @param ctx An initialized TALLOC_CTX
1747 * @param mods An initialized ADS_MODLIST
1748 * @param name name of the ldap attribute to append to
1749 * @param vals an array of values to add
1750 * @return status of addition
1753 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1754 const char *name
, const char **vals
)
1756 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1757 (const void *) vals
);
1761 * Determines the an account's current KVNO via an LDAP lookup
1762 * @param ads An initialized ADS_STRUCT
1763 * @param account_name the NT samaccountname.
1764 * @return the kvno for the account, or -1 in case of a failure.
1767 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1769 LDAPMessage
*res
= NULL
;
1770 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1772 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1773 char *dn_string
= NULL
;
1774 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1776 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1777 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1780 ret
= ads_search(ads
, &res
, filter
, attrs
);
1782 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1783 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1784 ads_msgfree(ads
, res
);
1788 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1790 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1791 ads_msgfree(ads
, res
);
1794 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1795 TALLOC_FREE(dn_string
);
1797 /* ---------------------------------------------------------
1798 * 0 is returned as a default KVNO from this point on...
1799 * This is done because Windows 2000 does not support key
1800 * version numbers. Chances are that a failure in the next
1801 * step is simply due to Windows 2000 being used for a
1802 * domain controller. */
1805 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1806 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1807 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1808 ads_msgfree(ads
, res
);
1813 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1814 ads_msgfree(ads
, res
);
1819 * Determines the computer account's current KVNO via an LDAP lookup
1820 * @param ads An initialized ADS_STRUCT
1821 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1822 * @return the kvno for the computer account, or -1 in case of a failure.
1825 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1827 char *computer_account
= NULL
;
1830 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1834 kvno
= ads_get_kvno(ads
, computer_account
);
1835 free(computer_account
);
1841 * This clears out all registered spn's for a given hostname
1842 * @param ads An initilaized ADS_STRUCT
1843 * @param machine_name the NetBIOS name of the computer.
1844 * @return 0 upon success, non-zero otherwise.
1847 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1850 LDAPMessage
*res
= NULL
;
1852 const char *servicePrincipalName
[1] = {NULL
};
1853 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1854 char *dn_string
= NULL
;
1856 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1857 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1859 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1860 ads_msgfree(ads
, res
);
1861 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1864 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1865 ctx
= talloc_init("ads_clear_service_principal_names");
1867 ads_msgfree(ads
, res
);
1868 return ADS_ERROR(LDAP_NO_MEMORY
);
1871 if (!(mods
= ads_init_mods(ctx
))) {
1872 talloc_destroy(ctx
);
1873 ads_msgfree(ads
, res
);
1874 return ADS_ERROR(LDAP_NO_MEMORY
);
1876 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1877 if (!ADS_ERR_OK(ret
)) {
1878 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1879 ads_msgfree(ads
, res
);
1880 talloc_destroy(ctx
);
1883 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1885 talloc_destroy(ctx
);
1886 ads_msgfree(ads
, res
);
1887 return ADS_ERROR(LDAP_NO_MEMORY
);
1889 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1890 TALLOC_FREE(dn_string
);
1891 if (!ADS_ERR_OK(ret
)) {
1892 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1894 ads_msgfree(ads
, res
);
1895 talloc_destroy(ctx
);
1899 ads_msgfree(ads
, res
);
1900 talloc_destroy(ctx
);
1905 * This adds a service principal name to an existing computer account
1906 * (found by hostname) in AD.
1907 * @param ads An initialized ADS_STRUCT
1908 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1909 * @param my_fqdn The fully qualified DNS name of the machine
1910 * @param spn A string of the service principal to add, i.e. 'host'
1911 * @return 0 upon sucess, or non-zero if a failure occurs
1914 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1915 const char *my_fqdn
, const char *spn
)
1919 LDAPMessage
*res
= NULL
;
1922 char *dn_string
= NULL
;
1923 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1925 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1926 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1927 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1929 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1930 spn
, machine_name
, ads
->config
.realm
));
1931 ads_msgfree(ads
, res
);
1932 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1935 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1936 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1937 ads_msgfree(ads
, res
);
1938 return ADS_ERROR(LDAP_NO_MEMORY
);
1941 /* add short name spn */
1943 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1944 talloc_destroy(ctx
);
1945 ads_msgfree(ads
, res
);
1946 return ADS_ERROR(LDAP_NO_MEMORY
);
1949 strlower_m(&psp1
[strlen(spn
)]);
1950 servicePrincipalName
[0] = psp1
;
1952 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1953 psp1
, machine_name
));
1956 /* add fully qualified spn */
1958 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1959 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1963 strlower_m(&psp2
[strlen(spn
)]);
1964 servicePrincipalName
[1] = psp2
;
1966 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1967 psp2
, machine_name
));
1969 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1970 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1974 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1975 if (!ADS_ERR_OK(ret
)) {
1976 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1980 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
1981 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1985 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1986 if (!ADS_ERR_OK(ret
)) {
1987 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1993 ads_msgfree(ads
, res
);
1998 * adds a machine account to the ADS server
1999 * @param ads An intialized ADS_STRUCT
2000 * @param machine_name - the NetBIOS machine name of this account.
2001 * @param account_type A number indicating the type of account to create
2002 * @param org_unit The LDAP path in which to place this account
2003 * @return 0 upon success, or non-zero otherwise
2006 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2007 const char *org_unit
)
2010 char *samAccountName
, *controlstr
;
2013 char *machine_escaped
= NULL
;
2015 const char *objectClass
[] = {"top", "person", "organizationalPerson",
2016 "user", "computer", NULL
};
2017 LDAPMessage
*res
= NULL
;
2018 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
2019 UF_DONT_EXPIRE_PASSWD
|\
2020 UF_ACCOUNTDISABLE
);
2022 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2023 return ADS_ERROR(LDAP_NO_MEMORY
);
2025 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2027 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2028 if (!machine_escaped
) {
2032 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2033 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2035 if ( !new_dn
|| !samAccountName
) {
2039 #ifndef ENCTYPE_ARCFOUR_HMAC
2040 acct_control
|= UF_USE_DES_KEY_ONLY
;
2043 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2047 if (!(mods
= ads_init_mods(ctx
))) {
2051 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2052 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2053 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2054 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2056 ret
= ads_gen_add(ads
, new_dn
, mods
);
2059 SAFE_FREE(machine_escaped
);
2060 ads_msgfree(ads
, res
);
2061 talloc_destroy(ctx
);
2067 * move a machine account to another OU on the ADS server
2068 * @param ads - An intialized ADS_STRUCT
2069 * @param machine_name - the NetBIOS machine name of this account.
2070 * @param org_unit - The LDAP path in which to place this account
2071 * @param moved - whether we moved the machine account (optional)
2072 * @return 0 upon success, or non-zero otherwise
2075 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2076 const char *org_unit
, bool *moved
)
2080 LDAPMessage
*res
= NULL
;
2081 char *filter
= NULL
;
2082 char *computer_dn
= NULL
;
2084 char *computer_rdn
= NULL
;
2085 bool need_move
= False
;
2087 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2088 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2092 /* Find pre-existing machine */
2093 rc
= ads_search(ads
, &res
, filter
, NULL
);
2094 if (!ADS_ERR_OK(rc
)) {
2098 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2100 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2104 parent_dn
= ads_parent_dn(computer_dn
);
2105 if (strequal(parent_dn
, org_unit
)) {
2111 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2112 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2116 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2117 org_unit
, 1, NULL
, NULL
);
2118 rc
= ADS_ERROR(ldap_status
);
2121 ads_msgfree(ads
, res
);
2123 TALLOC_FREE(computer_dn
);
2124 SAFE_FREE(computer_rdn
);
2126 if (!ADS_ERR_OK(rc
)) {
2138 dump a binary result from ldap
2140 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2143 for (i
=0; values
[i
]; i
++) {
2144 printf("%s: ", field
);
2145 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2146 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2152 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2155 for (i
=0; values
[i
]; i
++) {
2157 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2160 status
= GUID_from_ndr_blob(&in
, &guid
);
2161 if (NT_STATUS_IS_OK(status
)) {
2162 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2164 printf("%s: INVALID GUID\n", field
);
2170 dump a sid result from ldap
2172 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2175 for (i
=0; values
[i
]; i
++) {
2178 if (!sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
)) {
2181 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2186 dump ntSecurityDescriptor
2188 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2190 TALLOC_CTX
*frame
= talloc_stackframe();
2191 struct security_descriptor
*psd
;
2194 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2195 values
[0]->bv_len
, &psd
);
2196 if (!NT_STATUS_IS_OK(status
)) {
2197 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2198 nt_errstr(status
)));
2204 ads_disp_sd(ads
, talloc_tos(), psd
);
2211 dump a string result from ldap
2213 static void dump_string(const char *field
, char **values
)
2216 for (i
=0; values
[i
]; i
++) {
2217 printf("%s: %s\n", field
, values
[i
]);
2222 dump a field from LDAP on stdout
2226 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2231 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2233 {"objectGUID", False
, dump_guid
},
2234 {"netbootGUID", False
, dump_guid
},
2235 {"nTSecurityDescriptor", False
, dump_sd
},
2236 {"dnsRecord", False
, dump_binary
},
2237 {"objectSid", False
, dump_sid
},
2238 {"tokenGroups", False
, dump_sid
},
2239 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2240 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2241 {"mS-DS-CreatorSID", False
, dump_sid
},
2242 {"msExchMailboxGuid", False
, dump_guid
},
2247 if (!field
) { /* must be end of an entry */
2252 for (i
=0; handlers
[i
].name
; i
++) {
2253 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2254 if (!values
) /* first time, indicate string or not */
2255 return handlers
[i
].string
;
2256 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2260 if (!handlers
[i
].name
) {
2261 if (!values
) /* first time, indicate string conversion */
2263 dump_string(field
, (char **)values
);
2269 * Dump a result from LDAP on stdout
2270 * used for debugging
2271 * @param ads connection to ads server
2272 * @param res Results to dump
2275 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2277 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2281 * Walk through results, calling a function for each entry found.
2282 * The function receives a field name, a berval * array of values,
2283 * and a data area passed through from the start. The function is
2284 * called once with null for field and values at the end of each
2286 * @param ads connection to ads server
2287 * @param res Results to process
2288 * @param fn Function for processing each result
2289 * @param data_area user-defined area to pass to function
2291 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2292 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2297 size_t converted_size
;
2299 if (!(ctx
= talloc_init("ads_process_results")))
2302 for (msg
= ads_first_entry(ads
, res
); msg
;
2303 msg
= ads_next_entry(ads
, msg
)) {
2307 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2308 (LDAPMessage
*)msg
,&b
);
2310 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2311 (LDAPMessage
*)msg
,b
)) {
2312 struct berval
**ber_vals
;
2313 char **str_vals
, **utf8_vals
;
2317 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2320 DEBUG(0,("ads_process_results: "
2321 "pull_utf8_talloc failed: %s",
2325 string
= fn(ads
, field
, NULL
, data_area
);
2328 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2329 (LDAPMessage
*)msg
, field
);
2330 str_vals
= ads_pull_strvals(ctx
,
2331 (const char **) utf8_vals
);
2332 fn(ads
, field
, (void **) str_vals
, data_area
);
2333 ldap_value_free(utf8_vals
);
2335 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2336 (LDAPMessage
*)msg
, field
);
2337 fn(ads
, field
, (void **) ber_vals
, data_area
);
2339 ldap_value_free_len(ber_vals
);
2341 ldap_memfree(utf8_field
);
2344 talloc_free_children(ctx
);
2345 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2348 talloc_destroy(ctx
);
2352 * count how many replies are in a LDAPMessage
2353 * @param ads connection to ads server
2354 * @param res Results to count
2355 * @return number of replies
2357 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2359 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2363 * pull the first entry from a ADS result
2364 * @param ads connection to ads server
2365 * @param res Results of search
2366 * @return first entry from result
2368 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2370 return ldap_first_entry(ads
->ldap
.ld
, res
);
2374 * pull the next entry from a ADS result
2375 * @param ads connection to ads server
2376 * @param res Results of search
2377 * @return next entry from result
2379 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2381 return ldap_next_entry(ads
->ldap
.ld
, res
);
2385 * pull the first message from a ADS result
2386 * @param ads connection to ads server
2387 * @param res Results of search
2388 * @return first message from result
2390 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2392 return ldap_first_message(ads
->ldap
.ld
, res
);
2396 * pull the next message from a ADS result
2397 * @param ads connection to ads server
2398 * @param res Results of search
2399 * @return next message from result
2401 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2403 return ldap_next_message(ads
->ldap
.ld
, res
);
2407 * pull a single string from a ADS result
2408 * @param ads connection to ads server
2409 * @param mem_ctx TALLOC_CTX to use for allocating result string
2410 * @param msg Results of search
2411 * @param field Attribute to retrieve
2412 * @return Result string in talloc context
2414 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2420 size_t converted_size
;
2422 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2426 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2431 ldap_value_free(values
);
2436 * pull an array of strings from a ADS result
2437 * @param ads connection to ads server
2438 * @param mem_ctx TALLOC_CTX to use for allocating result string
2439 * @param msg Results of search
2440 * @param field Attribute to retrieve
2441 * @return Result strings in talloc context
2443 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2444 LDAPMessage
*msg
, const char *field
,
2450 size_t converted_size
;
2452 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2456 *num_values
= ldap_count_values(values
);
2458 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2460 ldap_value_free(values
);
2464 for (i
=0;i
<*num_values
;i
++) {
2465 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2468 ldap_value_free(values
);
2474 ldap_value_free(values
);
2479 * pull an array of strings from a ADS result
2480 * (handle large multivalue attributes with range retrieval)
2481 * @param ads connection to ads server
2482 * @param mem_ctx TALLOC_CTX to use for allocating result string
2483 * @param msg Results of search
2484 * @param field Attribute to retrieve
2485 * @param current_strings strings returned by a previous call to this function
2486 * @param next_attribute The next query should ask for this attribute
2487 * @param num_values How many values did we get this time?
2488 * @param more_values Are there more values to get?
2489 * @return Result strings in talloc context
2491 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2492 TALLOC_CTX
*mem_ctx
,
2493 LDAPMessage
*msg
, const char *field
,
2494 char **current_strings
,
2495 const char **next_attribute
,
2496 size_t *num_strings
,
2500 char *expected_range_attrib
, *range_attr
;
2501 BerElement
*ptr
= NULL
;
2504 size_t num_new_strings
;
2505 unsigned long int range_start
;
2506 unsigned long int range_end
;
2508 /* we might have been given the whole lot anyway */
2509 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2510 *more_strings
= False
;
2514 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2516 /* look for Range result */
2517 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2519 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2520 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2521 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2529 /* nothing here - this field is just empty */
2530 *more_strings
= False
;
2534 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2535 &range_start
, &range_end
) == 2) {
2536 *more_strings
= True
;
2538 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2539 &range_start
) == 1) {
2540 *more_strings
= False
;
2542 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2544 ldap_memfree(range_attr
);
2545 *more_strings
= False
;
2550 if ((*num_strings
) != range_start
) {
2551 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2552 " - aborting range retreival\n",
2553 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2554 ldap_memfree(range_attr
);
2555 *more_strings
= False
;
2559 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2561 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2562 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2563 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2564 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2565 (unsigned long int)num_new_strings
));
2566 ldap_memfree(range_attr
);
2567 *more_strings
= False
;
2571 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2572 *num_strings
+ num_new_strings
);
2574 if (strings
== NULL
) {
2575 ldap_memfree(range_attr
);
2576 *more_strings
= False
;
2580 if (new_strings
&& num_new_strings
) {
2581 memcpy(&strings
[*num_strings
], new_strings
,
2582 sizeof(*new_strings
) * num_new_strings
);
2585 (*num_strings
) += num_new_strings
;
2587 if (*more_strings
) {
2588 *next_attribute
= talloc_asprintf(mem_ctx
,
2593 if (!*next_attribute
) {
2594 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2595 ldap_memfree(range_attr
);
2596 *more_strings
= False
;
2601 ldap_memfree(range_attr
);
2607 * pull a single uint32 from a ADS result
2608 * @param ads connection to ads server
2609 * @param msg Results of search
2610 * @param field Attribute to retrieve
2611 * @param v Pointer to int to store result
2612 * @return boolean inidicating success
2614 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2619 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2623 ldap_value_free(values
);
2627 *v
= atoi(values
[0]);
2628 ldap_value_free(values
);
2633 * pull a single objectGUID from an ADS result
2634 * @param ads connection to ADS server
2635 * @param msg results of search
2636 * @param guid 37-byte area to receive text guid
2637 * @return boolean indicating success
2639 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2644 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2649 status
= GUID_from_ndr_blob(&blob
, guid
);
2650 talloc_free(blob
.data
);
2651 return NT_STATUS_IS_OK(status
);
2656 * pull a single struct dom_sid from a ADS result
2657 * @param ads connection to ads server
2658 * @param msg Results of search
2659 * @param field Attribute to retrieve
2660 * @param sid Pointer to sid to store result
2661 * @return boolean inidicating success
2663 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2664 struct dom_sid
*sid
)
2666 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2670 * pull an array of struct dom_sids from a ADS result
2671 * @param ads connection to ads server
2672 * @param mem_ctx TALLOC_CTX for allocating sid array
2673 * @param msg Results of search
2674 * @param field Attribute to retrieve
2675 * @param sids pointer to sid array to allocate
2676 * @return the count of SIDs pulled
2678 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2679 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2681 struct berval
**values
;
2685 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2690 for (i
=0; values
[i
]; i
++)
2694 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2696 ldap_value_free_len(values
);
2704 for (i
=0; values
[i
]; i
++) {
2705 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2707 DEBUG(10, ("pulling SID: %s\n",
2708 sid_string_dbg(&(*sids
)[count
])));
2713 ldap_value_free_len(values
);
2718 * pull a struct security_descriptor from a ADS result
2719 * @param ads connection to ads server
2720 * @param mem_ctx TALLOC_CTX for allocating sid array
2721 * @param msg Results of search
2722 * @param field Attribute to retrieve
2723 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2724 * @return boolean inidicating success
2726 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2727 LDAPMessage
*msg
, const char *field
,
2728 struct security_descriptor
**sd
)
2730 struct berval
**values
;
2733 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2735 if (!values
) return false;
2739 status
= unmarshall_sec_desc(mem_ctx
,
2740 (uint8
*)values
[0]->bv_val
,
2741 values
[0]->bv_len
, sd
);
2742 if (!NT_STATUS_IS_OK(status
)) {
2743 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2744 nt_errstr(status
)));
2749 ldap_value_free_len(values
);
2754 * in order to support usernames longer than 21 characters we need to
2755 * use both the sAMAccountName and the userPrincipalName attributes
2756 * It seems that not all users have the userPrincipalName attribute set
2758 * @param ads connection to ads server
2759 * @param mem_ctx TALLOC_CTX for allocating sid array
2760 * @param msg Results of search
2761 * @return the username
2763 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2769 /* lookup_name() only works on the sAMAccountName to
2770 returning the username portion of userPrincipalName
2771 breaks winbindd_getpwnam() */
2773 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2774 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2779 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2784 * find the update serial number - this is the core of the ldap cache
2785 * @param ads connection to ads server
2786 * @param ads connection to ADS server
2787 * @param usn Pointer to retrieved update serial number
2788 * @return status of search
2790 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2792 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2796 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2797 if (!ADS_ERR_OK(status
))
2800 if (ads_count_replies(ads
, res
) != 1) {
2801 ads_msgfree(ads
, res
);
2802 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2805 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2806 ads_msgfree(ads
, res
);
2807 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2810 ads_msgfree(ads
, res
);
2814 /* parse a ADS timestring - typical string is
2815 '20020917091222.0Z0' which means 09:12.22 17th September
2817 static time_t ads_parse_time(const char *str
)
2823 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2824 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2825 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2834 /********************************************************************
2835 ********************************************************************/
2837 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2839 const char *attrs
[] = {"currentTime", NULL
};
2844 ADS_STRUCT
*ads_s
= ads
;
2846 if (!(ctx
= talloc_init("ads_current_time"))) {
2847 return ADS_ERROR(LDAP_NO_MEMORY
);
2850 /* establish a new ldap tcp session if necessary */
2852 if ( !ads
->ldap
.ld
) {
2853 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2854 ads
->server
.ldap_server
)) == NULL
)
2858 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2859 status
= ads_connect( ads_s
);
2860 if ( !ADS_ERR_OK(status
))
2864 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2865 if (!ADS_ERR_OK(status
)) {
2869 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2871 ads_msgfree(ads_s
, res
);
2872 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2876 /* but save the time and offset in the original ADS_STRUCT */
2878 ads
->config
.current_time
= ads_parse_time(timestr
);
2880 if (ads
->config
.current_time
!= 0) {
2881 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2882 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
2885 ads_msgfree(ads
, res
);
2887 status
= ADS_SUCCESS
;
2890 /* free any temporary ads connections */
2891 if ( ads_s
!= ads
) {
2892 ads_destroy( &ads_s
);
2894 talloc_destroy(ctx
);
2899 /********************************************************************
2900 ********************************************************************/
2902 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2904 const char *attrs
[] = {"domainFunctionality", NULL
};
2907 ADS_STRUCT
*ads_s
= ads
;
2909 *val
= DS_DOMAIN_FUNCTION_2000
;
2911 /* establish a new ldap tcp session if necessary */
2913 if ( !ads
->ldap
.ld
) {
2914 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2915 ads
->server
.ldap_server
)) == NULL
)
2917 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2920 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2921 status
= ads_connect( ads_s
);
2922 if ( !ADS_ERR_OK(status
))
2926 /* If the attribute does not exist assume it is a Windows 2000
2927 functional domain */
2929 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2930 if (!ADS_ERR_OK(status
)) {
2931 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2932 status
= ADS_SUCCESS
;
2937 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2938 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2940 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2943 ads_msgfree(ads
, res
);
2946 /* free any temporary ads connections */
2947 if ( ads_s
!= ads
) {
2948 ads_destroy( &ads_s
);
2955 * find the domain sid for our domain
2956 * @param ads connection to ads server
2957 * @param sid Pointer to domain sid
2958 * @return status of search
2960 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
2962 const char *attrs
[] = {"objectSid", NULL
};
2966 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2968 if (!ADS_ERR_OK(rc
)) return rc
;
2969 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2970 ads_msgfree(ads
, res
);
2971 return ADS_ERROR_SYSTEM(ENOENT
);
2973 ads_msgfree(ads
, res
);
2979 * find our site name
2980 * @param ads connection to ads server
2981 * @param mem_ctx Pointer to talloc context
2982 * @param site_name Pointer to the sitename
2983 * @return status of search
2985 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2989 const char *dn
, *service_name
;
2990 const char *attrs
[] = { "dsServiceName", NULL
};
2992 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2993 if (!ADS_ERR_OK(status
)) {
2997 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2998 if (service_name
== NULL
) {
2999 ads_msgfree(ads
, res
);
3000 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3003 ads_msgfree(ads
, res
);
3005 /* go up three levels */
3006 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3008 return ADS_ERROR(LDAP_NO_MEMORY
);
3011 *site_name
= talloc_strdup(mem_ctx
, dn
);
3012 if (*site_name
== NULL
) {
3013 return ADS_ERROR(LDAP_NO_MEMORY
);
3018 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3023 * find the site dn where a machine resides
3024 * @param ads connection to ads server
3025 * @param mem_ctx Pointer to talloc context
3026 * @param computer_name name of the machine
3027 * @param site_name Pointer to the sitename
3028 * @return status of search
3030 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3034 const char *parent
, *filter
;
3035 char *config_context
= NULL
;
3038 /* shortcut a query */
3039 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3040 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3043 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3044 if (!ADS_ERR_OK(status
)) {
3048 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3049 if (filter
== NULL
) {
3050 return ADS_ERROR(LDAP_NO_MEMORY
);
3053 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3054 filter
, NULL
, &res
);
3055 if (!ADS_ERR_OK(status
)) {
3059 if (ads_count_replies(ads
, res
) != 1) {
3060 ads_msgfree(ads
, res
);
3061 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3064 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3066 ads_msgfree(ads
, res
);
3067 return ADS_ERROR(LDAP_NO_MEMORY
);
3070 /* go up three levels */
3071 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3072 if (parent
== NULL
) {
3073 ads_msgfree(ads
, res
);
3075 return ADS_ERROR(LDAP_NO_MEMORY
);
3078 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3079 if (*site_dn
== NULL
) {
3080 ads_msgfree(ads
, res
);
3082 return ADS_ERROR(LDAP_NO_MEMORY
);
3086 ads_msgfree(ads
, res
);
3092 * get the upn suffixes for a domain
3093 * @param ads connection to ads server
3094 * @param mem_ctx Pointer to talloc context
3095 * @param suffixes Pointer to an array of suffixes
3096 * @param num_suffixes Pointer to the number of suffixes
3097 * @return status of search
3099 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3104 char *config_context
= NULL
;
3105 const char *attrs
[] = { "uPNSuffixes", NULL
};
3107 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3108 if (!ADS_ERR_OK(status
)) {
3112 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3114 return ADS_ERROR(LDAP_NO_MEMORY
);
3117 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3118 if (!ADS_ERR_OK(status
)) {
3122 if (ads_count_replies(ads
, res
) != 1) {
3123 ads_msgfree(ads
, res
);
3124 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3127 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3128 if ((*suffixes
) == NULL
) {
3129 ads_msgfree(ads
, res
);
3130 return ADS_ERROR(LDAP_NO_MEMORY
);
3133 ads_msgfree(ads
, res
);
3139 * get the joinable ous for a domain
3140 * @param ads connection to ads server
3141 * @param mem_ctx Pointer to talloc context
3142 * @param ous Pointer to an array of ous
3143 * @param num_ous Pointer to the number of ous
3144 * @return status of search
3146 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3147 TALLOC_CTX
*mem_ctx
,
3152 LDAPMessage
*res
= NULL
;
3153 LDAPMessage
*msg
= NULL
;
3154 const char *attrs
[] = { "dn", NULL
};
3157 status
= ads_search(ads
, &res
,
3158 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3160 if (!ADS_ERR_OK(status
)) {
3164 count
= ads_count_replies(ads
, res
);
3166 ads_msgfree(ads
, res
);
3167 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3170 for (msg
= ads_first_entry(ads
, res
); msg
;
3171 msg
= ads_next_entry(ads
, msg
)) {
3175 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3177 ads_msgfree(ads
, res
);
3178 return ADS_ERROR(LDAP_NO_MEMORY
);
3181 if (!add_string_to_array(mem_ctx
, dn
,
3182 (const char ***)ous
,
3185 ads_msgfree(ads
, res
);
3186 return ADS_ERROR(LDAP_NO_MEMORY
);
3192 ads_msgfree(ads
, res
);
3199 * pull a struct dom_sid from an extended dn string
3200 * @param mem_ctx TALLOC_CTX
3201 * @param extended_dn string
3202 * @param flags string type of extended_dn
3203 * @param sid pointer to a struct dom_sid
3204 * @return NT_STATUS_OK on success,
3205 * NT_INVALID_PARAMETER on error,
3206 * NT_STATUS_NOT_FOUND if no SID present
3208 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3209 const char *extended_dn
,
3210 enum ads_extended_dn_flags flags
,
3211 struct dom_sid
*sid
)
3216 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3219 /* otherwise extended_dn gets stripped off */
3220 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3221 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3224 * ADS_EXTENDED_DN_HEX_STRING:
3225 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3227 * ADS_EXTENDED_DN_STRING (only with w2k3):
3228 * <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
3230 * Object with no SID, such as an Exchange Public Folder
3231 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3234 p
= strchr(dn
, ';');
3236 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3239 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3240 DEBUG(5,("No SID present in extended dn\n"));
3241 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3244 p
+= strlen(";<SID=");
3248 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3253 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3257 case ADS_EXTENDED_DN_STRING
:
3258 if (!string_to_sid(sid
, p
)) {
3259 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3262 case ADS_EXTENDED_DN_HEX_STRING
: {
3266 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3268 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3271 if (!sid_parse(buf
, buf_len
, sid
)) {
3272 DEBUG(10,("failed to parse sid\n"));
3273 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3278 DEBUG(10,("unknown extended dn format\n"));
3279 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3282 return ADS_ERROR_NT(NT_STATUS_OK
);
3285 /********************************************************************
3286 ********************************************************************/
3288 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3290 LDAPMessage
*res
= NULL
;
3295 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3296 if (!ADS_ERR_OK(status
)) {
3297 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3298 lp_netbios_name()));
3302 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3303 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3307 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3308 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3312 ads_msgfree(ads
, res
);
3317 /********************************************************************
3318 ********************************************************************/
3320 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3322 LDAPMessage
*res
= NULL
;
3327 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3328 if (!ADS_ERR_OK(status
)) {
3329 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3330 lp_netbios_name()));
3334 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3335 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3339 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3340 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3344 ads_msgfree(ads
, res
);
3349 /********************************************************************
3350 ********************************************************************/
3352 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3354 LDAPMessage
*res
= NULL
;
3359 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3360 if (!ADS_ERR_OK(status
)) {
3361 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3362 lp_netbios_name()));
3366 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3367 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3371 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3372 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3376 ads_msgfree(ads
, res
);
3383 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3386 * Join a machine to a realm
3387 * Creates the machine account and sets the machine password
3388 * @param ads connection to ads server
3389 * @param machine name of host to add
3390 * @param org_unit Organizational unit to place machine in
3391 * @return status of join
3393 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3394 uint32 account_type
, const char *org_unit
)
3397 LDAPMessage
*res
= NULL
;
3400 /* machine name must be lowercase */
3401 machine
= SMB_STRDUP(machine_name
);
3402 strlower_m(machine
);
3405 status = ads_find_machine_acct(ads, (void **)&res, machine);
3406 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3407 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3408 status = ads_leave_realm(ads, machine);
3409 if (!ADS_ERR_OK(status)) {
3410 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3411 machine, ads->config.realm));
3416 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3417 if (!ADS_ERR_OK(status
)) {
3418 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3423 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3424 if (!ADS_ERR_OK(status
)) {
3425 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3431 ads_msgfree(ads
, res
);
3438 * Delete a machine from the realm
3439 * @param ads connection to ads server
3440 * @param hostname Machine to remove
3441 * @return status of delete
3443 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3448 char *hostnameDN
, *host
;
3450 LDAPControl ldap_control
;
3451 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3453 pldap_control
[0] = &ldap_control
;
3454 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3455 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3457 /* hostname must be lowercase */
3458 host
= SMB_STRDUP(hostname
);
3461 status
= ads_find_machine_acct(ads
, &res
, host
);
3462 if (!ADS_ERR_OK(status
)) {
3463 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3468 msg
= ads_first_entry(ads
, res
);
3471 return ADS_ERROR_SYSTEM(ENOENT
);
3474 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3475 if (hostnameDN
== NULL
) {
3477 return ADS_ERROR_SYSTEM(ENOENT
);
3480 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3482 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3484 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3487 if (rc
!= LDAP_SUCCESS
) {
3488 const char *attrs
[] = { "cn", NULL
};
3489 LDAPMessage
*msg_sub
;
3491 /* we only search with scope ONE, we do not expect any further
3492 * objects to be created deeper */
3494 status
= ads_do_search_retry(ads
, hostnameDN
,
3495 LDAP_SCOPE_ONELEVEL
,
3496 "(objectclass=*)", attrs
, &res
);
3498 if (!ADS_ERR_OK(status
)) {
3500 TALLOC_FREE(hostnameDN
);
3504 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3505 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3509 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3511 TALLOC_FREE(hostnameDN
);
3512 return ADS_ERROR(LDAP_NO_MEMORY
);
3515 status
= ads_del_dn(ads
, dn
);
3516 if (!ADS_ERR_OK(status
)) {
3517 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3520 TALLOC_FREE(hostnameDN
);
3527 /* there should be no subordinate objects anymore */
3528 status
= ads_do_search_retry(ads
, hostnameDN
,
3529 LDAP_SCOPE_ONELEVEL
,
3530 "(objectclass=*)", attrs
, &res
);
3532 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3534 TALLOC_FREE(hostnameDN
);
3538 /* delete hostnameDN now */
3539 status
= ads_del_dn(ads
, hostnameDN
);
3540 if (!ADS_ERR_OK(status
)) {
3542 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3543 TALLOC_FREE(hostnameDN
);
3548 TALLOC_FREE(hostnameDN
);
3550 status
= ads_find_machine_acct(ads
, &res
, host
);
3551 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3552 DEBUG(3, ("Failed to remove host account.\n"));
3562 * pull all token-sids from an LDAP dn
3563 * @param ads connection to ads server
3564 * @param mem_ctx TALLOC_CTX for allocating sid array
3565 * @param dn of LDAP object
3566 * @param user_sid pointer to struct dom_sid (objectSid)
3567 * @param primary_group_sid pointer to struct dom_sid (self composed)
3568 * @param sids pointer to sid array to allocate
3569 * @param num_sids counter of SIDs pulled
3570 * @return status of token query
3572 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3573 TALLOC_CTX
*mem_ctx
,
3575 struct dom_sid
*user_sid
,
3576 struct dom_sid
*primary_group_sid
,
3577 struct dom_sid
**sids
,
3581 LDAPMessage
*res
= NULL
;
3583 size_t tmp_num_sids
;
3584 struct dom_sid
*tmp_sids
;
3585 struct dom_sid tmp_user_sid
;
3586 struct dom_sid tmp_primary_group_sid
;
3588 const char *attrs
[] = {
3595 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3596 if (!ADS_ERR_OK(status
)) {
3600 count
= ads_count_replies(ads
, res
);
3602 ads_msgfree(ads
, res
);
3603 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3606 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3607 ads_msgfree(ads
, res
);
3608 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3611 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3612 ads_msgfree(ads
, res
);
3613 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3617 /* hack to compose the primary group sid without knowing the
3620 struct dom_sid domsid
;
3622 sid_copy(&domsid
, &tmp_user_sid
);
3624 if (!sid_split_rid(&domsid
, NULL
)) {
3625 ads_msgfree(ads
, res
);
3626 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3629 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3630 ads_msgfree(ads
, res
);
3631 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3635 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3637 if (tmp_num_sids
== 0 || !tmp_sids
) {
3638 ads_msgfree(ads
, res
);
3639 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3643 *num_sids
= tmp_num_sids
;
3651 *user_sid
= tmp_user_sid
;
3654 if (primary_group_sid
) {
3655 *primary_group_sid
= tmp_primary_group_sid
;
3658 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3660 ads_msgfree(ads
, res
);
3661 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3665 * Find a sAMAccoutName in LDAP
3666 * @param ads connection to ads server
3667 * @param mem_ctx TALLOC_CTX for allocating sid array
3668 * @param samaccountname to search
3669 * @param uac_ret uint32 pointer userAccountControl attribute value
3670 * @param dn_ret pointer to dn
3671 * @return status of token query
3673 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3674 TALLOC_CTX
*mem_ctx
,
3675 const char *samaccountname
,
3677 const char **dn_ret
)
3680 const char *attrs
[] = { "userAccountControl", NULL
};
3682 LDAPMessage
*res
= NULL
;
3686 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3688 if (filter
== NULL
) {
3689 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3693 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3695 filter
, attrs
, &res
);
3697 if (!ADS_ERR_OK(status
)) {
3701 if (ads_count_replies(ads
, res
) != 1) {
3702 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3706 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3708 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3712 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3713 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3722 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3724 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3730 ads_msgfree(ads
, res
);
3736 * find our configuration path
3737 * @param ads connection to ads server
3738 * @param mem_ctx Pointer to talloc context
3739 * @param config_path Pointer to the config path
3740 * @return status of search
3742 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3743 TALLOC_CTX
*mem_ctx
,
3747 LDAPMessage
*res
= NULL
;
3748 const char *config_context
= NULL
;
3749 const char *attrs
[] = { "configurationNamingContext", NULL
};
3751 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3752 "(objectclass=*)", attrs
, &res
);
3753 if (!ADS_ERR_OK(status
)) {
3757 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3758 "configurationNamingContext");
3759 ads_msgfree(ads
, res
);
3760 if (!config_context
) {
3761 return ADS_ERROR(LDAP_NO_MEMORY
);
3765 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3766 if (!*config_path
) {
3767 return ADS_ERROR(LDAP_NO_MEMORY
);
3771 return ADS_ERROR(LDAP_SUCCESS
);
3775 * find the displayName of an extended right
3776 * @param ads connection to ads server
3777 * @param config_path The config path
3778 * @param mem_ctx Pointer to talloc context
3779 * @param GUID struct of the rightsGUID
3780 * @return status of search
3782 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3783 const char *config_path
,
3784 TALLOC_CTX
*mem_ctx
,
3785 const struct GUID
*rights_guid
)
3788 LDAPMessage
*res
= NULL
;
3790 const char *attrs
[] = { "displayName", NULL
};
3791 const char *result
= NULL
;
3794 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3798 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3799 GUID_string(mem_ctx
, rights_guid
));
3804 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3809 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3811 if (!ADS_ERR_OK(rc
)) {
3815 if (ads_count_replies(ads
, res
) != 1) {
3819 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3822 ads_msgfree(ads
, res
);
3827 * verify or build and verify an account ou
3828 * @param mem_ctx Pointer to talloc context
3829 * @param ads connection to ads server
3831 * @return status of search
3834 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3836 const char **account_ou
)
3842 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
3844 ldap_value_free(exploded_dn
);
3848 ou_string
= ads_ou_string(ads
, *account_ou
);
3850 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3853 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3854 ads
->config
.bind_path
);
3855 SAFE_FREE(ou_string
);
3858 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3861 exploded_dn
= ldap_explode_dn(name
, 0);
3863 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3865 ldap_value_free(exploded_dn
);