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
)));
1044 if (rc
== LDAP_OTHER
) {
1048 ret
= ldap_parse_result(ads
->ldap
.ld
,
1056 if (ret
== LDAP_SUCCESS
) {
1057 DEBUG(3, ("ldap_search_with_timeout(%s) "
1058 "error: %s\n", expr
, ldap_errmsg
));
1059 ldap_memfree(ldap_errmsg
);
1065 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1066 NULL
, &rcontrols
, 0);
1072 for (i
=0; rcontrols
[i
]; i
++) {
1073 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1074 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1075 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1077 /* the berval is the cookie, but must be freed when
1079 if (cookie_bv
->bv_len
) /* still more to do */
1080 *cookie
=ber_bvdup(cookie_bv
);
1083 ber_bvfree(cookie_bv
);
1084 ber_free(cookie_be
, 1);
1088 ldap_controls_free(rcontrols
);
1091 talloc_destroy(ctx
);
1094 ber_free(ext_be
, 1);
1101 /* if/when we decide to utf8-encode attrs, take out this next line */
1102 TALLOC_FREE(search_attrs
);
1104 return ADS_ERROR(rc
);
1107 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1108 int scope
, const char *expr
,
1109 const char **attrs
, LDAPMessage
**res
,
1110 int *count
, struct berval
**cookie
)
1112 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1117 * Get all results for a search. This uses ads_do_paged_search() to return
1118 * all entries in a large search.
1119 * @param ads connection to ads server
1120 * @param bind_path Base dn for the search
1121 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1122 * @param expr Search expression
1123 * @param attrs Attributes to retrieve
1124 * @param res ** which will contain results - free res* with ads_msgfree()
1125 * @return status of search
1127 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1128 int scope
, const char *expr
,
1129 const char **attrs
, void *args
,
1132 struct berval
*cookie
= NULL
;
1137 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1140 if (!ADS_ERR_OK(status
))
1143 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1145 LDAPMessage
*res2
= NULL
;
1147 LDAPMessage
*msg
, *next
;
1149 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1150 attrs
, args
, &res2
, &count
, &cookie
);
1152 if (!ADS_ERR_OK(status2
)) break;
1154 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1155 that this works on all ldap libs, but I have only tested with openldap */
1156 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1157 next
= ads_next_message(ads
, msg
);
1158 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1160 /* note that we do not free res2, as the memory is now
1161 part of the main returned list */
1164 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1165 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1171 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1172 int scope
, const char *expr
,
1173 const char **attrs
, LDAPMessage
**res
)
1175 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1178 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1179 int scope
, const char *expr
,
1180 const char **attrs
, uint32 sd_flags
,
1185 args
.control
= ADS_SD_FLAGS_OID
;
1186 args
.val
= sd_flags
;
1187 args
.critical
= True
;
1189 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1194 * Run a function on all results for a search. Uses ads_do_paged_search() and
1195 * runs the function as each page is returned, using ads_process_results()
1196 * @param ads connection to ads server
1197 * @param bind_path Base dn for the search
1198 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1199 * @param expr Search expression - specified in local charset
1200 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1201 * @param fn Function which takes attr name, values list, and data_area
1202 * @param data_area Pointer which is passed to function on each call
1203 * @return status of search
1205 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1206 int scope
, const char *expr
, const char **attrs
,
1207 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1210 struct berval
*cookie
= NULL
;
1215 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1218 if (!ADS_ERR_OK(status
)) return status
;
1220 ads_process_results(ads
, res
, fn
, data_area
);
1221 ads_msgfree(ads
, res
);
1224 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1225 &res
, &count
, &cookie
);
1227 if (!ADS_ERR_OK(status
)) break;
1229 ads_process_results(ads
, res
, fn
, data_area
);
1230 ads_msgfree(ads
, res
);
1237 * Do a search with a timeout.
1238 * @param ads connection to ads server
1239 * @param bind_path Base dn for the search
1240 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1241 * @param expr Search expression
1242 * @param attrs Attributes to retrieve
1243 * @param res ** which will contain results - free res* with ads_msgfree()
1244 * @return status of search
1246 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1248 const char **attrs
, LDAPMessage
**res
)
1251 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1252 size_t converted_size
;
1256 if (!(ctx
= talloc_init("ads_do_search"))) {
1257 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1258 return ADS_ERROR(LDAP_NO_MEMORY
);
1261 /* 0 means the conversion worked but the result was empty
1262 so we only fail if it's negative. In any case, it always
1263 at least nulls out the dest */
1264 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1265 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1267 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1268 rc
= LDAP_NO_MEMORY
;
1272 if (!attrs
|| !(*attrs
))
1273 search_attrs
= NULL
;
1275 /* This would be the utf8-encoded version...*/
1276 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1277 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1279 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1280 rc
= LDAP_NO_MEMORY
;
1285 /* see the note in ads_do_paged_search - we *must* disable referrals */
1286 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1288 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1289 search_attrs
, 0, NULL
, NULL
,
1291 (LDAPMessage
**)res
);
1293 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1294 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1299 talloc_destroy(ctx
);
1300 /* if/when we decide to utf8-encode attrs, take out this next line */
1301 TALLOC_FREE(search_attrs
);
1302 return ADS_ERROR(rc
);
1305 * Do a general ADS search
1306 * @param ads connection to ads server
1307 * @param res ** which will contain results - free res* with ads_msgfree()
1308 * @param expr Search expression
1309 * @param attrs Attributes to retrieve
1310 * @return status of search
1312 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1313 const char *expr
, const char **attrs
)
1315 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1320 * Do a search on a specific DistinguishedName
1321 * @param ads connection to ads server
1322 * @param res ** which will contain results - free res* with ads_msgfree()
1323 * @param dn DistinguishName to search
1324 * @param attrs Attributes to retrieve
1325 * @return status of search
1327 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1328 const char *dn
, const char **attrs
)
1330 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1335 * Free up memory from a ads_search
1336 * @param ads connection to ads server
1337 * @param msg Search results to free
1339 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1346 * Get a dn from search results
1347 * @param ads connection to ads server
1348 * @param msg Search result
1351 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1353 char *utf8_dn
, *unix_dn
;
1354 size_t converted_size
;
1356 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1359 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1363 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1364 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1368 ldap_memfree(utf8_dn
);
1373 * Get the parent from a dn
1374 * @param dn the dn to return the parent from
1375 * @return parent dn string
1377 char *ads_parent_dn(const char *dn
)
1385 p
= strchr(dn
, ',');
1395 * Find a machine account given a hostname
1396 * @param ads connection to ads server
1397 * @param res ** which will contain results - free res* with ads_msgfree()
1398 * @param host Hostname to search for
1399 * @return status of search
1401 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1402 const char *machine
)
1406 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1410 /* the easiest way to find a machine account anywhere in the tree
1411 is to look for hostname$ */
1412 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1413 DEBUG(1, ("asprintf failed!\n"));
1414 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1417 status
= ads_search(ads
, res
, expr
, attrs
);
1423 * Initialize a list of mods to be used in a modify request
1424 * @param ctx An initialized TALLOC_CTX
1425 * @return allocated ADS_MODLIST
1427 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1429 #define ADS_MODLIST_ALLOC_SIZE 10
1432 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1433 /* -1 is safety to make sure we don't go over the end.
1434 need to reset it to NULL before doing ldap modify */
1435 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1437 return (ADS_MODLIST
)mods
;
1442 add an attribute to the list, with values list already constructed
1444 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1445 int mod_op
, const char *name
,
1446 const void *_invals
)
1448 const void **invals
= (const void **)_invals
;
1450 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1451 struct berval
**ber_values
= NULL
;
1452 char **char_values
= NULL
;
1455 mod_op
= LDAP_MOD_DELETE
;
1457 if (mod_op
& LDAP_MOD_BVALUES
)
1458 ber_values
= ads_dup_values(ctx
,
1459 (const struct berval
**)invals
);
1461 char_values
= ads_push_strvals(ctx
,
1462 (const char **) invals
);
1465 /* find the first empty slot */
1466 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1468 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1469 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1470 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1471 return ADS_ERROR(LDAP_NO_MEMORY
);
1472 memset(&modlist
[curmod
], 0,
1473 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1474 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1475 *mods
= (ADS_MODLIST
)modlist
;
1478 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1479 return ADS_ERROR(LDAP_NO_MEMORY
);
1480 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1481 if (mod_op
& LDAP_MOD_BVALUES
) {
1482 modlist
[curmod
]->mod_bvalues
= ber_values
;
1483 } else if (mod_op
& LDAP_MOD_DELETE
) {
1484 modlist
[curmod
]->mod_values
= NULL
;
1486 modlist
[curmod
]->mod_values
= char_values
;
1489 modlist
[curmod
]->mod_op
= mod_op
;
1490 return ADS_ERROR(LDAP_SUCCESS
);
1494 * Add a single string value to a mod list
1495 * @param ctx An initialized TALLOC_CTX
1496 * @param mods An initialized ADS_MODLIST
1497 * @param name The attribute name to add
1498 * @param val The value to add - NULL means DELETE
1499 * @return ADS STATUS indicating success of add
1501 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1502 const char *name
, const char *val
)
1504 const char *values
[2];
1510 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1511 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1515 * Add an array of string values to a mod list
1516 * @param ctx An initialized TALLOC_CTX
1517 * @param mods An initialized ADS_MODLIST
1518 * @param name The attribute name to add
1519 * @param vals The array of string values to add - NULL means DELETE
1520 * @return ADS STATUS indicating success of add
1522 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1523 const char *name
, const char **vals
)
1526 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1527 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1528 name
, (const void **) vals
);
1533 * Add a single ber-encoded value to a mod list
1534 * @param ctx An initialized TALLOC_CTX
1535 * @param mods An initialized ADS_MODLIST
1536 * @param name The attribute name to add
1537 * @param val The value to add - NULL means DELETE
1538 * @return ADS STATUS indicating success of add
1540 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1541 const char *name
, const struct berval
*val
)
1543 const struct berval
*values
[2];
1548 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1549 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1550 name
, (const void **) values
);
1555 * Perform an ldap modify
1556 * @param ads connection to ads server
1557 * @param mod_dn DistinguishedName to modify
1558 * @param mods list of modifications to perform
1559 * @return status of modify
1561 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1564 char *utf8_dn
= NULL
;
1565 size_t converted_size
;
1567 this control is needed to modify that contains a currently
1568 non-existent attribute (but allowable for the object) to run
1570 LDAPControl PermitModify
= {
1571 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1574 LDAPControl
*controls
[2];
1576 controls
[0] = &PermitModify
;
1579 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1580 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1583 /* find the end of the list, marked by NULL or -1 */
1584 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1585 /* make sure the end of the list is NULL */
1587 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1588 (LDAPMod
**) mods
, controls
, NULL
);
1589 TALLOC_FREE(utf8_dn
);
1590 return ADS_ERROR(ret
);
1594 * Perform an ldap add
1595 * @param ads connection to ads server
1596 * @param new_dn DistinguishedName to add
1597 * @param mods list of attributes and values for DN
1598 * @return status of add
1600 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1603 char *utf8_dn
= NULL
;
1604 size_t converted_size
;
1606 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1607 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1608 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1611 /* find the end of the list, marked by NULL or -1 */
1612 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1613 /* make sure the end of the list is NULL */
1616 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1617 TALLOC_FREE(utf8_dn
);
1618 return ADS_ERROR(ret
);
1622 * Delete a DistinguishedName
1623 * @param ads connection to ads server
1624 * @param new_dn DistinguishedName to delete
1625 * @return status of delete
1627 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1630 char *utf8_dn
= NULL
;
1631 size_t converted_size
;
1632 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1633 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1634 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1637 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1638 TALLOC_FREE(utf8_dn
);
1639 return ADS_ERROR(ret
);
1643 * Build an org unit string
1644 * if org unit is Computers or blank then assume a container, otherwise
1645 * assume a / separated list of organisational units.
1646 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1647 * @param ads connection to ads server
1648 * @param org_unit Organizational unit
1649 * @return org unit string - caller must free
1651 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1655 if (!org_unit
|| !*org_unit
) {
1657 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1659 /* samba4 might not yet respond to a wellknownobject-query */
1660 return ret
? ret
: SMB_STRDUP("cn=Computers");
1663 if (strequal(org_unit
, "Computers")) {
1664 return SMB_STRDUP("cn=Computers");
1667 /* jmcd: removed "\\" from the separation chars, because it is
1668 needed as an escape for chars like '#' which are valid in an
1670 return ads_build_path(org_unit
, "/", "ou=", 1);
1674 * Get a org unit string for a well-known GUID
1675 * @param ads connection to ads server
1676 * @param wknguid Well known GUID
1677 * @return org unit string - caller must free
1679 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1682 LDAPMessage
*res
= NULL
;
1683 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1684 **bind_dn_exp
= NULL
;
1685 const char *attrs
[] = {"distinguishedName", NULL
};
1686 int new_ln
, wkn_ln
, bind_ln
, i
;
1688 if (wknguid
== NULL
) {
1692 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1693 DEBUG(1, ("asprintf failed!\n"));
1697 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1698 if (!ADS_ERR_OK(status
)) {
1699 DEBUG(1,("Failed while searching for: %s\n", base
));
1703 if (ads_count_replies(ads
, res
) != 1) {
1707 /* substitute the bind-path from the well-known-guid-search result */
1708 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1713 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1718 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1723 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1725 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1728 new_ln
= wkn_ln
- bind_ln
;
1730 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1735 for (i
=1; i
< new_ln
; i
++) {
1738 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1744 ret
= SMB_STRDUP(s
);
1753 ads_msgfree(ads
, res
);
1754 TALLOC_FREE(wkn_dn
);
1756 ldap_value_free(wkn_dn_exp
);
1759 ldap_value_free(bind_dn_exp
);
1766 * Adds (appends) an item to an attribute array, rather then
1767 * replacing the whole list
1768 * @param ctx An initialized TALLOC_CTX
1769 * @param mods An initialized ADS_MODLIST
1770 * @param name name of the ldap attribute to append to
1771 * @param vals an array of values to add
1772 * @return status of addition
1775 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1776 const char *name
, const char **vals
)
1778 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1779 (const void *) vals
);
1783 * Determines the an account's current KVNO via an LDAP lookup
1784 * @param ads An initialized ADS_STRUCT
1785 * @param account_name the NT samaccountname.
1786 * @return the kvno for the account, or -1 in case of a failure.
1789 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1791 LDAPMessage
*res
= NULL
;
1792 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1794 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1795 char *dn_string
= NULL
;
1796 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1798 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1799 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1802 ret
= ads_search(ads
, &res
, filter
, attrs
);
1804 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1805 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1806 ads_msgfree(ads
, res
);
1810 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1812 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1813 ads_msgfree(ads
, res
);
1816 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1817 TALLOC_FREE(dn_string
);
1819 /* ---------------------------------------------------------
1820 * 0 is returned as a default KVNO from this point on...
1821 * This is done because Windows 2000 does not support key
1822 * version numbers. Chances are that a failure in the next
1823 * step is simply due to Windows 2000 being used for a
1824 * domain controller. */
1827 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1828 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1829 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1830 ads_msgfree(ads
, res
);
1835 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1836 ads_msgfree(ads
, res
);
1841 * Determines the computer account's current KVNO via an LDAP lookup
1842 * @param ads An initialized ADS_STRUCT
1843 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1844 * @return the kvno for the computer account, or -1 in case of a failure.
1847 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1849 char *computer_account
= NULL
;
1852 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1856 kvno
= ads_get_kvno(ads
, computer_account
);
1857 free(computer_account
);
1863 * This clears out all registered spn's for a given hostname
1864 * @param ads An initilaized ADS_STRUCT
1865 * @param machine_name the NetBIOS name of the computer.
1866 * @return 0 upon success, non-zero otherwise.
1869 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1872 LDAPMessage
*res
= NULL
;
1874 const char *servicePrincipalName
[1] = {NULL
};
1875 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1876 char *dn_string
= NULL
;
1878 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1879 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1880 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1881 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1882 ads_msgfree(ads
, res
);
1883 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1886 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1887 ctx
= talloc_init("ads_clear_service_principal_names");
1889 ads_msgfree(ads
, res
);
1890 return ADS_ERROR(LDAP_NO_MEMORY
);
1893 if (!(mods
= ads_init_mods(ctx
))) {
1894 talloc_destroy(ctx
);
1895 ads_msgfree(ads
, res
);
1896 return ADS_ERROR(LDAP_NO_MEMORY
);
1898 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1899 if (!ADS_ERR_OK(ret
)) {
1900 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1901 ads_msgfree(ads
, res
);
1902 talloc_destroy(ctx
);
1905 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1907 talloc_destroy(ctx
);
1908 ads_msgfree(ads
, res
);
1909 return ADS_ERROR(LDAP_NO_MEMORY
);
1911 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1912 TALLOC_FREE(dn_string
);
1913 if (!ADS_ERR_OK(ret
)) {
1914 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1916 ads_msgfree(ads
, res
);
1917 talloc_destroy(ctx
);
1921 ads_msgfree(ads
, res
);
1922 talloc_destroy(ctx
);
1927 * This adds a service principal name to an existing computer account
1928 * (found by hostname) in AD.
1929 * @param ads An initialized ADS_STRUCT
1930 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1931 * @param my_fqdn The fully qualified DNS name of the machine
1932 * @param spn A string of the service principal to add, i.e. 'host'
1933 * @return 0 upon sucess, or non-zero if a failure occurs
1936 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1937 const char *my_fqdn
, const char *spn
)
1941 LDAPMessage
*res
= NULL
;
1944 char *dn_string
= NULL
;
1945 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1947 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1948 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1949 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1951 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1952 spn
, machine_name
, ads
->config
.realm
));
1953 ads_msgfree(ads
, res
);
1954 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1957 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1958 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1959 ads_msgfree(ads
, res
);
1960 return ADS_ERROR(LDAP_NO_MEMORY
);
1963 /* add short name spn */
1965 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1966 talloc_destroy(ctx
);
1967 ads_msgfree(ads
, res
);
1968 return ADS_ERROR(LDAP_NO_MEMORY
);
1970 if (!strlower_m(&psp1
[strlen(spn
) + 1])) {
1971 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1974 servicePrincipalName
[0] = psp1
;
1976 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1977 psp1
, machine_name
));
1980 /* add fully qualified spn */
1982 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1983 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1986 if (!strlower_m(&psp2
[strlen(spn
) + 1])) {
1987 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1990 servicePrincipalName
[1] = psp2
;
1992 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1993 psp2
, machine_name
));
1995 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1996 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2000 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2001 if (!ADS_ERR_OK(ret
)) {
2002 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2006 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2007 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2011 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2012 if (!ADS_ERR_OK(ret
)) {
2013 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2019 ads_msgfree(ads
, res
);
2024 * adds a machine account to the ADS server
2025 * @param ads An intialized ADS_STRUCT
2026 * @param machine_name - the NetBIOS machine name of this account.
2027 * @param account_type A number indicating the type of account to create
2028 * @param org_unit The LDAP path in which to place this account
2029 * @return 0 upon success, or non-zero otherwise
2032 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2033 const char *org_unit
)
2036 char *samAccountName
, *controlstr
;
2039 char *machine_escaped
= NULL
;
2041 const char *objectClass
[] = {"top", "person", "organizationalPerson",
2042 "user", "computer", NULL
};
2043 LDAPMessage
*res
= NULL
;
2044 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
2045 UF_DONT_EXPIRE_PASSWD
|\
2046 UF_ACCOUNTDISABLE
);
2048 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2049 return ADS_ERROR(LDAP_NO_MEMORY
);
2051 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2053 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2054 if (!machine_escaped
) {
2058 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2059 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2061 if ( !new_dn
|| !samAccountName
) {
2065 #ifndef ENCTYPE_ARCFOUR_HMAC
2066 acct_control
|= UF_USE_DES_KEY_ONLY
;
2069 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2073 if (!(mods
= ads_init_mods(ctx
))) {
2077 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2078 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2079 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2080 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2082 ret
= ads_gen_add(ads
, new_dn
, mods
);
2085 SAFE_FREE(machine_escaped
);
2086 ads_msgfree(ads
, res
);
2087 talloc_destroy(ctx
);
2093 * move a machine account to another OU on the ADS server
2094 * @param ads - An intialized ADS_STRUCT
2095 * @param machine_name - the NetBIOS machine name of this account.
2096 * @param org_unit - The LDAP path in which to place this account
2097 * @param moved - whether we moved the machine account (optional)
2098 * @return 0 upon success, or non-zero otherwise
2101 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2102 const char *org_unit
, bool *moved
)
2106 LDAPMessage
*res
= NULL
;
2107 char *filter
= NULL
;
2108 char *computer_dn
= NULL
;
2110 char *computer_rdn
= NULL
;
2111 bool need_move
= False
;
2113 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2114 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2118 /* Find pre-existing machine */
2119 rc
= ads_search(ads
, &res
, filter
, NULL
);
2120 if (!ADS_ERR_OK(rc
)) {
2124 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2126 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2130 parent_dn
= ads_parent_dn(computer_dn
);
2131 if (strequal(parent_dn
, org_unit
)) {
2137 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2138 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2142 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2143 org_unit
, 1, NULL
, NULL
);
2144 rc
= ADS_ERROR(ldap_status
);
2147 ads_msgfree(ads
, res
);
2149 TALLOC_FREE(computer_dn
);
2150 SAFE_FREE(computer_rdn
);
2152 if (!ADS_ERR_OK(rc
)) {
2164 dump a binary result from ldap
2166 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2169 for (i
=0; values
[i
]; i
++) {
2170 printf("%s: ", field
);
2171 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2172 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2178 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2181 for (i
=0; values
[i
]; i
++) {
2183 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2186 status
= GUID_from_ndr_blob(&in
, &guid
);
2187 if (NT_STATUS_IS_OK(status
)) {
2188 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2190 printf("%s: INVALID GUID\n", field
);
2196 dump a sid result from ldap
2198 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2201 for (i
=0; values
[i
]; i
++) {
2204 if (!sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
)) {
2207 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2212 dump ntSecurityDescriptor
2214 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2216 TALLOC_CTX
*frame
= talloc_stackframe();
2217 struct security_descriptor
*psd
;
2220 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2221 values
[0]->bv_len
, &psd
);
2222 if (!NT_STATUS_IS_OK(status
)) {
2223 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2224 nt_errstr(status
)));
2230 ads_disp_sd(ads
, talloc_tos(), psd
);
2237 dump a string result from ldap
2239 static void dump_string(const char *field
, char **values
)
2242 for (i
=0; values
[i
]; i
++) {
2243 printf("%s: %s\n", field
, values
[i
]);
2248 dump a field from LDAP on stdout
2252 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2257 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2259 {"objectGUID", False
, dump_guid
},
2260 {"netbootGUID", False
, dump_guid
},
2261 {"nTSecurityDescriptor", False
, dump_sd
},
2262 {"dnsRecord", False
, dump_binary
},
2263 {"objectSid", False
, dump_sid
},
2264 {"tokenGroups", False
, dump_sid
},
2265 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2266 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2267 {"mS-DS-CreatorSID", False
, dump_sid
},
2268 {"msExchMailboxGuid", False
, dump_guid
},
2273 if (!field
) { /* must be end of an entry */
2278 for (i
=0; handlers
[i
].name
; i
++) {
2279 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2280 if (!values
) /* first time, indicate string or not */
2281 return handlers
[i
].string
;
2282 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2286 if (!handlers
[i
].name
) {
2287 if (!values
) /* first time, indicate string conversion */
2289 dump_string(field
, (char **)values
);
2295 * Dump a result from LDAP on stdout
2296 * used for debugging
2297 * @param ads connection to ads server
2298 * @param res Results to dump
2301 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2303 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2307 * Walk through results, calling a function for each entry found.
2308 * The function receives a field name, a berval * array of values,
2309 * and a data area passed through from the start. The function is
2310 * called once with null for field and values at the end of each
2312 * @param ads connection to ads server
2313 * @param res Results to process
2314 * @param fn Function for processing each result
2315 * @param data_area user-defined area to pass to function
2317 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2318 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2323 size_t converted_size
;
2325 if (!(ctx
= talloc_init("ads_process_results")))
2328 for (msg
= ads_first_entry(ads
, res
); msg
;
2329 msg
= ads_next_entry(ads
, msg
)) {
2333 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2334 (LDAPMessage
*)msg
,&b
);
2336 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2337 (LDAPMessage
*)msg
,b
)) {
2338 struct berval
**ber_vals
;
2339 char **str_vals
, **utf8_vals
;
2343 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2346 DEBUG(0,("ads_process_results: "
2347 "pull_utf8_talloc failed: %s",
2351 string
= fn(ads
, field
, NULL
, data_area
);
2354 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2355 (LDAPMessage
*)msg
, field
);
2356 str_vals
= ads_pull_strvals(ctx
,
2357 (const char **) utf8_vals
);
2358 fn(ads
, field
, (void **) str_vals
, data_area
);
2359 ldap_value_free(utf8_vals
);
2361 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2362 (LDAPMessage
*)msg
, field
);
2363 fn(ads
, field
, (void **) ber_vals
, data_area
);
2365 ldap_value_free_len(ber_vals
);
2367 ldap_memfree(utf8_field
);
2370 talloc_free_children(ctx
);
2371 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2374 talloc_destroy(ctx
);
2378 * count how many replies are in a LDAPMessage
2379 * @param ads connection to ads server
2380 * @param res Results to count
2381 * @return number of replies
2383 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2385 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2389 * pull the first entry from a ADS result
2390 * @param ads connection to ads server
2391 * @param res Results of search
2392 * @return first entry from result
2394 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2396 return ldap_first_entry(ads
->ldap
.ld
, res
);
2400 * pull the next entry from a ADS result
2401 * @param ads connection to ads server
2402 * @param res Results of search
2403 * @return next entry from result
2405 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2407 return ldap_next_entry(ads
->ldap
.ld
, res
);
2411 * pull the first message from a ADS result
2412 * @param ads connection to ads server
2413 * @param res Results of search
2414 * @return first message from result
2416 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2418 return ldap_first_message(ads
->ldap
.ld
, res
);
2422 * pull the next message from a ADS result
2423 * @param ads connection to ads server
2424 * @param res Results of search
2425 * @return next message from result
2427 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2429 return ldap_next_message(ads
->ldap
.ld
, res
);
2433 * pull a single string from a ADS result
2434 * @param ads connection to ads server
2435 * @param mem_ctx TALLOC_CTX to use for allocating result string
2436 * @param msg Results of search
2437 * @param field Attribute to retrieve
2438 * @return Result string in talloc context
2440 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2446 size_t converted_size
;
2448 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2452 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2457 ldap_value_free(values
);
2462 * pull an array of strings from a ADS result
2463 * @param ads connection to ads server
2464 * @param mem_ctx TALLOC_CTX to use for allocating result string
2465 * @param msg Results of search
2466 * @param field Attribute to retrieve
2467 * @return Result strings in talloc context
2469 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2470 LDAPMessage
*msg
, const char *field
,
2476 size_t converted_size
;
2478 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2482 *num_values
= ldap_count_values(values
);
2484 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2486 ldap_value_free(values
);
2490 for (i
=0;i
<*num_values
;i
++) {
2491 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2494 ldap_value_free(values
);
2500 ldap_value_free(values
);
2505 * pull an array of strings from a ADS result
2506 * (handle large multivalue attributes with range retrieval)
2507 * @param ads connection to ads server
2508 * @param mem_ctx TALLOC_CTX to use for allocating result string
2509 * @param msg Results of search
2510 * @param field Attribute to retrieve
2511 * @param current_strings strings returned by a previous call to this function
2512 * @param next_attribute The next query should ask for this attribute
2513 * @param num_values How many values did we get this time?
2514 * @param more_values Are there more values to get?
2515 * @return Result strings in talloc context
2517 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2518 TALLOC_CTX
*mem_ctx
,
2519 LDAPMessage
*msg
, const char *field
,
2520 char **current_strings
,
2521 const char **next_attribute
,
2522 size_t *num_strings
,
2526 char *expected_range_attrib
, *range_attr
;
2527 BerElement
*ptr
= NULL
;
2530 size_t num_new_strings
;
2531 unsigned long int range_start
;
2532 unsigned long int range_end
;
2534 /* we might have been given the whole lot anyway */
2535 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2536 *more_strings
= False
;
2540 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2542 /* look for Range result */
2543 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2545 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2546 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2547 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2555 /* nothing here - this field is just empty */
2556 *more_strings
= False
;
2560 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2561 &range_start
, &range_end
) == 2) {
2562 *more_strings
= True
;
2564 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2565 &range_start
) == 1) {
2566 *more_strings
= False
;
2568 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2570 ldap_memfree(range_attr
);
2571 *more_strings
= False
;
2576 if ((*num_strings
) != range_start
) {
2577 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2578 " - aborting range retreival\n",
2579 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2580 ldap_memfree(range_attr
);
2581 *more_strings
= False
;
2585 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2587 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2588 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2589 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2590 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2591 (unsigned long int)num_new_strings
));
2592 ldap_memfree(range_attr
);
2593 *more_strings
= False
;
2597 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2598 *num_strings
+ num_new_strings
);
2600 if (strings
== NULL
) {
2601 ldap_memfree(range_attr
);
2602 *more_strings
= False
;
2606 if (new_strings
&& num_new_strings
) {
2607 memcpy(&strings
[*num_strings
], new_strings
,
2608 sizeof(*new_strings
) * num_new_strings
);
2611 (*num_strings
) += num_new_strings
;
2613 if (*more_strings
) {
2614 *next_attribute
= talloc_asprintf(mem_ctx
,
2619 if (!*next_attribute
) {
2620 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2621 ldap_memfree(range_attr
);
2622 *more_strings
= False
;
2627 ldap_memfree(range_attr
);
2633 * pull a single uint32 from a ADS result
2634 * @param ads connection to ads server
2635 * @param msg Results of search
2636 * @param field Attribute to retrieve
2637 * @param v Pointer to int to store result
2638 * @return boolean inidicating success
2640 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2645 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2649 ldap_value_free(values
);
2653 *v
= atoi(values
[0]);
2654 ldap_value_free(values
);
2659 * pull a single objectGUID from an ADS result
2660 * @param ads connection to ADS server
2661 * @param msg results of search
2662 * @param guid 37-byte area to receive text guid
2663 * @return boolean indicating success
2665 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2670 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2675 status
= GUID_from_ndr_blob(&blob
, guid
);
2676 talloc_free(blob
.data
);
2677 return NT_STATUS_IS_OK(status
);
2682 * pull a single struct dom_sid from a ADS result
2683 * @param ads connection to ads server
2684 * @param msg Results of search
2685 * @param field Attribute to retrieve
2686 * @param sid Pointer to sid to store result
2687 * @return boolean inidicating success
2689 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2690 struct dom_sid
*sid
)
2692 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2696 * pull an array of struct dom_sids from a ADS result
2697 * @param ads connection to ads server
2698 * @param mem_ctx TALLOC_CTX for allocating sid array
2699 * @param msg Results of search
2700 * @param field Attribute to retrieve
2701 * @param sids pointer to sid array to allocate
2702 * @return the count of SIDs pulled
2704 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2705 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2707 struct berval
**values
;
2711 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2716 for (i
=0; values
[i
]; i
++)
2720 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2722 ldap_value_free_len(values
);
2730 for (i
=0; values
[i
]; i
++) {
2731 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2733 DEBUG(10, ("pulling SID: %s\n",
2734 sid_string_dbg(&(*sids
)[count
])));
2739 ldap_value_free_len(values
);
2744 * pull a struct security_descriptor from a ADS result
2745 * @param ads connection to ads server
2746 * @param mem_ctx TALLOC_CTX for allocating sid array
2747 * @param msg Results of search
2748 * @param field Attribute to retrieve
2749 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2750 * @return boolean inidicating success
2752 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2753 LDAPMessage
*msg
, const char *field
,
2754 struct security_descriptor
**sd
)
2756 struct berval
**values
;
2759 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2761 if (!values
) return false;
2765 status
= unmarshall_sec_desc(mem_ctx
,
2766 (uint8
*)values
[0]->bv_val
,
2767 values
[0]->bv_len
, sd
);
2768 if (!NT_STATUS_IS_OK(status
)) {
2769 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2770 nt_errstr(status
)));
2775 ldap_value_free_len(values
);
2780 * in order to support usernames longer than 21 characters we need to
2781 * use both the sAMAccountName and the userPrincipalName attributes
2782 * It seems that not all users have the userPrincipalName attribute set
2784 * @param ads connection to ads server
2785 * @param mem_ctx TALLOC_CTX for allocating sid array
2786 * @param msg Results of search
2787 * @return the username
2789 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2795 /* lookup_name() only works on the sAMAccountName to
2796 returning the username portion of userPrincipalName
2797 breaks winbindd_getpwnam() */
2799 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2800 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2805 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2810 * find the update serial number - this is the core of the ldap cache
2811 * @param ads connection to ads server
2812 * @param ads connection to ADS server
2813 * @param usn Pointer to retrieved update serial number
2814 * @return status of search
2816 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2818 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2822 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2823 if (!ADS_ERR_OK(status
))
2826 if (ads_count_replies(ads
, res
) != 1) {
2827 ads_msgfree(ads
, res
);
2828 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2831 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2832 ads_msgfree(ads
, res
);
2833 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2836 ads_msgfree(ads
, res
);
2840 /* parse a ADS timestring - typical string is
2841 '20020917091222.0Z0' which means 09:12.22 17th September
2843 static time_t ads_parse_time(const char *str
)
2849 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2850 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2851 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2860 /********************************************************************
2861 ********************************************************************/
2863 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2865 const char *attrs
[] = {"currentTime", NULL
};
2870 ADS_STRUCT
*ads_s
= ads
;
2872 if (!(ctx
= talloc_init("ads_current_time"))) {
2873 return ADS_ERROR(LDAP_NO_MEMORY
);
2876 /* establish a new ldap tcp session if necessary */
2878 if ( !ads
->ldap
.ld
) {
2879 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2880 ads
->server
.ldap_server
)) == NULL
)
2884 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2885 status
= ads_connect( ads_s
);
2886 if ( !ADS_ERR_OK(status
))
2890 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2891 if (!ADS_ERR_OK(status
)) {
2895 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2897 ads_msgfree(ads_s
, res
);
2898 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2902 /* but save the time and offset in the original ADS_STRUCT */
2904 ads
->config
.current_time
= ads_parse_time(timestr
);
2906 if (ads
->config
.current_time
!= 0) {
2907 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2908 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
2911 ads_msgfree(ads
, res
);
2913 status
= ADS_SUCCESS
;
2916 /* free any temporary ads connections */
2917 if ( ads_s
!= ads
) {
2918 ads_destroy( &ads_s
);
2920 talloc_destroy(ctx
);
2925 /********************************************************************
2926 ********************************************************************/
2928 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2930 const char *attrs
[] = {"domainFunctionality", NULL
};
2933 ADS_STRUCT
*ads_s
= ads
;
2935 *val
= DS_DOMAIN_FUNCTION_2000
;
2937 /* establish a new ldap tcp session if necessary */
2939 if ( !ads
->ldap
.ld
) {
2940 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2941 ads
->server
.ldap_server
)) == NULL
)
2943 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2946 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2947 status
= ads_connect( ads_s
);
2948 if ( !ADS_ERR_OK(status
))
2952 /* If the attribute does not exist assume it is a Windows 2000
2953 functional domain */
2955 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2956 if (!ADS_ERR_OK(status
)) {
2957 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2958 status
= ADS_SUCCESS
;
2963 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2964 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2966 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2969 ads_msgfree(ads
, res
);
2972 /* free any temporary ads connections */
2973 if ( ads_s
!= ads
) {
2974 ads_destroy( &ads_s
);
2981 * find the domain sid for our domain
2982 * @param ads connection to ads server
2983 * @param sid Pointer to domain sid
2984 * @return status of search
2986 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
2988 const char *attrs
[] = {"objectSid", NULL
};
2992 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2994 if (!ADS_ERR_OK(rc
)) return rc
;
2995 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2996 ads_msgfree(ads
, res
);
2997 return ADS_ERROR_SYSTEM(ENOENT
);
2999 ads_msgfree(ads
, res
);
3005 * find our site name
3006 * @param ads connection to ads server
3007 * @param mem_ctx Pointer to talloc context
3008 * @param site_name Pointer to the sitename
3009 * @return status of search
3011 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3015 const char *dn
, *service_name
;
3016 const char *attrs
[] = { "dsServiceName", NULL
};
3018 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3019 if (!ADS_ERR_OK(status
)) {
3023 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3024 if (service_name
== NULL
) {
3025 ads_msgfree(ads
, res
);
3026 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3029 ads_msgfree(ads
, res
);
3031 /* go up three levels */
3032 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3034 return ADS_ERROR(LDAP_NO_MEMORY
);
3037 *site_name
= talloc_strdup(mem_ctx
, dn
);
3038 if (*site_name
== NULL
) {
3039 return ADS_ERROR(LDAP_NO_MEMORY
);
3044 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3049 * find the site dn where a machine resides
3050 * @param ads connection to ads server
3051 * @param mem_ctx Pointer to talloc context
3052 * @param computer_name name of the machine
3053 * @param site_name Pointer to the sitename
3054 * @return status of search
3056 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3060 const char *parent
, *filter
;
3061 char *config_context
= NULL
;
3064 /* shortcut a query */
3065 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3066 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3069 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3070 if (!ADS_ERR_OK(status
)) {
3074 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3075 if (filter
== NULL
) {
3076 return ADS_ERROR(LDAP_NO_MEMORY
);
3079 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3080 filter
, NULL
, &res
);
3081 if (!ADS_ERR_OK(status
)) {
3085 if (ads_count_replies(ads
, res
) != 1) {
3086 ads_msgfree(ads
, res
);
3087 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3090 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3092 ads_msgfree(ads
, res
);
3093 return ADS_ERROR(LDAP_NO_MEMORY
);
3096 /* go up three levels */
3097 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3098 if (parent
== NULL
) {
3099 ads_msgfree(ads
, res
);
3101 return ADS_ERROR(LDAP_NO_MEMORY
);
3104 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3105 if (*site_dn
== NULL
) {
3106 ads_msgfree(ads
, res
);
3108 return ADS_ERROR(LDAP_NO_MEMORY
);
3112 ads_msgfree(ads
, res
);
3118 * get the upn suffixes for a domain
3119 * @param ads connection to ads server
3120 * @param mem_ctx Pointer to talloc context
3121 * @param suffixes Pointer to an array of suffixes
3122 * @param num_suffixes Pointer to the number of suffixes
3123 * @return status of search
3125 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3130 char *config_context
= NULL
;
3131 const char *attrs
[] = { "uPNSuffixes", NULL
};
3133 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3134 if (!ADS_ERR_OK(status
)) {
3138 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3140 return ADS_ERROR(LDAP_NO_MEMORY
);
3143 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3144 if (!ADS_ERR_OK(status
)) {
3148 if (ads_count_replies(ads
, res
) != 1) {
3149 ads_msgfree(ads
, res
);
3150 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3153 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3154 if ((*suffixes
) == NULL
) {
3155 ads_msgfree(ads
, res
);
3156 return ADS_ERROR(LDAP_NO_MEMORY
);
3159 ads_msgfree(ads
, res
);
3165 * get the joinable ous for a domain
3166 * @param ads connection to ads server
3167 * @param mem_ctx Pointer to talloc context
3168 * @param ous Pointer to an array of ous
3169 * @param num_ous Pointer to the number of ous
3170 * @return status of search
3172 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3173 TALLOC_CTX
*mem_ctx
,
3178 LDAPMessage
*res
= NULL
;
3179 LDAPMessage
*msg
= NULL
;
3180 const char *attrs
[] = { "dn", NULL
};
3183 status
= ads_search(ads
, &res
,
3184 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3186 if (!ADS_ERR_OK(status
)) {
3190 count
= ads_count_replies(ads
, res
);
3192 ads_msgfree(ads
, res
);
3193 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3196 for (msg
= ads_first_entry(ads
, res
); msg
;
3197 msg
= ads_next_entry(ads
, msg
)) {
3201 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3203 ads_msgfree(ads
, res
);
3204 return ADS_ERROR(LDAP_NO_MEMORY
);
3207 if (!add_string_to_array(mem_ctx
, dn
,
3208 (const char ***)ous
,
3211 ads_msgfree(ads
, res
);
3212 return ADS_ERROR(LDAP_NO_MEMORY
);
3218 ads_msgfree(ads
, res
);
3225 * pull a struct dom_sid from an extended dn string
3226 * @param mem_ctx TALLOC_CTX
3227 * @param extended_dn string
3228 * @param flags string type of extended_dn
3229 * @param sid pointer to a struct dom_sid
3230 * @return NT_STATUS_OK on success,
3231 * NT_INVALID_PARAMETER on error,
3232 * NT_STATUS_NOT_FOUND if no SID present
3234 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3235 const char *extended_dn
,
3236 enum ads_extended_dn_flags flags
,
3237 struct dom_sid
*sid
)
3242 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3245 /* otherwise extended_dn gets stripped off */
3246 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3250 * ADS_EXTENDED_DN_HEX_STRING:
3251 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3253 * ADS_EXTENDED_DN_STRING (only with w2k3):
3254 * <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
3256 * Object with no SID, such as an Exchange Public Folder
3257 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3260 p
= strchr(dn
, ';');
3262 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3265 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3266 DEBUG(5,("No SID present in extended dn\n"));
3267 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3270 p
+= strlen(";<SID=");
3274 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3279 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3283 case ADS_EXTENDED_DN_STRING
:
3284 if (!string_to_sid(sid
, p
)) {
3285 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3288 case ADS_EXTENDED_DN_HEX_STRING
: {
3292 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3294 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3297 if (!sid_parse(buf
, buf_len
, sid
)) {
3298 DEBUG(10,("failed to parse sid\n"));
3299 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3304 DEBUG(10,("unknown extended dn format\n"));
3305 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3308 return ADS_ERROR_NT(NT_STATUS_OK
);
3311 /********************************************************************
3312 ********************************************************************/
3314 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3316 LDAPMessage
*res
= NULL
;
3321 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3322 if (!ADS_ERR_OK(status
)) {
3323 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3324 lp_netbios_name()));
3328 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3329 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3333 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3334 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3338 ads_msgfree(ads
, res
);
3343 /********************************************************************
3344 ********************************************************************/
3346 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3348 LDAPMessage
*res
= NULL
;
3353 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3354 if (!ADS_ERR_OK(status
)) {
3355 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3356 lp_netbios_name()));
3360 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3361 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3365 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3366 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3370 ads_msgfree(ads
, res
);
3375 /********************************************************************
3376 ********************************************************************/
3378 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3380 LDAPMessage
*res
= NULL
;
3385 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3386 if (!ADS_ERR_OK(status
)) {
3387 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3388 lp_netbios_name()));
3392 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3393 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3397 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3398 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3402 ads_msgfree(ads
, res
);
3409 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3412 * Join a machine to a realm
3413 * Creates the machine account and sets the machine password
3414 * @param ads connection to ads server
3415 * @param machine name of host to add
3416 * @param org_unit Organizational unit to place machine in
3417 * @return status of join
3419 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3420 uint32 account_type
, const char *org_unit
)
3423 LDAPMessage
*res
= NULL
;
3426 /* machine name must be lowercase */
3427 machine
= SMB_STRDUP(machine_name
);
3428 strlower_m(machine
);
3431 status = ads_find_machine_acct(ads, (void **)&res, machine);
3432 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3433 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3434 status = ads_leave_realm(ads, machine);
3435 if (!ADS_ERR_OK(status)) {
3436 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3437 machine, ads->config.realm));
3442 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3443 if (!ADS_ERR_OK(status
)) {
3444 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3449 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3450 if (!ADS_ERR_OK(status
)) {
3451 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3457 ads_msgfree(ads
, res
);
3464 * Delete a machine from the realm
3465 * @param ads connection to ads server
3466 * @param hostname Machine to remove
3467 * @return status of delete
3469 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3474 char *hostnameDN
, *host
;
3476 LDAPControl ldap_control
;
3477 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3479 pldap_control
[0] = &ldap_control
;
3480 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3481 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3483 /* hostname must be lowercase */
3484 host
= SMB_STRDUP(hostname
);
3485 if (!strlower_m(host
)) {
3487 return ADS_ERROR_SYSTEM(EINVAL
);
3490 status
= ads_find_machine_acct(ads
, &res
, host
);
3491 if (!ADS_ERR_OK(status
)) {
3492 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3497 msg
= ads_first_entry(ads
, res
);
3500 return ADS_ERROR_SYSTEM(ENOENT
);
3503 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3504 if (hostnameDN
== NULL
) {
3506 return ADS_ERROR_SYSTEM(ENOENT
);
3509 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3511 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3513 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3516 if (rc
!= LDAP_SUCCESS
) {
3517 const char *attrs
[] = { "cn", NULL
};
3518 LDAPMessage
*msg_sub
;
3520 /* we only search with scope ONE, we do not expect any further
3521 * objects to be created deeper */
3523 status
= ads_do_search_retry(ads
, hostnameDN
,
3524 LDAP_SCOPE_ONELEVEL
,
3525 "(objectclass=*)", attrs
, &res
);
3527 if (!ADS_ERR_OK(status
)) {
3529 TALLOC_FREE(hostnameDN
);
3533 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3534 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3538 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3540 TALLOC_FREE(hostnameDN
);
3541 return ADS_ERROR(LDAP_NO_MEMORY
);
3544 status
= ads_del_dn(ads
, dn
);
3545 if (!ADS_ERR_OK(status
)) {
3546 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3549 TALLOC_FREE(hostnameDN
);
3556 /* there should be no subordinate objects anymore */
3557 status
= ads_do_search_retry(ads
, hostnameDN
,
3558 LDAP_SCOPE_ONELEVEL
,
3559 "(objectclass=*)", attrs
, &res
);
3561 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3563 TALLOC_FREE(hostnameDN
);
3567 /* delete hostnameDN now */
3568 status
= ads_del_dn(ads
, hostnameDN
);
3569 if (!ADS_ERR_OK(status
)) {
3571 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3572 TALLOC_FREE(hostnameDN
);
3577 TALLOC_FREE(hostnameDN
);
3579 status
= ads_find_machine_acct(ads
, &res
, host
);
3580 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3581 DEBUG(3, ("Failed to remove host account.\n"));
3591 * pull all token-sids from an LDAP dn
3592 * @param ads connection to ads server
3593 * @param mem_ctx TALLOC_CTX for allocating sid array
3594 * @param dn of LDAP object
3595 * @param user_sid pointer to struct dom_sid (objectSid)
3596 * @param primary_group_sid pointer to struct dom_sid (self composed)
3597 * @param sids pointer to sid array to allocate
3598 * @param num_sids counter of SIDs pulled
3599 * @return status of token query
3601 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3602 TALLOC_CTX
*mem_ctx
,
3604 struct dom_sid
*user_sid
,
3605 struct dom_sid
*primary_group_sid
,
3606 struct dom_sid
**sids
,
3610 LDAPMessage
*res
= NULL
;
3612 size_t tmp_num_sids
;
3613 struct dom_sid
*tmp_sids
;
3614 struct dom_sid tmp_user_sid
;
3615 struct dom_sid tmp_primary_group_sid
;
3617 const char *attrs
[] = {
3624 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3625 if (!ADS_ERR_OK(status
)) {
3629 count
= ads_count_replies(ads
, res
);
3631 ads_msgfree(ads
, res
);
3632 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3635 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3636 ads_msgfree(ads
, res
);
3637 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3640 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3641 ads_msgfree(ads
, res
);
3642 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3646 /* hack to compose the primary group sid without knowing the
3649 struct dom_sid domsid
;
3651 sid_copy(&domsid
, &tmp_user_sid
);
3653 if (!sid_split_rid(&domsid
, NULL
)) {
3654 ads_msgfree(ads
, res
);
3655 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3658 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3659 ads_msgfree(ads
, res
);
3660 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3664 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3666 if (tmp_num_sids
== 0 || !tmp_sids
) {
3667 ads_msgfree(ads
, res
);
3668 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3672 *num_sids
= tmp_num_sids
;
3680 *user_sid
= tmp_user_sid
;
3683 if (primary_group_sid
) {
3684 *primary_group_sid
= tmp_primary_group_sid
;
3687 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3689 ads_msgfree(ads
, res
);
3690 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3694 * Find a sAMAccoutName in LDAP
3695 * @param ads connection to ads server
3696 * @param mem_ctx TALLOC_CTX for allocating sid array
3697 * @param samaccountname to search
3698 * @param uac_ret uint32 pointer userAccountControl attribute value
3699 * @param dn_ret pointer to dn
3700 * @return status of token query
3702 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3703 TALLOC_CTX
*mem_ctx
,
3704 const char *samaccountname
,
3706 const char **dn_ret
)
3709 const char *attrs
[] = { "userAccountControl", NULL
};
3711 LDAPMessage
*res
= NULL
;
3715 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3717 if (filter
== NULL
) {
3718 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3722 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3724 filter
, attrs
, &res
);
3726 if (!ADS_ERR_OK(status
)) {
3730 if (ads_count_replies(ads
, res
) != 1) {
3731 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3735 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3737 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3741 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3742 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3751 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3753 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3759 ads_msgfree(ads
, res
);
3765 * find our configuration path
3766 * @param ads connection to ads server
3767 * @param mem_ctx Pointer to talloc context
3768 * @param config_path Pointer to the config path
3769 * @return status of search
3771 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3772 TALLOC_CTX
*mem_ctx
,
3776 LDAPMessage
*res
= NULL
;
3777 const char *config_context
= NULL
;
3778 const char *attrs
[] = { "configurationNamingContext", NULL
};
3780 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3781 "(objectclass=*)", attrs
, &res
);
3782 if (!ADS_ERR_OK(status
)) {
3786 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3787 "configurationNamingContext");
3788 ads_msgfree(ads
, res
);
3789 if (!config_context
) {
3790 return ADS_ERROR(LDAP_NO_MEMORY
);
3794 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3795 if (!*config_path
) {
3796 return ADS_ERROR(LDAP_NO_MEMORY
);
3800 return ADS_ERROR(LDAP_SUCCESS
);
3804 * find the displayName of an extended right
3805 * @param ads connection to ads server
3806 * @param config_path The config path
3807 * @param mem_ctx Pointer to talloc context
3808 * @param GUID struct of the rightsGUID
3809 * @return status of search
3811 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3812 const char *config_path
,
3813 TALLOC_CTX
*mem_ctx
,
3814 const struct GUID
*rights_guid
)
3817 LDAPMessage
*res
= NULL
;
3819 const char *attrs
[] = { "displayName", NULL
};
3820 const char *result
= NULL
;
3823 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3827 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3828 GUID_string(mem_ctx
, rights_guid
));
3833 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3838 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3840 if (!ADS_ERR_OK(rc
)) {
3844 if (ads_count_replies(ads
, res
) != 1) {
3848 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3851 ads_msgfree(ads
, res
);
3856 * verify or build and verify an account ou
3857 * @param mem_ctx Pointer to talloc context
3858 * @param ads connection to ads server
3860 * @return status of search
3863 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3865 const char **account_ou
)
3871 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
3873 ldap_value_free(exploded_dn
);
3877 ou_string
= ads_ou_string(ads
, *account_ou
);
3879 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3882 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3883 ads
->config
.bind_path
);
3884 SAFE_FREE(ou_string
);
3887 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3890 exploded_dn
= ldap_explode_dn(name
, 0);
3892 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3894 ldap_value_free(exploded_dn
);