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 if (!strupper_m(ads
->config
.realm
)) {
306 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
307 if (*cldap_reply
.server_site
) {
308 ads
->config
.server_site_name
=
309 SMB_STRDUP(cldap_reply
.server_site
);
311 if (*cldap_reply
.client_site
) {
312 ads
->config
.client_site_name
=
313 SMB_STRDUP(cldap_reply
.client_site
);
315 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain_name
);
317 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
320 /* Store our site name. */
321 sitename_store( cldap_reply
.domain_name
, cldap_reply
.client_site
);
322 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
332 /**********************************************************************
333 Try to find an AD dc using our internal name resolution routines
334 Try the realm first and then then workgroup name if netbios is not
336 **********************************************************************/
338 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
340 const char *c_domain
;
343 struct ip_service
*ip_list
;
346 bool got_realm
= False
;
347 bool use_own_domain
= False
;
349 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
351 /* if the realm and workgroup are both empty, assume they are ours */
354 c_realm
= ads
->server
.realm
;
356 if ( !c_realm
|| !*c_realm
) {
357 /* special case where no realm and no workgroup means our own */
358 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
359 use_own_domain
= True
;
360 c_realm
= lp_realm();
364 if (c_realm
&& *c_realm
)
367 /* we need to try once with the realm name and fallback to the
368 netbios domain name if we fail (if netbios has not been disabled */
370 if ( !got_realm
&& !lp_disable_netbios() ) {
371 c_realm
= ads
->server
.workgroup
;
372 if (!c_realm
|| !*c_realm
) {
373 if ( use_own_domain
)
374 c_realm
= lp_workgroup();
378 if ( !c_realm
|| !*c_realm
) {
379 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
381 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
384 if ( use_own_domain
) {
385 c_domain
= lp_workgroup();
387 c_domain
= ads
->server
.workgroup
;
394 * In case of LDAP we use get_dc_name() as that
395 * creates the custom krb5.conf file
397 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
399 struct sockaddr_storage ip_out
;
401 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
402 (got_realm
? "realm" : "domain"), realm
));
404 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
406 * we call ads_try_connect() to fill in the
407 * ads->config details
409 if (ads_try_connect(ads
, srv_name
, false)) {
414 return NT_STATUS_NO_LOGON_SERVERS
;
417 sitename
= sitename_fetch(realm
);
421 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
422 (got_realm
? "realm" : "domain"), realm
));
424 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
425 if (!NT_STATUS_IS_OK(status
)) {
426 /* fall back to netbios if we can */
427 if ( got_realm
&& !lp_disable_netbios() ) {
436 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
437 for ( i
=0; i
<count
; i
++ ) {
438 char server
[INET6_ADDRSTRLEN
];
440 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
442 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
446 /* realm in this case is a workgroup name. We need
447 to ignore any IP addresses in the negative connection
448 cache that match ip addresses returned in the ad realm
449 case. It sucks that I have to reproduce the logic above... */
450 c_realm
= ads
->server
.realm
;
451 if ( !c_realm
|| !*c_realm
) {
452 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
453 c_realm
= lp_realm();
456 if (c_realm
&& *c_realm
&&
457 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
458 /* Ensure we add the workgroup name for this
459 IP address as negative too. */
460 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
465 if ( ads_try_connect(ads
, server
, false) ) {
471 /* keep track of failures */
472 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
477 /* In case we failed to contact one of our closest DC on our site we
478 * need to try to find another DC, retry with a site-less SRV DNS query
482 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
483 "trying to find another DC\n", sitename
));
485 namecache_delete(realm
, 0x1C);
489 return NT_STATUS_NO_LOGON_SERVERS
;
492 /*********************************************************************
493 *********************************************************************/
495 static NTSTATUS
ads_lookup_site(void)
497 ADS_STRUCT
*ads
= NULL
;
498 ADS_STATUS ads_status
;
499 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
501 ads
= ads_init(lp_realm(), NULL
, NULL
);
503 return NT_STATUS_NO_MEMORY
;
506 /* The NO_BIND here will find a DC and set the client site
507 but not establish the TCP connection */
509 ads
->auth
.flags
= ADS_AUTH_NO_BIND
;
510 ads_status
= ads_connect(ads
);
511 if (!ADS_ERR_OK(ads_status
)) {
512 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
513 ads_errstr(ads_status
)));
515 nt_status
= ads_ntstatus(ads_status
);
524 /*********************************************************************
525 *********************************************************************/
527 static const char* host_dns_domain(const char *fqdn
)
529 const char *p
= fqdn
;
531 /* go to next char following '.' */
533 if ((p
= strchr_m(fqdn
, '.')) != NULL
) {
542 * Connect to the Global Catalog server
543 * @param ads Pointer to an existing ADS_STRUCT
544 * @return status of connection
546 * Simple wrapper around ads_connect() that fills in the
547 * GC ldap server information
550 ADS_STATUS
ads_connect_gc(ADS_STRUCT
*ads
)
552 TALLOC_CTX
*frame
= talloc_stackframe();
553 struct dns_rr_srv
*gcs_list
;
555 const char *realm
= ads
->server
.realm
;
556 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
557 ADS_STATUS ads_status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
560 char *sitename
= NULL
;
561 const char *dns_hosts_file
;
566 if ((sitename
= sitename_fetch(realm
)) == NULL
) {
568 sitename
= sitename_fetch(realm
);
571 dns_hosts_file
= lp_parm_const_string(-1, "resolv", "host file", NULL
);
573 /* We try once with a sitename and once without
574 (unless we don't have a sitename and then we're
577 if (sitename
== NULL
)
580 nt_status
= ads_dns_query_gcs(frame
, dns_hosts_file
,
582 &gcs_list
, &num_gcs
);
586 if (!NT_STATUS_IS_OK(nt_status
)) {
587 ads_status
= ADS_ERROR_NT(nt_status
);
591 /* Loop until we get a successful connection or have gone
592 through them all. When connecting a GC server, make sure that
593 the realm is the server's DNS name and not the forest root */
595 for (i
=0; i
<num_gcs
; i
++) {
596 ads
->server
.gc
= true;
597 ads
->server
.ldap_server
= SMB_STRDUP(gcs_list
[i
].hostname
);
598 ads
->server
.realm
= SMB_STRDUP(host_dns_domain(ads
->server
.ldap_server
));
599 ads_status
= ads_connect(ads
);
600 if (ADS_ERR_OK(ads_status
)) {
601 /* Reset the bind_dn to "". A Global Catalog server
602 may host multiple domain trees in a forest.
603 Windows 2003 GC server will accept "" as the search
604 path to imply search all domain trees in the forest */
606 SAFE_FREE(ads
->config
.bind_path
);
607 ads
->config
.bind_path
= SMB_STRDUP("");
612 SAFE_FREE(ads
->server
.ldap_server
);
613 SAFE_FREE(ads
->server
.realm
);
616 TALLOC_FREE(gcs_list
);
622 talloc_destroy(frame
);
629 * Connect to the LDAP server
630 * @param ads Pointer to an existing ADS_STRUCT
631 * @return status of connection
633 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
635 int version
= LDAP_VERSION3
;
638 char addr
[INET6_ADDRSTRLEN
];
640 ZERO_STRUCT(ads
->ldap
);
641 ads
->ldap
.last_attempt
= time_mono(NULL
);
642 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
644 /* try with a user specified server */
646 if (DEBUGLEVEL
>= 11) {
647 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
648 DEBUG(11,("ads_connect: entering\n"));
649 DEBUGADD(11,("%s\n", s
));
653 if (ads
->server
.ldap_server
)
655 if (ads_try_connect(ads
, ads
->server
.ldap_server
, ads
->server
.gc
)) {
659 /* The choice of which GC use is handled one level up in
660 ads_connect_gc(). If we continue on from here with
661 ads_find_dc() we will get GC searches on port 389 which
662 doesn't work. --jerry */
664 if (ads
->server
.gc
== true) {
665 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
669 ntstatus
= ads_find_dc(ads
);
670 if (NT_STATUS_IS_OK(ntstatus
)) {
674 status
= ADS_ERROR_NT(ntstatus
);
679 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
680 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
682 if (!ads
->auth
.user_name
) {
683 /* Must use the userPrincipalName value here or sAMAccountName
684 and not servicePrincipalName; found by Guenther Deschner */
686 if (asprintf(&ads
->auth
.user_name
, "%s$", lp_netbios_name() ) == -1) {
687 DEBUG(0,("ads_connect: asprintf fail.\n"));
688 ads
->auth
.user_name
= NULL
;
692 if (!ads
->auth
.realm
) {
693 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
696 if (!ads
->auth
.kdc_server
) {
697 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
698 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
701 /* If the caller() requested no LDAP bind, then we are done */
703 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
704 status
= ADS_SUCCESS
;
708 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
709 if (!ads
->ldap
.mem_ctx
) {
710 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
714 /* Otherwise setup the TCP LDAP session */
716 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
718 ads
->ldap
.port
, lp_ldap_timeout());
719 if (ads
->ldap
.ld
== NULL
) {
720 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
723 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
725 /* cache the successful connection for workgroup and realm */
726 if (ads_closest_dc(ads
)) {
727 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
728 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
731 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
733 if ( lp_ldap_ssl_ads() ) {
734 status
= ADS_ERROR(smbldap_start_tls(ads
->ldap
.ld
, version
));
735 if (!ADS_ERR_OK(status
)) {
740 /* fill in the current time and offsets */
742 status
= ads_current_time( ads
);
743 if ( !ADS_ERR_OK(status
) ) {
747 /* Now do the bind */
749 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
750 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
754 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
755 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
759 status
= ads_sasl_bind(ads
);
762 if (DEBUGLEVEL
>= 11) {
763 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
764 DEBUG(11,("ads_connect: leaving with: %s\n",
765 ads_errstr(status
)));
766 DEBUGADD(11,("%s\n", s
));
774 * Connect to the LDAP server using given credentials
775 * @param ads Pointer to an existing ADS_STRUCT
776 * @return status of connection
778 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
780 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
782 return ads_connect(ads
);
786 * Disconnect the LDAP server
787 * @param ads Pointer to an existing ADS_STRUCT
789 void ads_disconnect(ADS_STRUCT
*ads
)
792 ldap_unbind(ads
->ldap
.ld
);
795 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
796 ads
->ldap
.wrap_ops
->disconnect(ads
);
798 if (ads
->ldap
.mem_ctx
) {
799 talloc_free(ads
->ldap
.mem_ctx
);
801 ZERO_STRUCT(ads
->ldap
);
805 Duplicate a struct berval into talloc'ed memory
807 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
809 struct berval
*value
;
811 if (!in_val
) return NULL
;
813 value
= talloc_zero(ctx
, struct berval
);
816 if (in_val
->bv_len
== 0) return value
;
818 value
->bv_len
= in_val
->bv_len
;
819 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
825 Make a values list out of an array of (struct berval *)
827 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
828 const struct berval
**in_vals
)
830 struct berval
**values
;
833 if (!in_vals
) return NULL
;
834 for (i
=0; in_vals
[i
]; i
++)
836 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
837 if (!values
) return NULL
;
839 for (i
=0; in_vals
[i
]; i
++) {
840 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
846 UTF8-encode a values list out of an array of (char *)
848 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
854 if (!in_vals
) return NULL
;
855 for (i
=0; in_vals
[i
]; i
++)
857 values
= talloc_zero_array(ctx
, char *, i
+1);
858 if (!values
) return NULL
;
860 for (i
=0; in_vals
[i
]; i
++) {
861 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
870 Pull a (char *) array out of a UTF8-encoded values list
872 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
876 size_t converted_size
;
878 if (!in_vals
) return NULL
;
879 for (i
=0; in_vals
[i
]; i
++)
881 values
= talloc_zero_array(ctx
, char *, i
+1);
882 if (!values
) return NULL
;
884 for (i
=0; in_vals
[i
]; i
++) {
885 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
887 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
888 "%s", strerror(errno
)));
895 * Do a search with paged results. cookie must be null on the first
896 * call, and then returned on each subsequent call. It will be null
897 * again when the entire search is complete
898 * @param ads connection to ads server
899 * @param bind_path Base dn for the search
900 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
901 * @param expr Search expression - specified in local charset
902 * @param attrs Attributes to retrieve - specified in utf8 or ascii
903 * @param res ** which will contain results - free res* with ads_msgfree()
904 * @param count Number of entries retrieved on this page
905 * @param cookie The paged results cookie to be returned on subsequent calls
906 * @return status of search
908 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
909 const char *bind_path
,
910 int scope
, const char *expr
,
911 const char **attrs
, void *args
,
913 int *count
, struct berval
**cookie
)
916 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
917 size_t converted_size
;
918 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
919 BerElement
*cookie_be
= NULL
;
920 struct berval
*cookie_bv
= NULL
;
921 BerElement
*ext_be
= NULL
;
922 struct berval
*ext_bv
= NULL
;
925 ads_control
*external_control
= (ads_control
*) args
;
929 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
930 return ADS_ERROR(LDAP_NO_MEMORY
);
932 /* 0 means the conversion worked but the result was empty
933 so we only fail if it's -1. In any case, it always
934 at least nulls out the dest */
935 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
936 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
942 if (!attrs
|| !(*attrs
))
945 /* This would be the utf8-encoded version...*/
946 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
947 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
953 /* Paged results only available on ldap v3 or later */
954 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
955 if (version
< LDAP_VERSION3
) {
956 rc
= LDAP_NOT_SUPPORTED
;
960 cookie_be
= ber_alloc_t(LBER_USE_DER
);
962 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
963 ber_bvfree(*cookie
); /* don't need it from last time */
966 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
968 ber_flatten(cookie_be
, &cookie_bv
);
969 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
970 PagedResults
.ldctl_iscritical
= (char) 1;
971 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
972 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
974 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
975 NoReferrals
.ldctl_iscritical
= (char) 0;
976 NoReferrals
.ldctl_value
.bv_len
= 0;
977 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
979 if (external_control
&&
980 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
981 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
983 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
984 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
986 /* win2k does not accept a ldctl_value beeing passed in */
988 if (external_control
->val
!= 0) {
990 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
995 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
999 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1000 rc
= LDAP_NO_MEMORY
;
1004 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1005 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1008 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1009 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1012 controls
[0] = &NoReferrals
;
1013 controls
[1] = &PagedResults
;
1014 controls
[2] = &ExternalCtrl
;
1018 controls
[0] = &NoReferrals
;
1019 controls
[1] = &PagedResults
;
1023 /* we need to disable referrals as the openldap libs don't
1024 handle them and paged results at the same time. Using them
1025 together results in the result record containing the server
1026 page control being removed from the result list (tridge/jmcd)
1028 leaving this in despite the control that says don't generate
1029 referrals, in case the server doesn't support it (jmcd)
1031 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1033 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1034 search_attrs
, 0, controls
,
1035 NULL
, LDAP_NO_LIMIT
,
1036 (LDAPMessage
**)res
);
1038 ber_free(cookie_be
, 1);
1039 ber_bvfree(cookie_bv
);
1042 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1043 ldap_err2string(rc
)));
1047 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1048 NULL
, &rcontrols
, 0);
1054 for (i
=0; rcontrols
[i
]; i
++) {
1055 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1056 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1057 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1059 /* the berval is the cookie, but must be freed when
1061 if (cookie_bv
->bv_len
) /* still more to do */
1062 *cookie
=ber_bvdup(cookie_bv
);
1065 ber_bvfree(cookie_bv
);
1066 ber_free(cookie_be
, 1);
1070 ldap_controls_free(rcontrols
);
1073 talloc_destroy(ctx
);
1076 ber_free(ext_be
, 1);
1083 /* if/when we decide to utf8-encode attrs, take out this next line */
1084 TALLOC_FREE(search_attrs
);
1086 return ADS_ERROR(rc
);
1089 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1090 int scope
, const char *expr
,
1091 const char **attrs
, LDAPMessage
**res
,
1092 int *count
, struct berval
**cookie
)
1094 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1099 * Get all results for a search. This uses ads_do_paged_search() to return
1100 * all entries in a large search.
1101 * @param ads connection to ads server
1102 * @param bind_path Base dn for the search
1103 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1104 * @param expr Search expression
1105 * @param attrs Attributes to retrieve
1106 * @param res ** which will contain results - free res* with ads_msgfree()
1107 * @return status of search
1109 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1110 int scope
, const char *expr
,
1111 const char **attrs
, void *args
,
1114 struct berval
*cookie
= NULL
;
1119 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1122 if (!ADS_ERR_OK(status
))
1125 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1127 LDAPMessage
*res2
= NULL
;
1128 LDAPMessage
*msg
, *next
;
1130 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1131 attrs
, args
, &res2
, &count
, &cookie
);
1132 if (!ADS_ERR_OK(status
)) {
1133 /* Ensure we free all collected results */
1134 ads_msgfree(ads
, *res
);
1139 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1140 that this works on all ldap libs, but I have only tested with openldap */
1141 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1142 next
= ads_next_message(ads
, msg
);
1143 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1145 /* note that we do not free res2, as the memory is now
1146 part of the main returned list */
1149 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1150 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1156 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1157 int scope
, const char *expr
,
1158 const char **attrs
, LDAPMessage
**res
)
1160 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1163 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1164 int scope
, const char *expr
,
1165 const char **attrs
, uint32 sd_flags
,
1170 args
.control
= ADS_SD_FLAGS_OID
;
1171 args
.val
= sd_flags
;
1172 args
.critical
= True
;
1174 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1179 * Run a function on all results for a search. Uses ads_do_paged_search() and
1180 * runs the function as each page is returned, using ads_process_results()
1181 * @param ads connection to ads server
1182 * @param bind_path Base dn for the search
1183 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1184 * @param expr Search expression - specified in local charset
1185 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1186 * @param fn Function which takes attr name, values list, and data_area
1187 * @param data_area Pointer which is passed to function on each call
1188 * @return status of search
1190 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1191 int scope
, const char *expr
, const char **attrs
,
1192 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1195 struct berval
*cookie
= NULL
;
1200 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1203 if (!ADS_ERR_OK(status
)) return status
;
1205 ads_process_results(ads
, res
, fn
, data_area
);
1206 ads_msgfree(ads
, res
);
1209 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1210 &res
, &count
, &cookie
);
1212 if (!ADS_ERR_OK(status
)) break;
1214 ads_process_results(ads
, res
, fn
, data_area
);
1215 ads_msgfree(ads
, res
);
1222 * Do a search with a timeout.
1223 * @param ads connection to ads server
1224 * @param bind_path Base dn for the search
1225 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1226 * @param expr Search expression
1227 * @param attrs Attributes to retrieve
1228 * @param res ** which will contain results - free res* with ads_msgfree()
1229 * @return status of search
1231 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1233 const char **attrs
, LDAPMessage
**res
)
1236 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1237 size_t converted_size
;
1241 if (!(ctx
= talloc_init("ads_do_search"))) {
1242 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1243 return ADS_ERROR(LDAP_NO_MEMORY
);
1246 /* 0 means the conversion worked but the result was empty
1247 so we only fail if it's negative. In any case, it always
1248 at least nulls out the dest */
1249 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1250 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1252 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1253 rc
= LDAP_NO_MEMORY
;
1257 if (!attrs
|| !(*attrs
))
1258 search_attrs
= NULL
;
1260 /* This would be the utf8-encoded version...*/
1261 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1262 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1264 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1265 rc
= LDAP_NO_MEMORY
;
1270 /* see the note in ads_do_paged_search - we *must* disable referrals */
1271 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1273 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1274 search_attrs
, 0, NULL
, NULL
,
1276 (LDAPMessage
**)res
);
1278 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1279 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1284 talloc_destroy(ctx
);
1285 /* if/when we decide to utf8-encode attrs, take out this next line */
1286 TALLOC_FREE(search_attrs
);
1287 return ADS_ERROR(rc
);
1290 * Do a general ADS search
1291 * @param ads connection to ads server
1292 * @param res ** which will contain results - free res* with ads_msgfree()
1293 * @param expr Search expression
1294 * @param attrs Attributes to retrieve
1295 * @return status of search
1297 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1298 const char *expr
, const char **attrs
)
1300 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1305 * Do a search on a specific DistinguishedName
1306 * @param ads connection to ads server
1307 * @param res ** which will contain results - free res* with ads_msgfree()
1308 * @param dn DistinguishName to search
1309 * @param attrs Attributes to retrieve
1310 * @return status of search
1312 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1313 const char *dn
, const char **attrs
)
1315 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1320 * Free up memory from a ads_search
1321 * @param ads connection to ads server
1322 * @param msg Search results to free
1324 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1331 * Get a dn from search results
1332 * @param ads connection to ads server
1333 * @param msg Search result
1336 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1338 char *utf8_dn
, *unix_dn
;
1339 size_t converted_size
;
1341 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1344 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1348 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1349 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1353 ldap_memfree(utf8_dn
);
1358 * Get the parent from a dn
1359 * @param dn the dn to return the parent from
1360 * @return parent dn string
1362 char *ads_parent_dn(const char *dn
)
1370 p
= strchr(dn
, ',');
1380 * Find a machine account given a hostname
1381 * @param ads connection to ads server
1382 * @param res ** which will contain results - free res* with ads_msgfree()
1383 * @param host Hostname to search for
1384 * @return status of search
1386 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1387 const char *machine
)
1391 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1395 /* the easiest way to find a machine account anywhere in the tree
1396 is to look for hostname$ */
1397 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1398 DEBUG(1, ("asprintf failed!\n"));
1399 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1402 status
= ads_search(ads
, res
, expr
, attrs
);
1408 * Initialize a list of mods to be used in a modify request
1409 * @param ctx An initialized TALLOC_CTX
1410 * @return allocated ADS_MODLIST
1412 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1414 #define ADS_MODLIST_ALLOC_SIZE 10
1417 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1418 /* -1 is safety to make sure we don't go over the end.
1419 need to reset it to NULL before doing ldap modify */
1420 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1422 return (ADS_MODLIST
)mods
;
1427 add an attribute to the list, with values list already constructed
1429 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1430 int mod_op
, const char *name
,
1431 const void *_invals
)
1433 const void **invals
= (const void **)_invals
;
1435 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1436 struct berval
**ber_values
= NULL
;
1437 char **char_values
= NULL
;
1440 mod_op
= LDAP_MOD_DELETE
;
1442 if (mod_op
& LDAP_MOD_BVALUES
)
1443 ber_values
= ads_dup_values(ctx
,
1444 (const struct berval
**)invals
);
1446 char_values
= ads_push_strvals(ctx
,
1447 (const char **) invals
);
1450 /* find the first empty slot */
1451 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1453 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1454 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1455 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1456 return ADS_ERROR(LDAP_NO_MEMORY
);
1457 memset(&modlist
[curmod
], 0,
1458 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1459 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1460 *mods
= (ADS_MODLIST
)modlist
;
1463 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1464 return ADS_ERROR(LDAP_NO_MEMORY
);
1465 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1466 if (mod_op
& LDAP_MOD_BVALUES
) {
1467 modlist
[curmod
]->mod_bvalues
= ber_values
;
1468 } else if (mod_op
& LDAP_MOD_DELETE
) {
1469 modlist
[curmod
]->mod_values
= NULL
;
1471 modlist
[curmod
]->mod_values
= char_values
;
1474 modlist
[curmod
]->mod_op
= mod_op
;
1475 return ADS_ERROR(LDAP_SUCCESS
);
1479 * Add a single string value to a mod list
1480 * @param ctx An initialized TALLOC_CTX
1481 * @param mods An initialized ADS_MODLIST
1482 * @param name The attribute name to add
1483 * @param val The value to add - NULL means DELETE
1484 * @return ADS STATUS indicating success of add
1486 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1487 const char *name
, const char *val
)
1489 const char *values
[2];
1495 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1496 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1500 * Add an array of string values to a mod list
1501 * @param ctx An initialized TALLOC_CTX
1502 * @param mods An initialized ADS_MODLIST
1503 * @param name The attribute name to add
1504 * @param vals The array of string values to add - NULL means DELETE
1505 * @return ADS STATUS indicating success of add
1507 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1508 const char *name
, const char **vals
)
1511 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1512 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1513 name
, (const void **) vals
);
1518 * Add a single ber-encoded value to a mod list
1519 * @param ctx An initialized TALLOC_CTX
1520 * @param mods An initialized ADS_MODLIST
1521 * @param name The attribute name to add
1522 * @param val The value to add - NULL means DELETE
1523 * @return ADS STATUS indicating success of add
1525 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1526 const char *name
, const struct berval
*val
)
1528 const struct berval
*values
[2];
1533 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1534 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1535 name
, (const void **) values
);
1540 * Perform an ldap modify
1541 * @param ads connection to ads server
1542 * @param mod_dn DistinguishedName to modify
1543 * @param mods list of modifications to perform
1544 * @return status of modify
1546 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1549 char *utf8_dn
= NULL
;
1550 size_t converted_size
;
1552 this control is needed to modify that contains a currently
1553 non-existent attribute (but allowable for the object) to run
1555 LDAPControl PermitModify
= {
1556 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1559 LDAPControl
*controls
[2];
1561 controls
[0] = &PermitModify
;
1564 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1565 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1568 /* find the end of the list, marked by NULL or -1 */
1569 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1570 /* make sure the end of the list is NULL */
1572 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1573 (LDAPMod
**) mods
, controls
, NULL
);
1574 TALLOC_FREE(utf8_dn
);
1575 return ADS_ERROR(ret
);
1579 * Perform an ldap add
1580 * @param ads connection to ads server
1581 * @param new_dn DistinguishedName to add
1582 * @param mods list of attributes and values for DN
1583 * @return status of add
1585 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1588 char *utf8_dn
= NULL
;
1589 size_t converted_size
;
1591 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1592 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1593 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1596 /* find the end of the list, marked by NULL or -1 */
1597 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1598 /* make sure the end of the list is NULL */
1601 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1602 TALLOC_FREE(utf8_dn
);
1603 return ADS_ERROR(ret
);
1607 * Delete a DistinguishedName
1608 * @param ads connection to ads server
1609 * @param new_dn DistinguishedName to delete
1610 * @return status of delete
1612 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1615 char *utf8_dn
= NULL
;
1616 size_t converted_size
;
1617 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1618 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1619 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1622 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1623 TALLOC_FREE(utf8_dn
);
1624 return ADS_ERROR(ret
);
1628 * Build an org unit string
1629 * if org unit is Computers or blank then assume a container, otherwise
1630 * assume a / separated list of organisational units.
1631 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1632 * @param ads connection to ads server
1633 * @param org_unit Organizational unit
1634 * @return org unit string - caller must free
1636 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1640 if (!org_unit
|| !*org_unit
) {
1642 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1644 /* samba4 might not yet respond to a wellknownobject-query */
1645 return ret
? ret
: SMB_STRDUP("cn=Computers");
1648 if (strequal(org_unit
, "Computers")) {
1649 return SMB_STRDUP("cn=Computers");
1652 /* jmcd: removed "\\" from the separation chars, because it is
1653 needed as an escape for chars like '#' which are valid in an
1655 return ads_build_path(org_unit
, "/", "ou=", 1);
1659 * Get a org unit string for a well-known GUID
1660 * @param ads connection to ads server
1661 * @param wknguid Well known GUID
1662 * @return org unit string - caller must free
1664 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1667 LDAPMessage
*res
= NULL
;
1668 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1669 **bind_dn_exp
= NULL
;
1670 const char *attrs
[] = {"distinguishedName", NULL
};
1671 int new_ln
, wkn_ln
, bind_ln
, i
;
1673 if (wknguid
== NULL
) {
1677 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1678 DEBUG(1, ("asprintf failed!\n"));
1682 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1683 if (!ADS_ERR_OK(status
)) {
1684 DEBUG(1,("Failed while searching for: %s\n", base
));
1688 if (ads_count_replies(ads
, res
) != 1) {
1692 /* substitute the bind-path from the well-known-guid-search result */
1693 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1698 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1703 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1708 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1710 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1713 new_ln
= wkn_ln
- bind_ln
;
1715 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1720 for (i
=1; i
< new_ln
; i
++) {
1723 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1729 ret
= SMB_STRDUP(s
);
1738 ads_msgfree(ads
, res
);
1739 TALLOC_FREE(wkn_dn
);
1741 ldap_value_free(wkn_dn_exp
);
1744 ldap_value_free(bind_dn_exp
);
1751 * Adds (appends) an item to an attribute array, rather then
1752 * replacing the whole list
1753 * @param ctx An initialized TALLOC_CTX
1754 * @param mods An initialized ADS_MODLIST
1755 * @param name name of the ldap attribute to append to
1756 * @param vals an array of values to add
1757 * @return status of addition
1760 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1761 const char *name
, const char **vals
)
1763 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1764 (const void *) vals
);
1768 * Determines the an account's current KVNO via an LDAP lookup
1769 * @param ads An initialized ADS_STRUCT
1770 * @param account_name the NT samaccountname.
1771 * @return the kvno for the account, or -1 in case of a failure.
1774 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1776 LDAPMessage
*res
= NULL
;
1777 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1779 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1780 char *dn_string
= NULL
;
1781 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1783 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1784 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1787 ret
= ads_search(ads
, &res
, filter
, attrs
);
1789 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1790 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1791 ads_msgfree(ads
, res
);
1795 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1797 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1798 ads_msgfree(ads
, res
);
1801 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1802 TALLOC_FREE(dn_string
);
1804 /* ---------------------------------------------------------
1805 * 0 is returned as a default KVNO from this point on...
1806 * This is done because Windows 2000 does not support key
1807 * version numbers. Chances are that a failure in the next
1808 * step is simply due to Windows 2000 being used for a
1809 * domain controller. */
1812 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1813 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1814 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1815 ads_msgfree(ads
, res
);
1820 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1821 ads_msgfree(ads
, res
);
1826 * Determines the computer account's current KVNO via an LDAP lookup
1827 * @param ads An initialized ADS_STRUCT
1828 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1829 * @return the kvno for the computer account, or -1 in case of a failure.
1832 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1834 char *computer_account
= NULL
;
1837 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1841 kvno
= ads_get_kvno(ads
, computer_account
);
1842 free(computer_account
);
1848 * This clears out all registered spn's for a given hostname
1849 * @param ads An initilaized ADS_STRUCT
1850 * @param machine_name the NetBIOS name of the computer.
1851 * @return 0 upon success, non-zero otherwise.
1854 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1857 LDAPMessage
*res
= NULL
;
1859 const char *servicePrincipalName
[1] = {NULL
};
1860 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1861 char *dn_string
= NULL
;
1863 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1864 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1865 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1866 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1867 ads_msgfree(ads
, res
);
1868 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1871 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1872 ctx
= talloc_init("ads_clear_service_principal_names");
1874 ads_msgfree(ads
, res
);
1875 return ADS_ERROR(LDAP_NO_MEMORY
);
1878 if (!(mods
= ads_init_mods(ctx
))) {
1879 talloc_destroy(ctx
);
1880 ads_msgfree(ads
, res
);
1881 return ADS_ERROR(LDAP_NO_MEMORY
);
1883 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1884 if (!ADS_ERR_OK(ret
)) {
1885 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1886 ads_msgfree(ads
, res
);
1887 talloc_destroy(ctx
);
1890 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1892 talloc_destroy(ctx
);
1893 ads_msgfree(ads
, res
);
1894 return ADS_ERROR(LDAP_NO_MEMORY
);
1896 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1897 TALLOC_FREE(dn_string
);
1898 if (!ADS_ERR_OK(ret
)) {
1899 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1901 ads_msgfree(ads
, res
);
1902 talloc_destroy(ctx
);
1906 ads_msgfree(ads
, res
);
1907 talloc_destroy(ctx
);
1912 * @brief This gets the service principal names of an existing computer account.
1914 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1916 * @param[in] ads The ADS context to use.
1918 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1919 * identify the computer account.
1921 * @param[in] spn_array A pointer to store the array for SPNs.
1923 * @param[in] num_spns The number of principals stored in the array.
1925 * @return 0 on success, or a ADS error if a failure occured.
1927 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
1929 const char *machine_name
,
1934 LDAPMessage
*res
= NULL
;
1938 status
= ads_find_machine_acct(ads
,
1941 if (!ADS_ERR_OK(status
)) {
1942 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1947 count
= ads_count_replies(ads
, res
);
1949 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1953 dn
= ads_get_dn(ads
, mem_ctx
, res
);
1955 status
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
1959 *spn_array
= ads_pull_strings(ads
,
1962 "servicePrincipalName",
1966 ads_msgfree(ads
, res
);
1972 * This adds a service principal name to an existing computer account
1973 * (found by hostname) in AD.
1974 * @param ads An initialized ADS_STRUCT
1975 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1976 * @param my_fqdn The fully qualified DNS name of the machine
1977 * @param spn A string of the service principal to add, i.e. 'host'
1978 * @return 0 upon sucess, or non-zero if a failure occurs
1981 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1982 const char *my_fqdn
, const char *spn
)
1986 LDAPMessage
*res
= NULL
;
1989 char *dn_string
= NULL
;
1990 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1992 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1993 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1994 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1996 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1997 spn
, machine_name
, ads
->config
.realm
));
1998 ads_msgfree(ads
, res
);
1999 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2002 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2003 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2004 ads_msgfree(ads
, res
);
2005 return ADS_ERROR(LDAP_NO_MEMORY
);
2008 /* add short name spn */
2010 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
2011 talloc_destroy(ctx
);
2012 ads_msgfree(ads
, res
);
2013 return ADS_ERROR(LDAP_NO_MEMORY
);
2015 if (!strlower_m(&psp1
[strlen(spn
) + 1])) {
2016 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2019 servicePrincipalName
[0] = psp1
;
2021 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2022 psp1
, machine_name
));
2025 /* add fully qualified spn */
2027 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
2028 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2031 if (!strlower_m(&psp2
[strlen(spn
) + 1])) {
2032 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2035 servicePrincipalName
[1] = psp2
;
2037 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2038 psp2
, machine_name
));
2040 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2041 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2045 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2046 if (!ADS_ERR_OK(ret
)) {
2047 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2051 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2052 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2056 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2057 if (!ADS_ERR_OK(ret
)) {
2058 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2064 ads_msgfree(ads
, res
);
2069 * adds a machine account to the ADS server
2070 * @param ads An intialized ADS_STRUCT
2071 * @param machine_name - the NetBIOS machine name of this account.
2072 * @param account_type A number indicating the type of account to create
2073 * @param org_unit The LDAP path in which to place this account
2074 * @return 0 upon success, or non-zero otherwise
2077 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2078 const char *org_unit
)
2081 char *samAccountName
, *controlstr
;
2084 char *machine_escaped
= NULL
;
2086 const char *objectClass
[] = {"top", "person", "organizationalPerson",
2087 "user", "computer", NULL
};
2088 LDAPMessage
*res
= NULL
;
2089 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
2090 UF_DONT_EXPIRE_PASSWD
|\
2091 UF_ACCOUNTDISABLE
);
2093 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2094 return ADS_ERROR(LDAP_NO_MEMORY
);
2096 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2098 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2099 if (!machine_escaped
) {
2103 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2104 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2106 if ( !new_dn
|| !samAccountName
) {
2110 #ifndef ENCTYPE_ARCFOUR_HMAC
2111 acct_control
|= UF_USE_DES_KEY_ONLY
;
2114 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2118 if (!(mods
= ads_init_mods(ctx
))) {
2122 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2123 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2124 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2125 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2127 ret
= ads_gen_add(ads
, new_dn
, mods
);
2130 SAFE_FREE(machine_escaped
);
2131 ads_msgfree(ads
, res
);
2132 talloc_destroy(ctx
);
2138 * move a machine account to another OU on the ADS server
2139 * @param ads - An intialized ADS_STRUCT
2140 * @param machine_name - the NetBIOS machine name of this account.
2141 * @param org_unit - The LDAP path in which to place this account
2142 * @param moved - whether we moved the machine account (optional)
2143 * @return 0 upon success, or non-zero otherwise
2146 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2147 const char *org_unit
, bool *moved
)
2151 LDAPMessage
*res
= NULL
;
2152 char *filter
= NULL
;
2153 char *computer_dn
= NULL
;
2155 char *computer_rdn
= NULL
;
2156 bool need_move
= False
;
2158 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2159 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2163 /* Find pre-existing machine */
2164 rc
= ads_search(ads
, &res
, filter
, NULL
);
2165 if (!ADS_ERR_OK(rc
)) {
2169 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2171 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2175 parent_dn
= ads_parent_dn(computer_dn
);
2176 if (strequal(parent_dn
, org_unit
)) {
2182 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2183 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2187 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2188 org_unit
, 1, NULL
, NULL
);
2189 rc
= ADS_ERROR(ldap_status
);
2192 ads_msgfree(ads
, res
);
2194 TALLOC_FREE(computer_dn
);
2195 SAFE_FREE(computer_rdn
);
2197 if (!ADS_ERR_OK(rc
)) {
2209 dump a binary result from ldap
2211 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2214 for (i
=0; values
[i
]; i
++) {
2215 printf("%s: ", field
);
2216 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2217 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2223 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2226 for (i
=0; values
[i
]; i
++) {
2228 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2231 status
= GUID_from_ndr_blob(&in
, &guid
);
2232 if (NT_STATUS_IS_OK(status
)) {
2233 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2235 printf("%s: INVALID GUID\n", field
);
2241 dump a sid result from ldap
2243 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2246 for (i
=0; values
[i
]; i
++) {
2249 if (!sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
)) {
2252 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2257 dump ntSecurityDescriptor
2259 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2261 TALLOC_CTX
*frame
= talloc_stackframe();
2262 struct security_descriptor
*psd
;
2265 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2266 values
[0]->bv_len
, &psd
);
2267 if (!NT_STATUS_IS_OK(status
)) {
2268 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2269 nt_errstr(status
)));
2275 ads_disp_sd(ads
, talloc_tos(), psd
);
2282 dump a string result from ldap
2284 static void dump_string(const char *field
, char **values
)
2287 for (i
=0; values
[i
]; i
++) {
2288 printf("%s: %s\n", field
, values
[i
]);
2293 dump a field from LDAP on stdout
2297 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2302 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2304 {"objectGUID", False
, dump_guid
},
2305 {"netbootGUID", False
, dump_guid
},
2306 {"nTSecurityDescriptor", False
, dump_sd
},
2307 {"dnsRecord", False
, dump_binary
},
2308 {"objectSid", False
, dump_sid
},
2309 {"tokenGroups", False
, dump_sid
},
2310 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2311 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2312 {"mS-DS-CreatorSID", False
, dump_sid
},
2313 {"msExchMailboxGuid", False
, dump_guid
},
2318 if (!field
) { /* must be end of an entry */
2323 for (i
=0; handlers
[i
].name
; i
++) {
2324 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2325 if (!values
) /* first time, indicate string or not */
2326 return handlers
[i
].string
;
2327 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2331 if (!handlers
[i
].name
) {
2332 if (!values
) /* first time, indicate string conversion */
2334 dump_string(field
, (char **)values
);
2340 * Dump a result from LDAP on stdout
2341 * used for debugging
2342 * @param ads connection to ads server
2343 * @param res Results to dump
2346 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2348 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2352 * Walk through results, calling a function for each entry found.
2353 * The function receives a field name, a berval * array of values,
2354 * and a data area passed through from the start. The function is
2355 * called once with null for field and values at the end of each
2357 * @param ads connection to ads server
2358 * @param res Results to process
2359 * @param fn Function for processing each result
2360 * @param data_area user-defined area to pass to function
2362 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2363 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2368 size_t converted_size
;
2370 if (!(ctx
= talloc_init("ads_process_results")))
2373 for (msg
= ads_first_entry(ads
, res
); msg
;
2374 msg
= ads_next_entry(ads
, msg
)) {
2378 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2379 (LDAPMessage
*)msg
,&b
);
2381 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2382 (LDAPMessage
*)msg
,b
)) {
2383 struct berval
**ber_vals
;
2384 char **str_vals
, **utf8_vals
;
2388 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2391 DEBUG(0,("ads_process_results: "
2392 "pull_utf8_talloc failed: %s",
2396 string
= fn(ads
, field
, NULL
, data_area
);
2399 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2400 (LDAPMessage
*)msg
, field
);
2401 str_vals
= ads_pull_strvals(ctx
,
2402 (const char **) utf8_vals
);
2403 fn(ads
, field
, (void **) str_vals
, data_area
);
2404 ldap_value_free(utf8_vals
);
2406 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2407 (LDAPMessage
*)msg
, field
);
2408 fn(ads
, field
, (void **) ber_vals
, data_area
);
2410 ldap_value_free_len(ber_vals
);
2412 ldap_memfree(utf8_field
);
2415 talloc_free_children(ctx
);
2416 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2419 talloc_destroy(ctx
);
2423 * count how many replies are in a LDAPMessage
2424 * @param ads connection to ads server
2425 * @param res Results to count
2426 * @return number of replies
2428 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2430 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2434 * pull the first entry from a ADS result
2435 * @param ads connection to ads server
2436 * @param res Results of search
2437 * @return first entry from result
2439 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2441 return ldap_first_entry(ads
->ldap
.ld
, res
);
2445 * pull the next entry from a ADS result
2446 * @param ads connection to ads server
2447 * @param res Results of search
2448 * @return next entry from result
2450 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2452 return ldap_next_entry(ads
->ldap
.ld
, res
);
2456 * pull the first message from a ADS result
2457 * @param ads connection to ads server
2458 * @param res Results of search
2459 * @return first message from result
2461 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2463 return ldap_first_message(ads
->ldap
.ld
, res
);
2467 * pull the next message from a ADS result
2468 * @param ads connection to ads server
2469 * @param res Results of search
2470 * @return next message from result
2472 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2474 return ldap_next_message(ads
->ldap
.ld
, res
);
2478 * pull a single string from a ADS result
2479 * @param ads connection to ads server
2480 * @param mem_ctx TALLOC_CTX to use for allocating result string
2481 * @param msg Results of search
2482 * @param field Attribute to retrieve
2483 * @return Result string in talloc context
2485 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2491 size_t converted_size
;
2493 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2497 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2502 ldap_value_free(values
);
2507 * pull an array of strings from a ADS result
2508 * @param ads connection to ads server
2509 * @param mem_ctx TALLOC_CTX to use for allocating result string
2510 * @param msg Results of search
2511 * @param field Attribute to retrieve
2512 * @return Result strings in talloc context
2514 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2515 LDAPMessage
*msg
, const char *field
,
2521 size_t converted_size
;
2523 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2527 *num_values
= ldap_count_values(values
);
2529 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2531 ldap_value_free(values
);
2535 for (i
=0;i
<*num_values
;i
++) {
2536 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2539 ldap_value_free(values
);
2545 ldap_value_free(values
);
2550 * pull an array of strings from a ADS result
2551 * (handle large multivalue attributes with range retrieval)
2552 * @param ads connection to ads server
2553 * @param mem_ctx TALLOC_CTX to use for allocating result string
2554 * @param msg Results of search
2555 * @param field Attribute to retrieve
2556 * @param current_strings strings returned by a previous call to this function
2557 * @param next_attribute The next query should ask for this attribute
2558 * @param num_values How many values did we get this time?
2559 * @param more_values Are there more values to get?
2560 * @return Result strings in talloc context
2562 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2563 TALLOC_CTX
*mem_ctx
,
2564 LDAPMessage
*msg
, const char *field
,
2565 char **current_strings
,
2566 const char **next_attribute
,
2567 size_t *num_strings
,
2571 char *expected_range_attrib
, *range_attr
;
2572 BerElement
*ptr
= NULL
;
2575 size_t num_new_strings
;
2576 unsigned long int range_start
;
2577 unsigned long int range_end
;
2579 /* we might have been given the whole lot anyway */
2580 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2581 *more_strings
= False
;
2585 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2587 /* look for Range result */
2588 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2590 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2591 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2592 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2600 /* nothing here - this field is just empty */
2601 *more_strings
= False
;
2605 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2606 &range_start
, &range_end
) == 2) {
2607 *more_strings
= True
;
2609 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2610 &range_start
) == 1) {
2611 *more_strings
= False
;
2613 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2615 ldap_memfree(range_attr
);
2616 *more_strings
= False
;
2621 if ((*num_strings
) != range_start
) {
2622 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2623 " - aborting range retreival\n",
2624 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2625 ldap_memfree(range_attr
);
2626 *more_strings
= False
;
2630 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2632 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2633 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2634 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2635 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2636 (unsigned long int)num_new_strings
));
2637 ldap_memfree(range_attr
);
2638 *more_strings
= False
;
2642 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2643 *num_strings
+ num_new_strings
);
2645 if (strings
== NULL
) {
2646 ldap_memfree(range_attr
);
2647 *more_strings
= False
;
2651 if (new_strings
&& num_new_strings
) {
2652 memcpy(&strings
[*num_strings
], new_strings
,
2653 sizeof(*new_strings
) * num_new_strings
);
2656 (*num_strings
) += num_new_strings
;
2658 if (*more_strings
) {
2659 *next_attribute
= talloc_asprintf(mem_ctx
,
2664 if (!*next_attribute
) {
2665 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2666 ldap_memfree(range_attr
);
2667 *more_strings
= False
;
2672 ldap_memfree(range_attr
);
2678 * pull a single uint32 from a ADS result
2679 * @param ads connection to ads server
2680 * @param msg Results of search
2681 * @param field Attribute to retrieve
2682 * @param v Pointer to int to store result
2683 * @return boolean inidicating success
2685 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2690 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2694 ldap_value_free(values
);
2698 *v
= atoi(values
[0]);
2699 ldap_value_free(values
);
2704 * pull a single objectGUID from an ADS result
2705 * @param ads connection to ADS server
2706 * @param msg results of search
2707 * @param guid 37-byte area to receive text guid
2708 * @return boolean indicating success
2710 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2715 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2720 status
= GUID_from_ndr_blob(&blob
, guid
);
2721 talloc_free(blob
.data
);
2722 return NT_STATUS_IS_OK(status
);
2727 * pull a single struct dom_sid from a ADS result
2728 * @param ads connection to ads server
2729 * @param msg Results of search
2730 * @param field Attribute to retrieve
2731 * @param sid Pointer to sid to store result
2732 * @return boolean inidicating success
2734 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2735 struct dom_sid
*sid
)
2737 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2741 * pull an array of struct dom_sids from a ADS result
2742 * @param ads connection to ads server
2743 * @param mem_ctx TALLOC_CTX for allocating sid array
2744 * @param msg Results of search
2745 * @param field Attribute to retrieve
2746 * @param sids pointer to sid array to allocate
2747 * @return the count of SIDs pulled
2749 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2750 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2752 struct berval
**values
;
2756 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2761 for (i
=0; values
[i
]; i
++)
2765 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2767 ldap_value_free_len(values
);
2775 for (i
=0; values
[i
]; i
++) {
2776 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2778 DEBUG(10, ("pulling SID: %s\n",
2779 sid_string_dbg(&(*sids
)[count
])));
2784 ldap_value_free_len(values
);
2789 * pull a struct security_descriptor from a ADS result
2790 * @param ads connection to ads server
2791 * @param mem_ctx TALLOC_CTX for allocating sid array
2792 * @param msg Results of search
2793 * @param field Attribute to retrieve
2794 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2795 * @return boolean inidicating success
2797 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2798 LDAPMessage
*msg
, const char *field
,
2799 struct security_descriptor
**sd
)
2801 struct berval
**values
;
2804 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2806 if (!values
) return false;
2810 status
= unmarshall_sec_desc(mem_ctx
,
2811 (uint8
*)values
[0]->bv_val
,
2812 values
[0]->bv_len
, sd
);
2813 if (!NT_STATUS_IS_OK(status
)) {
2814 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2815 nt_errstr(status
)));
2820 ldap_value_free_len(values
);
2825 * in order to support usernames longer than 21 characters we need to
2826 * use both the sAMAccountName and the userPrincipalName attributes
2827 * It seems that not all users have the userPrincipalName attribute set
2829 * @param ads connection to ads server
2830 * @param mem_ctx TALLOC_CTX for allocating sid array
2831 * @param msg Results of search
2832 * @return the username
2834 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2840 /* lookup_name() only works on the sAMAccountName to
2841 returning the username portion of userPrincipalName
2842 breaks winbindd_getpwnam() */
2844 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2845 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2850 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2855 * find the update serial number - this is the core of the ldap cache
2856 * @param ads connection to ads server
2857 * @param ads connection to ADS server
2858 * @param usn Pointer to retrieved update serial number
2859 * @return status of search
2861 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2863 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2867 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2868 if (!ADS_ERR_OK(status
))
2871 if (ads_count_replies(ads
, res
) != 1) {
2872 ads_msgfree(ads
, res
);
2873 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2876 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2877 ads_msgfree(ads
, res
);
2878 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2881 ads_msgfree(ads
, res
);
2885 /* parse a ADS timestring - typical string is
2886 '20020917091222.0Z0' which means 09:12.22 17th September
2888 static time_t ads_parse_time(const char *str
)
2894 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2895 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2896 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2905 /********************************************************************
2906 ********************************************************************/
2908 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2910 const char *attrs
[] = {"currentTime", NULL
};
2915 ADS_STRUCT
*ads_s
= ads
;
2917 if (!(ctx
= talloc_init("ads_current_time"))) {
2918 return ADS_ERROR(LDAP_NO_MEMORY
);
2921 /* establish a new ldap tcp session if necessary */
2923 if ( !ads
->ldap
.ld
) {
2924 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2925 ads
->server
.ldap_server
)) == NULL
)
2929 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2930 status
= ads_connect( ads_s
);
2931 if ( !ADS_ERR_OK(status
))
2935 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2936 if (!ADS_ERR_OK(status
)) {
2940 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2942 ads_msgfree(ads_s
, res
);
2943 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2947 /* but save the time and offset in the original ADS_STRUCT */
2949 ads
->config
.current_time
= ads_parse_time(timestr
);
2951 if (ads
->config
.current_time
!= 0) {
2952 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2953 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
2956 ads_msgfree(ads
, res
);
2958 status
= ADS_SUCCESS
;
2961 /* free any temporary ads connections */
2962 if ( ads_s
!= ads
) {
2963 ads_destroy( &ads_s
);
2965 talloc_destroy(ctx
);
2970 /********************************************************************
2971 ********************************************************************/
2973 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2975 const char *attrs
[] = {"domainFunctionality", NULL
};
2978 ADS_STRUCT
*ads_s
= ads
;
2980 *val
= DS_DOMAIN_FUNCTION_2000
;
2982 /* establish a new ldap tcp session if necessary */
2984 if ( !ads
->ldap
.ld
) {
2985 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2986 ads
->server
.ldap_server
)) == NULL
)
2988 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2991 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2992 status
= ads_connect( ads_s
);
2993 if ( !ADS_ERR_OK(status
))
2997 /* If the attribute does not exist assume it is a Windows 2000
2998 functional domain */
3000 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3001 if (!ADS_ERR_OK(status
)) {
3002 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3003 status
= ADS_SUCCESS
;
3008 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3009 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3011 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3014 ads_msgfree(ads
, res
);
3017 /* free any temporary ads connections */
3018 if ( ads_s
!= ads
) {
3019 ads_destroy( &ads_s
);
3026 * find the domain sid for our domain
3027 * @param ads connection to ads server
3028 * @param sid Pointer to domain sid
3029 * @return status of search
3031 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3033 const char *attrs
[] = {"objectSid", NULL
};
3037 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3039 if (!ADS_ERR_OK(rc
)) return rc
;
3040 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3041 ads_msgfree(ads
, res
);
3042 return ADS_ERROR_SYSTEM(ENOENT
);
3044 ads_msgfree(ads
, res
);
3050 * find our site name
3051 * @param ads connection to ads server
3052 * @param mem_ctx Pointer to talloc context
3053 * @param site_name Pointer to the sitename
3054 * @return status of search
3056 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3060 const char *dn
, *service_name
;
3061 const char *attrs
[] = { "dsServiceName", NULL
};
3063 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3064 if (!ADS_ERR_OK(status
)) {
3068 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3069 if (service_name
== NULL
) {
3070 ads_msgfree(ads
, res
);
3071 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3074 ads_msgfree(ads
, res
);
3076 /* go up three levels */
3077 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3079 return ADS_ERROR(LDAP_NO_MEMORY
);
3082 *site_name
= talloc_strdup(mem_ctx
, dn
);
3083 if (*site_name
== NULL
) {
3084 return ADS_ERROR(LDAP_NO_MEMORY
);
3089 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3094 * find the site dn where a machine resides
3095 * @param ads connection to ads server
3096 * @param mem_ctx Pointer to talloc context
3097 * @param computer_name name of the machine
3098 * @param site_name Pointer to the sitename
3099 * @return status of search
3101 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3105 const char *parent
, *filter
;
3106 char *config_context
= NULL
;
3109 /* shortcut a query */
3110 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3111 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3114 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3115 if (!ADS_ERR_OK(status
)) {
3119 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3120 if (filter
== NULL
) {
3121 return ADS_ERROR(LDAP_NO_MEMORY
);
3124 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3125 filter
, NULL
, &res
);
3126 if (!ADS_ERR_OK(status
)) {
3130 if (ads_count_replies(ads
, res
) != 1) {
3131 ads_msgfree(ads
, res
);
3132 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3135 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3137 ads_msgfree(ads
, res
);
3138 return ADS_ERROR(LDAP_NO_MEMORY
);
3141 /* go up three levels */
3142 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3143 if (parent
== NULL
) {
3144 ads_msgfree(ads
, res
);
3146 return ADS_ERROR(LDAP_NO_MEMORY
);
3149 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3150 if (*site_dn
== NULL
) {
3151 ads_msgfree(ads
, res
);
3153 return ADS_ERROR(LDAP_NO_MEMORY
);
3157 ads_msgfree(ads
, res
);
3163 * get the upn suffixes for a domain
3164 * @param ads connection to ads server
3165 * @param mem_ctx Pointer to talloc context
3166 * @param suffixes Pointer to an array of suffixes
3167 * @param num_suffixes Pointer to the number of suffixes
3168 * @return status of search
3170 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3175 char *config_context
= NULL
;
3176 const char *attrs
[] = { "uPNSuffixes", NULL
};
3178 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3179 if (!ADS_ERR_OK(status
)) {
3183 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3185 return ADS_ERROR(LDAP_NO_MEMORY
);
3188 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3189 if (!ADS_ERR_OK(status
)) {
3193 if (ads_count_replies(ads
, res
) != 1) {
3194 ads_msgfree(ads
, res
);
3195 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3198 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3199 if ((*suffixes
) == NULL
) {
3200 ads_msgfree(ads
, res
);
3201 return ADS_ERROR(LDAP_NO_MEMORY
);
3204 ads_msgfree(ads
, res
);
3210 * get the joinable ous for a domain
3211 * @param ads connection to ads server
3212 * @param mem_ctx Pointer to talloc context
3213 * @param ous Pointer to an array of ous
3214 * @param num_ous Pointer to the number of ous
3215 * @return status of search
3217 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3218 TALLOC_CTX
*mem_ctx
,
3223 LDAPMessage
*res
= NULL
;
3224 LDAPMessage
*msg
= NULL
;
3225 const char *attrs
[] = { "dn", NULL
};
3228 status
= ads_search(ads
, &res
,
3229 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3231 if (!ADS_ERR_OK(status
)) {
3235 count
= ads_count_replies(ads
, res
);
3237 ads_msgfree(ads
, res
);
3238 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3241 for (msg
= ads_first_entry(ads
, res
); msg
;
3242 msg
= ads_next_entry(ads
, msg
)) {
3246 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3248 ads_msgfree(ads
, res
);
3249 return ADS_ERROR(LDAP_NO_MEMORY
);
3252 if (!add_string_to_array(mem_ctx
, dn
,
3253 (const char ***)ous
,
3256 ads_msgfree(ads
, res
);
3257 return ADS_ERROR(LDAP_NO_MEMORY
);
3263 ads_msgfree(ads
, res
);
3270 * pull a struct dom_sid from an extended dn string
3271 * @param mem_ctx TALLOC_CTX
3272 * @param extended_dn string
3273 * @param flags string type of extended_dn
3274 * @param sid pointer to a struct dom_sid
3275 * @return NT_STATUS_OK on success,
3276 * NT_INVALID_PARAMETER on error,
3277 * NT_STATUS_NOT_FOUND if no SID present
3279 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3280 const char *extended_dn
,
3281 enum ads_extended_dn_flags flags
,
3282 struct dom_sid
*sid
)
3287 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3290 /* otherwise extended_dn gets stripped off */
3291 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3292 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3295 * ADS_EXTENDED_DN_HEX_STRING:
3296 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3298 * ADS_EXTENDED_DN_STRING (only with w2k3):
3299 * <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
3301 * Object with no SID, such as an Exchange Public Folder
3302 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3305 p
= strchr(dn
, ';');
3307 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3310 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3311 DEBUG(5,("No SID present in extended dn\n"));
3312 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3315 p
+= strlen(";<SID=");
3319 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3324 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3328 case ADS_EXTENDED_DN_STRING
:
3329 if (!string_to_sid(sid
, p
)) {
3330 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3333 case ADS_EXTENDED_DN_HEX_STRING
: {
3337 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3339 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3342 if (!sid_parse(buf
, buf_len
, sid
)) {
3343 DEBUG(10,("failed to parse sid\n"));
3344 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3349 DEBUG(10,("unknown extended dn format\n"));
3350 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3353 return ADS_ERROR_NT(NT_STATUS_OK
);
3356 /********************************************************************
3357 ********************************************************************/
3359 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3361 LDAPMessage
*res
= NULL
;
3366 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3367 if (!ADS_ERR_OK(status
)) {
3368 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3369 lp_netbios_name()));
3373 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3374 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3378 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3379 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3383 ads_msgfree(ads
, res
);
3388 /********************************************************************
3389 ********************************************************************/
3391 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3393 LDAPMessage
*res
= NULL
;
3398 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3399 if (!ADS_ERR_OK(status
)) {
3400 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3401 lp_netbios_name()));
3405 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3406 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3410 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3411 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3415 ads_msgfree(ads
, res
);
3420 /********************************************************************
3421 ********************************************************************/
3423 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3425 LDAPMessage
*res
= NULL
;
3430 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3431 if (!ADS_ERR_OK(status
)) {
3432 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3433 lp_netbios_name()));
3437 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3438 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3442 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3443 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3447 ads_msgfree(ads
, res
);
3454 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3457 * Join a machine to a realm
3458 * Creates the machine account and sets the machine password
3459 * @param ads connection to ads server
3460 * @param machine name of host to add
3461 * @param org_unit Organizational unit to place machine in
3462 * @return status of join
3464 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3465 uint32 account_type
, const char *org_unit
)
3468 LDAPMessage
*res
= NULL
;
3471 /* machine name must be lowercase */
3472 machine
= SMB_STRDUP(machine_name
);
3473 strlower_m(machine
);
3476 status = ads_find_machine_acct(ads, (void **)&res, machine);
3477 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3478 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3479 status = ads_leave_realm(ads, machine);
3480 if (!ADS_ERR_OK(status)) {
3481 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3482 machine, ads->config.realm));
3487 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3488 if (!ADS_ERR_OK(status
)) {
3489 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3494 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3495 if (!ADS_ERR_OK(status
)) {
3496 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3502 ads_msgfree(ads
, res
);
3509 * Delete a machine from the realm
3510 * @param ads connection to ads server
3511 * @param hostname Machine to remove
3512 * @return status of delete
3514 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3519 char *hostnameDN
, *host
;
3521 LDAPControl ldap_control
;
3522 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3524 pldap_control
[0] = &ldap_control
;
3525 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3526 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3528 /* hostname must be lowercase */
3529 host
= SMB_STRDUP(hostname
);
3530 if (!strlower_m(host
)) {
3532 return ADS_ERROR_SYSTEM(EINVAL
);
3535 status
= ads_find_machine_acct(ads
, &res
, host
);
3536 if (!ADS_ERR_OK(status
)) {
3537 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3542 msg
= ads_first_entry(ads
, res
);
3545 return ADS_ERROR_SYSTEM(ENOENT
);
3548 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3549 if (hostnameDN
== NULL
) {
3551 return ADS_ERROR_SYSTEM(ENOENT
);
3554 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3556 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3558 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3561 if (rc
!= LDAP_SUCCESS
) {
3562 const char *attrs
[] = { "cn", NULL
};
3563 LDAPMessage
*msg_sub
;
3565 /* we only search with scope ONE, we do not expect any further
3566 * objects to be created deeper */
3568 status
= ads_do_search_retry(ads
, hostnameDN
,
3569 LDAP_SCOPE_ONELEVEL
,
3570 "(objectclass=*)", attrs
, &res
);
3572 if (!ADS_ERR_OK(status
)) {
3574 TALLOC_FREE(hostnameDN
);
3578 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3579 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3583 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3585 TALLOC_FREE(hostnameDN
);
3586 return ADS_ERROR(LDAP_NO_MEMORY
);
3589 status
= ads_del_dn(ads
, dn
);
3590 if (!ADS_ERR_OK(status
)) {
3591 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3594 TALLOC_FREE(hostnameDN
);
3601 /* there should be no subordinate objects anymore */
3602 status
= ads_do_search_retry(ads
, hostnameDN
,
3603 LDAP_SCOPE_ONELEVEL
,
3604 "(objectclass=*)", attrs
, &res
);
3606 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3608 TALLOC_FREE(hostnameDN
);
3612 /* delete hostnameDN now */
3613 status
= ads_del_dn(ads
, hostnameDN
);
3614 if (!ADS_ERR_OK(status
)) {
3616 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3617 TALLOC_FREE(hostnameDN
);
3622 TALLOC_FREE(hostnameDN
);
3624 status
= ads_find_machine_acct(ads
, &res
, host
);
3625 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3626 DEBUG(3, ("Failed to remove host account.\n"));
3636 * pull all token-sids from an LDAP dn
3637 * @param ads connection to ads server
3638 * @param mem_ctx TALLOC_CTX for allocating sid array
3639 * @param dn of LDAP object
3640 * @param user_sid pointer to struct dom_sid (objectSid)
3641 * @param primary_group_sid pointer to struct dom_sid (self composed)
3642 * @param sids pointer to sid array to allocate
3643 * @param num_sids counter of SIDs pulled
3644 * @return status of token query
3646 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3647 TALLOC_CTX
*mem_ctx
,
3649 struct dom_sid
*user_sid
,
3650 struct dom_sid
*primary_group_sid
,
3651 struct dom_sid
**sids
,
3655 LDAPMessage
*res
= NULL
;
3657 size_t tmp_num_sids
;
3658 struct dom_sid
*tmp_sids
;
3659 struct dom_sid tmp_user_sid
;
3660 struct dom_sid tmp_primary_group_sid
;
3662 const char *attrs
[] = {
3669 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3670 if (!ADS_ERR_OK(status
)) {
3674 count
= ads_count_replies(ads
, res
);
3676 ads_msgfree(ads
, res
);
3677 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3680 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3681 ads_msgfree(ads
, res
);
3682 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3685 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3686 ads_msgfree(ads
, res
);
3687 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3691 /* hack to compose the primary group sid without knowing the
3694 struct dom_sid domsid
;
3696 sid_copy(&domsid
, &tmp_user_sid
);
3698 if (!sid_split_rid(&domsid
, NULL
)) {
3699 ads_msgfree(ads
, res
);
3700 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3703 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3704 ads_msgfree(ads
, res
);
3705 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3709 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3711 if (tmp_num_sids
== 0 || !tmp_sids
) {
3712 ads_msgfree(ads
, res
);
3713 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3717 *num_sids
= tmp_num_sids
;
3725 *user_sid
= tmp_user_sid
;
3728 if (primary_group_sid
) {
3729 *primary_group_sid
= tmp_primary_group_sid
;
3732 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3734 ads_msgfree(ads
, res
);
3735 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3739 * Find a sAMAccoutName in LDAP
3740 * @param ads connection to ads server
3741 * @param mem_ctx TALLOC_CTX for allocating sid array
3742 * @param samaccountname to search
3743 * @param uac_ret uint32 pointer userAccountControl attribute value
3744 * @param dn_ret pointer to dn
3745 * @return status of token query
3747 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3748 TALLOC_CTX
*mem_ctx
,
3749 const char *samaccountname
,
3751 const char **dn_ret
)
3754 const char *attrs
[] = { "userAccountControl", NULL
};
3756 LDAPMessage
*res
= NULL
;
3760 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3762 if (filter
== NULL
) {
3763 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3767 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3769 filter
, attrs
, &res
);
3771 if (!ADS_ERR_OK(status
)) {
3775 if (ads_count_replies(ads
, res
) != 1) {
3776 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3780 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3782 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3786 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3787 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3796 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3798 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3804 ads_msgfree(ads
, res
);
3810 * find our configuration path
3811 * @param ads connection to ads server
3812 * @param mem_ctx Pointer to talloc context
3813 * @param config_path Pointer to the config path
3814 * @return status of search
3816 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3817 TALLOC_CTX
*mem_ctx
,
3821 LDAPMessage
*res
= NULL
;
3822 const char *config_context
= NULL
;
3823 const char *attrs
[] = { "configurationNamingContext", NULL
};
3825 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3826 "(objectclass=*)", attrs
, &res
);
3827 if (!ADS_ERR_OK(status
)) {
3831 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3832 "configurationNamingContext");
3833 ads_msgfree(ads
, res
);
3834 if (!config_context
) {
3835 return ADS_ERROR(LDAP_NO_MEMORY
);
3839 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3840 if (!*config_path
) {
3841 return ADS_ERROR(LDAP_NO_MEMORY
);
3845 return ADS_ERROR(LDAP_SUCCESS
);
3849 * find the displayName of an extended right
3850 * @param ads connection to ads server
3851 * @param config_path The config path
3852 * @param mem_ctx Pointer to talloc context
3853 * @param GUID struct of the rightsGUID
3854 * @return status of search
3856 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3857 const char *config_path
,
3858 TALLOC_CTX
*mem_ctx
,
3859 const struct GUID
*rights_guid
)
3862 LDAPMessage
*res
= NULL
;
3864 const char *attrs
[] = { "displayName", NULL
};
3865 const char *result
= NULL
;
3868 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3872 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3873 GUID_string(mem_ctx
, rights_guid
));
3878 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3883 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3885 if (!ADS_ERR_OK(rc
)) {
3889 if (ads_count_replies(ads
, res
) != 1) {
3893 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3896 ads_msgfree(ads
, res
);
3901 * verify or build and verify an account ou
3902 * @param mem_ctx Pointer to talloc context
3903 * @param ads connection to ads server
3905 * @return status of search
3908 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3910 const char **account_ou
)
3916 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
3918 ldap_value_free(exploded_dn
);
3922 ou_string
= ads_ou_string(ads
, *account_ou
);
3924 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3927 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3928 ads
->config
.bind_path
);
3929 SAFE_FREE(ou_string
);
3932 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3935 exploded_dn
= ldap_explode_dn(name
, 0);
3937 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3939 ldap_value_free(exploded_dn
);