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
;
1146 LDAPMessage
*msg
, *next
;
1148 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1149 attrs
, args
, &res2
, &count
, &cookie
);
1150 if (!ADS_ERR_OK(status
)) {
1151 /* Ensure we free all collected results */
1152 ads_msgfree(ads
, *res
);
1157 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1158 that this works on all ldap libs, but I have only tested with openldap */
1159 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1160 next
= ads_next_message(ads
, msg
);
1161 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1163 /* note that we do not free res2, as the memory is now
1164 part of the main returned list */
1167 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1168 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1174 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1175 int scope
, const char *expr
,
1176 const char **attrs
, LDAPMessage
**res
)
1178 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1181 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1182 int scope
, const char *expr
,
1183 const char **attrs
, uint32 sd_flags
,
1188 args
.control
= ADS_SD_FLAGS_OID
;
1189 args
.val
= sd_flags
;
1190 args
.critical
= True
;
1192 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1197 * Run a function on all results for a search. Uses ads_do_paged_search() and
1198 * runs the function as each page is returned, using ads_process_results()
1199 * @param ads connection to ads server
1200 * @param bind_path Base dn for the search
1201 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1202 * @param expr Search expression - specified in local charset
1203 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1204 * @param fn Function which takes attr name, values list, and data_area
1205 * @param data_area Pointer which is passed to function on each call
1206 * @return status of search
1208 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1209 int scope
, const char *expr
, const char **attrs
,
1210 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1213 struct berval
*cookie
= NULL
;
1218 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1221 if (!ADS_ERR_OK(status
)) return status
;
1223 ads_process_results(ads
, res
, fn
, data_area
);
1224 ads_msgfree(ads
, res
);
1227 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1228 &res
, &count
, &cookie
);
1230 if (!ADS_ERR_OK(status
)) break;
1232 ads_process_results(ads
, res
, fn
, data_area
);
1233 ads_msgfree(ads
, res
);
1240 * Do a search with a timeout.
1241 * @param ads connection to ads server
1242 * @param bind_path Base dn for the search
1243 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1244 * @param expr Search expression
1245 * @param attrs Attributes to retrieve
1246 * @param res ** which will contain results - free res* with ads_msgfree()
1247 * @return status of search
1249 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1251 const char **attrs
, LDAPMessage
**res
)
1254 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1255 size_t converted_size
;
1259 if (!(ctx
= talloc_init("ads_do_search"))) {
1260 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1261 return ADS_ERROR(LDAP_NO_MEMORY
);
1264 /* 0 means the conversion worked but the result was empty
1265 so we only fail if it's negative. In any case, it always
1266 at least nulls out the dest */
1267 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1268 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1270 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1271 rc
= LDAP_NO_MEMORY
;
1275 if (!attrs
|| !(*attrs
))
1276 search_attrs
= NULL
;
1278 /* This would be the utf8-encoded version...*/
1279 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1280 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1282 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1283 rc
= LDAP_NO_MEMORY
;
1288 /* see the note in ads_do_paged_search - we *must* disable referrals */
1289 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1291 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1292 search_attrs
, 0, NULL
, NULL
,
1294 (LDAPMessage
**)res
);
1296 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1297 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1302 talloc_destroy(ctx
);
1303 /* if/when we decide to utf8-encode attrs, take out this next line */
1304 TALLOC_FREE(search_attrs
);
1305 return ADS_ERROR(rc
);
1308 * Do a general ADS search
1309 * @param ads connection to ads server
1310 * @param res ** which will contain results - free res* with ads_msgfree()
1311 * @param expr Search expression
1312 * @param attrs Attributes to retrieve
1313 * @return status of search
1315 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1316 const char *expr
, const char **attrs
)
1318 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1323 * Do a search on a specific DistinguishedName
1324 * @param ads connection to ads server
1325 * @param res ** which will contain results - free res* with ads_msgfree()
1326 * @param dn DistinguishName to search
1327 * @param attrs Attributes to retrieve
1328 * @return status of search
1330 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1331 const char *dn
, const char **attrs
)
1333 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1338 * Free up memory from a ads_search
1339 * @param ads connection to ads server
1340 * @param msg Search results to free
1342 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1349 * Get a dn from search results
1350 * @param ads connection to ads server
1351 * @param msg Search result
1354 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1356 char *utf8_dn
, *unix_dn
;
1357 size_t converted_size
;
1359 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1362 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1366 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1367 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1371 ldap_memfree(utf8_dn
);
1376 * Get the parent from a dn
1377 * @param dn the dn to return the parent from
1378 * @return parent dn string
1380 char *ads_parent_dn(const char *dn
)
1388 p
= strchr(dn
, ',');
1398 * Find a machine account given a hostname
1399 * @param ads connection to ads server
1400 * @param res ** which will contain results - free res* with ads_msgfree()
1401 * @param host Hostname to search for
1402 * @return status of search
1404 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1405 const char *machine
)
1409 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1413 /* the easiest way to find a machine account anywhere in the tree
1414 is to look for hostname$ */
1415 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1416 DEBUG(1, ("asprintf failed!\n"));
1417 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1420 status
= ads_search(ads
, res
, expr
, attrs
);
1426 * Initialize a list of mods to be used in a modify request
1427 * @param ctx An initialized TALLOC_CTX
1428 * @return allocated ADS_MODLIST
1430 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1432 #define ADS_MODLIST_ALLOC_SIZE 10
1435 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1436 /* -1 is safety to make sure we don't go over the end.
1437 need to reset it to NULL before doing ldap modify */
1438 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1440 return (ADS_MODLIST
)mods
;
1445 add an attribute to the list, with values list already constructed
1447 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1448 int mod_op
, const char *name
,
1449 const void *_invals
)
1451 const void **invals
= (const void **)_invals
;
1453 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1454 struct berval
**ber_values
= NULL
;
1455 char **char_values
= NULL
;
1458 mod_op
= LDAP_MOD_DELETE
;
1460 if (mod_op
& LDAP_MOD_BVALUES
)
1461 ber_values
= ads_dup_values(ctx
,
1462 (const struct berval
**)invals
);
1464 char_values
= ads_push_strvals(ctx
,
1465 (const char **) invals
);
1468 /* find the first empty slot */
1469 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1471 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1472 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1473 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1474 return ADS_ERROR(LDAP_NO_MEMORY
);
1475 memset(&modlist
[curmod
], 0,
1476 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1477 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1478 *mods
= (ADS_MODLIST
)modlist
;
1481 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1482 return ADS_ERROR(LDAP_NO_MEMORY
);
1483 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1484 if (mod_op
& LDAP_MOD_BVALUES
) {
1485 modlist
[curmod
]->mod_bvalues
= ber_values
;
1486 } else if (mod_op
& LDAP_MOD_DELETE
) {
1487 modlist
[curmod
]->mod_values
= NULL
;
1489 modlist
[curmod
]->mod_values
= char_values
;
1492 modlist
[curmod
]->mod_op
= mod_op
;
1493 return ADS_ERROR(LDAP_SUCCESS
);
1497 * Add a single string value to a mod list
1498 * @param ctx An initialized TALLOC_CTX
1499 * @param mods An initialized ADS_MODLIST
1500 * @param name The attribute name to add
1501 * @param val The value to add - NULL means DELETE
1502 * @return ADS STATUS indicating success of add
1504 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1505 const char *name
, const char *val
)
1507 const char *values
[2];
1513 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1514 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1518 * Add an array of string values to a mod list
1519 * @param ctx An initialized TALLOC_CTX
1520 * @param mods An initialized ADS_MODLIST
1521 * @param name The attribute name to add
1522 * @param vals The array of string values to add - NULL means DELETE
1523 * @return ADS STATUS indicating success of add
1525 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1526 const char *name
, const char **vals
)
1529 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1530 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1531 name
, (const void **) vals
);
1536 * Add a single ber-encoded value to a mod list
1537 * @param ctx An initialized TALLOC_CTX
1538 * @param mods An initialized ADS_MODLIST
1539 * @param name The attribute name to add
1540 * @param val The value to add - NULL means DELETE
1541 * @return ADS STATUS indicating success of add
1543 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1544 const char *name
, const struct berval
*val
)
1546 const struct berval
*values
[2];
1551 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1552 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1553 name
, (const void **) values
);
1558 * Perform an ldap modify
1559 * @param ads connection to ads server
1560 * @param mod_dn DistinguishedName to modify
1561 * @param mods list of modifications to perform
1562 * @return status of modify
1564 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1567 char *utf8_dn
= NULL
;
1568 size_t converted_size
;
1570 this control is needed to modify that contains a currently
1571 non-existent attribute (but allowable for the object) to run
1573 LDAPControl PermitModify
= {
1574 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1577 LDAPControl
*controls
[2];
1579 controls
[0] = &PermitModify
;
1582 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1583 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1586 /* find the end of the list, marked by NULL or -1 */
1587 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1588 /* make sure the end of the list is NULL */
1590 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1591 (LDAPMod
**) mods
, controls
, NULL
);
1592 TALLOC_FREE(utf8_dn
);
1593 return ADS_ERROR(ret
);
1597 * Perform an ldap add
1598 * @param ads connection to ads server
1599 * @param new_dn DistinguishedName to add
1600 * @param mods list of attributes and values for DN
1601 * @return status of add
1603 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1606 char *utf8_dn
= NULL
;
1607 size_t converted_size
;
1609 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1610 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1611 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1614 /* find the end of the list, marked by NULL or -1 */
1615 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1616 /* make sure the end of the list is NULL */
1619 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1620 TALLOC_FREE(utf8_dn
);
1621 return ADS_ERROR(ret
);
1625 * Delete a DistinguishedName
1626 * @param ads connection to ads server
1627 * @param new_dn DistinguishedName to delete
1628 * @return status of delete
1630 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1633 char *utf8_dn
= NULL
;
1634 size_t converted_size
;
1635 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1636 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1637 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1640 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1641 TALLOC_FREE(utf8_dn
);
1642 return ADS_ERROR(ret
);
1646 * Build an org unit string
1647 * if org unit is Computers or blank then assume a container, otherwise
1648 * assume a / separated list of organisational units.
1649 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1650 * @param ads connection to ads server
1651 * @param org_unit Organizational unit
1652 * @return org unit string - caller must free
1654 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1658 if (!org_unit
|| !*org_unit
) {
1660 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1662 /* samba4 might not yet respond to a wellknownobject-query */
1663 return ret
? ret
: SMB_STRDUP("cn=Computers");
1666 if (strequal(org_unit
, "Computers")) {
1667 return SMB_STRDUP("cn=Computers");
1670 /* jmcd: removed "\\" from the separation chars, because it is
1671 needed as an escape for chars like '#' which are valid in an
1673 return ads_build_path(org_unit
, "/", "ou=", 1);
1677 * Get a org unit string for a well-known GUID
1678 * @param ads connection to ads server
1679 * @param wknguid Well known GUID
1680 * @return org unit string - caller must free
1682 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1685 LDAPMessage
*res
= NULL
;
1686 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1687 **bind_dn_exp
= NULL
;
1688 const char *attrs
[] = {"distinguishedName", NULL
};
1689 int new_ln
, wkn_ln
, bind_ln
, i
;
1691 if (wknguid
== NULL
) {
1695 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1696 DEBUG(1, ("asprintf failed!\n"));
1700 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1701 if (!ADS_ERR_OK(status
)) {
1702 DEBUG(1,("Failed while searching for: %s\n", base
));
1706 if (ads_count_replies(ads
, res
) != 1) {
1710 /* substitute the bind-path from the well-known-guid-search result */
1711 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1716 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1721 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1726 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1728 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1731 new_ln
= wkn_ln
- bind_ln
;
1733 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1738 for (i
=1; i
< new_ln
; i
++) {
1741 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1747 ret
= SMB_STRDUP(s
);
1756 ads_msgfree(ads
, res
);
1757 TALLOC_FREE(wkn_dn
);
1759 ldap_value_free(wkn_dn_exp
);
1762 ldap_value_free(bind_dn_exp
);
1769 * Adds (appends) an item to an attribute array, rather then
1770 * replacing the whole list
1771 * @param ctx An initialized TALLOC_CTX
1772 * @param mods An initialized ADS_MODLIST
1773 * @param name name of the ldap attribute to append to
1774 * @param vals an array of values to add
1775 * @return status of addition
1778 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1779 const char *name
, const char **vals
)
1781 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1782 (const void *) vals
);
1786 * Determines the an account's current KVNO via an LDAP lookup
1787 * @param ads An initialized ADS_STRUCT
1788 * @param account_name the NT samaccountname.
1789 * @return the kvno for the account, or -1 in case of a failure.
1792 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1794 LDAPMessage
*res
= NULL
;
1795 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1797 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1798 char *dn_string
= NULL
;
1799 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1801 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1802 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1805 ret
= ads_search(ads
, &res
, filter
, attrs
);
1807 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1808 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1809 ads_msgfree(ads
, res
);
1813 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1815 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1816 ads_msgfree(ads
, res
);
1819 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1820 TALLOC_FREE(dn_string
);
1822 /* ---------------------------------------------------------
1823 * 0 is returned as a default KVNO from this point on...
1824 * This is done because Windows 2000 does not support key
1825 * version numbers. Chances are that a failure in the next
1826 * step is simply due to Windows 2000 being used for a
1827 * domain controller. */
1830 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1831 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1832 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1833 ads_msgfree(ads
, res
);
1838 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1839 ads_msgfree(ads
, res
);
1844 * Determines the computer account's current KVNO via an LDAP lookup
1845 * @param ads An initialized ADS_STRUCT
1846 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1847 * @return the kvno for the computer account, or -1 in case of a failure.
1850 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1852 char *computer_account
= NULL
;
1855 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1859 kvno
= ads_get_kvno(ads
, computer_account
);
1860 free(computer_account
);
1866 * This clears out all registered spn's for a given hostname
1867 * @param ads An initilaized ADS_STRUCT
1868 * @param machine_name the NetBIOS name of the computer.
1869 * @return 0 upon success, non-zero otherwise.
1872 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1875 LDAPMessage
*res
= NULL
;
1877 const char *servicePrincipalName
[1] = {NULL
};
1878 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1879 char *dn_string
= NULL
;
1881 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1882 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1883 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1884 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1885 ads_msgfree(ads
, res
);
1886 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1889 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1890 ctx
= talloc_init("ads_clear_service_principal_names");
1892 ads_msgfree(ads
, res
);
1893 return ADS_ERROR(LDAP_NO_MEMORY
);
1896 if (!(mods
= ads_init_mods(ctx
))) {
1897 talloc_destroy(ctx
);
1898 ads_msgfree(ads
, res
);
1899 return ADS_ERROR(LDAP_NO_MEMORY
);
1901 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1902 if (!ADS_ERR_OK(ret
)) {
1903 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1904 ads_msgfree(ads
, res
);
1905 talloc_destroy(ctx
);
1908 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1910 talloc_destroy(ctx
);
1911 ads_msgfree(ads
, res
);
1912 return ADS_ERROR(LDAP_NO_MEMORY
);
1914 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1915 TALLOC_FREE(dn_string
);
1916 if (!ADS_ERR_OK(ret
)) {
1917 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1919 ads_msgfree(ads
, res
);
1920 talloc_destroy(ctx
);
1924 ads_msgfree(ads
, res
);
1925 talloc_destroy(ctx
);
1930 * @brief Search for an element in a string array.
1932 * @param[in] el_array The string array to search.
1934 * @param[in] num_el The number of elements in the string array.
1936 * @param[in] el The string to search.
1938 * @return True if found, false if not.
1940 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
1944 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
1948 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
1951 cmp
= strcasecmp_m(el_array
[i
], el
);
1961 * @brief This gets the service principal names of an existing computer account.
1963 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1965 * @param[in] ads The ADS context to use.
1967 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1968 * identify the computer account.
1970 * @param[in] spn_array A pointer to store the array for SPNs.
1972 * @param[in] num_spns The number of principals stored in the array.
1974 * @return 0 on success, or a ADS error if a failure occured.
1976 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
1978 const char *machine_name
,
1983 LDAPMessage
*res
= NULL
;
1987 status
= ads_find_machine_acct(ads
,
1990 if (!ADS_ERR_OK(status
)) {
1991 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1996 count
= ads_count_replies(ads
, res
);
1998 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2002 dn
= ads_get_dn(ads
, mem_ctx
, res
);
2004 status
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2008 *spn_array
= ads_pull_strings(ads
,
2011 "servicePrincipalName",
2015 ads_msgfree(ads
, res
);
2021 * This adds a service principal name to an existing computer account
2022 * (found by hostname) in AD.
2023 * @param ads An initialized ADS_STRUCT
2024 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2025 * @param my_fqdn The fully qualified DNS name of the machine
2026 * @param spn A string of the service principal to add, i.e. 'host'
2027 * @return 0 upon sucess, or non-zero if a failure occurs
2030 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
2031 const char *my_fqdn
, const char *spn
)
2035 LDAPMessage
*res
= NULL
;
2038 char *dn_string
= NULL
;
2039 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
2041 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2042 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
2043 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2045 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2046 spn
, machine_name
, ads
->config
.realm
));
2047 ads_msgfree(ads
, res
);
2048 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2051 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2052 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2053 ads_msgfree(ads
, res
);
2054 return ADS_ERROR(LDAP_NO_MEMORY
);
2057 /* add short name spn */
2059 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
2060 talloc_destroy(ctx
);
2061 ads_msgfree(ads
, res
);
2062 return ADS_ERROR(LDAP_NO_MEMORY
);
2064 if (!strlower_m(&psp1
[strlen(spn
) + 1])) {
2065 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2068 servicePrincipalName
[0] = psp1
;
2070 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2071 psp1
, machine_name
));
2074 /* add fully qualified spn */
2076 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
2077 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2080 if (!strlower_m(&psp2
[strlen(spn
) + 1])) {
2081 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2084 servicePrincipalName
[1] = psp2
;
2086 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2087 psp2
, machine_name
));
2089 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2090 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2094 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2095 if (!ADS_ERR_OK(ret
)) {
2096 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2100 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2101 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2105 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2106 if (!ADS_ERR_OK(ret
)) {
2107 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2113 ads_msgfree(ads
, res
);
2118 * adds a machine account to the ADS server
2119 * @param ads An intialized ADS_STRUCT
2120 * @param machine_name - the NetBIOS machine name of this account.
2121 * @param account_type A number indicating the type of account to create
2122 * @param org_unit The LDAP path in which to place this account
2123 * @return 0 upon success, or non-zero otherwise
2126 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2127 const char *org_unit
)
2130 char *samAccountName
, *controlstr
;
2133 char *machine_escaped
= NULL
;
2135 const char *objectClass
[] = {"top", "person", "organizationalPerson",
2136 "user", "computer", NULL
};
2137 LDAPMessage
*res
= NULL
;
2138 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
2139 UF_DONT_EXPIRE_PASSWD
|\
2140 UF_ACCOUNTDISABLE
);
2142 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2143 return ADS_ERROR(LDAP_NO_MEMORY
);
2145 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2147 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2148 if (!machine_escaped
) {
2152 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2153 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2155 if ( !new_dn
|| !samAccountName
) {
2159 #ifndef ENCTYPE_ARCFOUR_HMAC
2160 acct_control
|= UF_USE_DES_KEY_ONLY
;
2163 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2167 if (!(mods
= ads_init_mods(ctx
))) {
2171 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2172 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2173 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2174 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2176 ret
= ads_gen_add(ads
, new_dn
, mods
);
2179 SAFE_FREE(machine_escaped
);
2180 ads_msgfree(ads
, res
);
2181 talloc_destroy(ctx
);
2187 * move a machine account to another OU on the ADS server
2188 * @param ads - An intialized ADS_STRUCT
2189 * @param machine_name - the NetBIOS machine name of this account.
2190 * @param org_unit - The LDAP path in which to place this account
2191 * @param moved - whether we moved the machine account (optional)
2192 * @return 0 upon success, or non-zero otherwise
2195 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2196 const char *org_unit
, bool *moved
)
2200 LDAPMessage
*res
= NULL
;
2201 char *filter
= NULL
;
2202 char *computer_dn
= NULL
;
2204 char *computer_rdn
= NULL
;
2205 bool need_move
= False
;
2207 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2208 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2212 /* Find pre-existing machine */
2213 rc
= ads_search(ads
, &res
, filter
, NULL
);
2214 if (!ADS_ERR_OK(rc
)) {
2218 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2220 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2224 parent_dn
= ads_parent_dn(computer_dn
);
2225 if (strequal(parent_dn
, org_unit
)) {
2231 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2232 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2236 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2237 org_unit
, 1, NULL
, NULL
);
2238 rc
= ADS_ERROR(ldap_status
);
2241 ads_msgfree(ads
, res
);
2243 TALLOC_FREE(computer_dn
);
2244 SAFE_FREE(computer_rdn
);
2246 if (!ADS_ERR_OK(rc
)) {
2258 dump a binary result from ldap
2260 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2263 for (i
=0; values
[i
]; i
++) {
2264 printf("%s: ", field
);
2265 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2266 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2272 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2275 for (i
=0; values
[i
]; i
++) {
2277 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2280 status
= GUID_from_ndr_blob(&in
, &guid
);
2281 if (NT_STATUS_IS_OK(status
)) {
2282 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2284 printf("%s: INVALID GUID\n", field
);
2290 dump a sid result from ldap
2292 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2295 for (i
=0; values
[i
]; i
++) {
2298 if (!sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
)) {
2301 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2306 dump ntSecurityDescriptor
2308 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2310 TALLOC_CTX
*frame
= talloc_stackframe();
2311 struct security_descriptor
*psd
;
2314 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2315 values
[0]->bv_len
, &psd
);
2316 if (!NT_STATUS_IS_OK(status
)) {
2317 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2318 nt_errstr(status
)));
2324 ads_disp_sd(ads
, talloc_tos(), psd
);
2331 dump a string result from ldap
2333 static void dump_string(const char *field
, char **values
)
2336 for (i
=0; values
[i
]; i
++) {
2337 printf("%s: %s\n", field
, values
[i
]);
2342 dump a field from LDAP on stdout
2346 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2351 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2353 {"objectGUID", False
, dump_guid
},
2354 {"netbootGUID", False
, dump_guid
},
2355 {"nTSecurityDescriptor", False
, dump_sd
},
2356 {"dnsRecord", False
, dump_binary
},
2357 {"objectSid", False
, dump_sid
},
2358 {"tokenGroups", False
, dump_sid
},
2359 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2360 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2361 {"mS-DS-CreatorSID", False
, dump_sid
},
2362 {"msExchMailboxGuid", False
, dump_guid
},
2367 if (!field
) { /* must be end of an entry */
2372 for (i
=0; handlers
[i
].name
; i
++) {
2373 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2374 if (!values
) /* first time, indicate string or not */
2375 return handlers
[i
].string
;
2376 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2380 if (!handlers
[i
].name
) {
2381 if (!values
) /* first time, indicate string conversion */
2383 dump_string(field
, (char **)values
);
2389 * Dump a result from LDAP on stdout
2390 * used for debugging
2391 * @param ads connection to ads server
2392 * @param res Results to dump
2395 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2397 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2401 * Walk through results, calling a function for each entry found.
2402 * The function receives a field name, a berval * array of values,
2403 * and a data area passed through from the start. The function is
2404 * called once with null for field and values at the end of each
2406 * @param ads connection to ads server
2407 * @param res Results to process
2408 * @param fn Function for processing each result
2409 * @param data_area user-defined area to pass to function
2411 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2412 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2417 size_t converted_size
;
2419 if (!(ctx
= talloc_init("ads_process_results")))
2422 for (msg
= ads_first_entry(ads
, res
); msg
;
2423 msg
= ads_next_entry(ads
, msg
)) {
2427 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2428 (LDAPMessage
*)msg
,&b
);
2430 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2431 (LDAPMessage
*)msg
,b
)) {
2432 struct berval
**ber_vals
;
2433 char **str_vals
, **utf8_vals
;
2437 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2440 DEBUG(0,("ads_process_results: "
2441 "pull_utf8_talloc failed: %s",
2445 string
= fn(ads
, field
, NULL
, data_area
);
2448 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2449 (LDAPMessage
*)msg
, field
);
2450 str_vals
= ads_pull_strvals(ctx
,
2451 (const char **) utf8_vals
);
2452 fn(ads
, field
, (void **) str_vals
, data_area
);
2453 ldap_value_free(utf8_vals
);
2455 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2456 (LDAPMessage
*)msg
, field
);
2457 fn(ads
, field
, (void **) ber_vals
, data_area
);
2459 ldap_value_free_len(ber_vals
);
2461 ldap_memfree(utf8_field
);
2464 talloc_free_children(ctx
);
2465 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2468 talloc_destroy(ctx
);
2472 * count how many replies are in a LDAPMessage
2473 * @param ads connection to ads server
2474 * @param res Results to count
2475 * @return number of replies
2477 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2479 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2483 * pull the first entry from a ADS result
2484 * @param ads connection to ads server
2485 * @param res Results of search
2486 * @return first entry from result
2488 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2490 return ldap_first_entry(ads
->ldap
.ld
, res
);
2494 * pull the next entry from a ADS result
2495 * @param ads connection to ads server
2496 * @param res Results of search
2497 * @return next entry from result
2499 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2501 return ldap_next_entry(ads
->ldap
.ld
, res
);
2505 * pull the first message from a ADS result
2506 * @param ads connection to ads server
2507 * @param res Results of search
2508 * @return first message from result
2510 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2512 return ldap_first_message(ads
->ldap
.ld
, res
);
2516 * pull the next message from a ADS result
2517 * @param ads connection to ads server
2518 * @param res Results of search
2519 * @return next message from result
2521 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2523 return ldap_next_message(ads
->ldap
.ld
, res
);
2527 * pull a single string from a ADS result
2528 * @param ads connection to ads server
2529 * @param mem_ctx TALLOC_CTX to use for allocating result string
2530 * @param msg Results of search
2531 * @param field Attribute to retrieve
2532 * @return Result string in talloc context
2534 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2540 size_t converted_size
;
2542 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2546 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2551 ldap_value_free(values
);
2556 * pull an array of strings from a ADS result
2557 * @param ads connection to ads server
2558 * @param mem_ctx TALLOC_CTX to use for allocating result string
2559 * @param msg Results of search
2560 * @param field Attribute to retrieve
2561 * @return Result strings in talloc context
2563 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2564 LDAPMessage
*msg
, const char *field
,
2570 size_t converted_size
;
2572 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2576 *num_values
= ldap_count_values(values
);
2578 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2580 ldap_value_free(values
);
2584 for (i
=0;i
<*num_values
;i
++) {
2585 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2588 ldap_value_free(values
);
2594 ldap_value_free(values
);
2599 * pull an array of strings from a ADS result
2600 * (handle large multivalue attributes with range retrieval)
2601 * @param ads connection to ads server
2602 * @param mem_ctx TALLOC_CTX to use for allocating result string
2603 * @param msg Results of search
2604 * @param field Attribute to retrieve
2605 * @param current_strings strings returned by a previous call to this function
2606 * @param next_attribute The next query should ask for this attribute
2607 * @param num_values How many values did we get this time?
2608 * @param more_values Are there more values to get?
2609 * @return Result strings in talloc context
2611 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2612 TALLOC_CTX
*mem_ctx
,
2613 LDAPMessage
*msg
, const char *field
,
2614 char **current_strings
,
2615 const char **next_attribute
,
2616 size_t *num_strings
,
2620 char *expected_range_attrib
, *range_attr
;
2621 BerElement
*ptr
= NULL
;
2624 size_t num_new_strings
;
2625 unsigned long int range_start
;
2626 unsigned long int range_end
;
2628 /* we might have been given the whole lot anyway */
2629 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2630 *more_strings
= False
;
2634 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2636 /* look for Range result */
2637 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2639 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2640 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2641 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2649 /* nothing here - this field is just empty */
2650 *more_strings
= False
;
2654 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2655 &range_start
, &range_end
) == 2) {
2656 *more_strings
= True
;
2658 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2659 &range_start
) == 1) {
2660 *more_strings
= False
;
2662 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2664 ldap_memfree(range_attr
);
2665 *more_strings
= False
;
2670 if ((*num_strings
) != range_start
) {
2671 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2672 " - aborting range retreival\n",
2673 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2674 ldap_memfree(range_attr
);
2675 *more_strings
= False
;
2679 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2681 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2682 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2683 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2684 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2685 (unsigned long int)num_new_strings
));
2686 ldap_memfree(range_attr
);
2687 *more_strings
= False
;
2691 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2692 *num_strings
+ num_new_strings
);
2694 if (strings
== NULL
) {
2695 ldap_memfree(range_attr
);
2696 *more_strings
= False
;
2700 if (new_strings
&& num_new_strings
) {
2701 memcpy(&strings
[*num_strings
], new_strings
,
2702 sizeof(*new_strings
) * num_new_strings
);
2705 (*num_strings
) += num_new_strings
;
2707 if (*more_strings
) {
2708 *next_attribute
= talloc_asprintf(mem_ctx
,
2713 if (!*next_attribute
) {
2714 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2715 ldap_memfree(range_attr
);
2716 *more_strings
= False
;
2721 ldap_memfree(range_attr
);
2727 * pull a single uint32 from a ADS result
2728 * @param ads connection to ads server
2729 * @param msg Results of search
2730 * @param field Attribute to retrieve
2731 * @param v Pointer to int to store result
2732 * @return boolean inidicating success
2734 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2739 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2743 ldap_value_free(values
);
2747 *v
= atoi(values
[0]);
2748 ldap_value_free(values
);
2753 * pull a single objectGUID from an ADS result
2754 * @param ads connection to ADS server
2755 * @param msg results of search
2756 * @param guid 37-byte area to receive text guid
2757 * @return boolean indicating success
2759 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2764 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2769 status
= GUID_from_ndr_blob(&blob
, guid
);
2770 talloc_free(blob
.data
);
2771 return NT_STATUS_IS_OK(status
);
2776 * pull a single struct dom_sid from a ADS result
2777 * @param ads connection to ads server
2778 * @param msg Results of search
2779 * @param field Attribute to retrieve
2780 * @param sid Pointer to sid to store result
2781 * @return boolean inidicating success
2783 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2784 struct dom_sid
*sid
)
2786 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2790 * pull an array of struct dom_sids from a ADS result
2791 * @param ads connection to ads server
2792 * @param mem_ctx TALLOC_CTX for allocating sid array
2793 * @param msg Results of search
2794 * @param field Attribute to retrieve
2795 * @param sids pointer to sid array to allocate
2796 * @return the count of SIDs pulled
2798 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2799 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2801 struct berval
**values
;
2805 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2810 for (i
=0; values
[i
]; i
++)
2814 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2816 ldap_value_free_len(values
);
2824 for (i
=0; values
[i
]; i
++) {
2825 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2827 DEBUG(10, ("pulling SID: %s\n",
2828 sid_string_dbg(&(*sids
)[count
])));
2833 ldap_value_free_len(values
);
2838 * pull a struct security_descriptor from a ADS result
2839 * @param ads connection to ads server
2840 * @param mem_ctx TALLOC_CTX for allocating sid array
2841 * @param msg Results of search
2842 * @param field Attribute to retrieve
2843 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2844 * @return boolean inidicating success
2846 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2847 LDAPMessage
*msg
, const char *field
,
2848 struct security_descriptor
**sd
)
2850 struct berval
**values
;
2853 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2855 if (!values
) return false;
2859 status
= unmarshall_sec_desc(mem_ctx
,
2860 (uint8
*)values
[0]->bv_val
,
2861 values
[0]->bv_len
, sd
);
2862 if (!NT_STATUS_IS_OK(status
)) {
2863 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2864 nt_errstr(status
)));
2869 ldap_value_free_len(values
);
2874 * in order to support usernames longer than 21 characters we need to
2875 * use both the sAMAccountName and the userPrincipalName attributes
2876 * It seems that not all users have the userPrincipalName attribute set
2878 * @param ads connection to ads server
2879 * @param mem_ctx TALLOC_CTX for allocating sid array
2880 * @param msg Results of search
2881 * @return the username
2883 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2889 /* lookup_name() only works on the sAMAccountName to
2890 returning the username portion of userPrincipalName
2891 breaks winbindd_getpwnam() */
2893 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2894 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2899 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2904 * find the update serial number - this is the core of the ldap cache
2905 * @param ads connection to ads server
2906 * @param ads connection to ADS server
2907 * @param usn Pointer to retrieved update serial number
2908 * @return status of search
2910 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2912 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2916 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2917 if (!ADS_ERR_OK(status
))
2920 if (ads_count_replies(ads
, res
) != 1) {
2921 ads_msgfree(ads
, res
);
2922 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2925 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2926 ads_msgfree(ads
, res
);
2927 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2930 ads_msgfree(ads
, res
);
2934 /* parse a ADS timestring - typical string is
2935 '20020917091222.0Z0' which means 09:12.22 17th September
2937 static time_t ads_parse_time(const char *str
)
2943 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2944 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2945 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2954 /********************************************************************
2955 ********************************************************************/
2957 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2959 const char *attrs
[] = {"currentTime", NULL
};
2964 ADS_STRUCT
*ads_s
= ads
;
2966 if (!(ctx
= talloc_init("ads_current_time"))) {
2967 return ADS_ERROR(LDAP_NO_MEMORY
);
2970 /* establish a new ldap tcp session if necessary */
2972 if ( !ads
->ldap
.ld
) {
2973 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2974 ads
->server
.ldap_server
)) == NULL
)
2978 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2979 status
= ads_connect( ads_s
);
2980 if ( !ADS_ERR_OK(status
))
2984 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2985 if (!ADS_ERR_OK(status
)) {
2989 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2991 ads_msgfree(ads_s
, res
);
2992 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2996 /* but save the time and offset in the original ADS_STRUCT */
2998 ads
->config
.current_time
= ads_parse_time(timestr
);
3000 if (ads
->config
.current_time
!= 0) {
3001 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
3002 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
3005 ads_msgfree(ads
, res
);
3007 status
= ADS_SUCCESS
;
3010 /* free any temporary ads connections */
3011 if ( ads_s
!= ads
) {
3012 ads_destroy( &ads_s
);
3014 talloc_destroy(ctx
);
3019 /********************************************************************
3020 ********************************************************************/
3022 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
3024 const char *attrs
[] = {"domainFunctionality", NULL
};
3027 ADS_STRUCT
*ads_s
= ads
;
3029 *val
= DS_DOMAIN_FUNCTION_2000
;
3031 /* establish a new ldap tcp session if necessary */
3033 if ( !ads
->ldap
.ld
) {
3034 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
3035 ads
->server
.ldap_server
)) == NULL
)
3037 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3040 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3041 status
= ads_connect( ads_s
);
3042 if ( !ADS_ERR_OK(status
))
3046 /* If the attribute does not exist assume it is a Windows 2000
3047 functional domain */
3049 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3050 if (!ADS_ERR_OK(status
)) {
3051 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3052 status
= ADS_SUCCESS
;
3057 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3058 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3060 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3063 ads_msgfree(ads
, res
);
3066 /* free any temporary ads connections */
3067 if ( ads_s
!= ads
) {
3068 ads_destroy( &ads_s
);
3075 * find the domain sid for our domain
3076 * @param ads connection to ads server
3077 * @param sid Pointer to domain sid
3078 * @return status of search
3080 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3082 const char *attrs
[] = {"objectSid", NULL
};
3086 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3088 if (!ADS_ERR_OK(rc
)) return rc
;
3089 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3090 ads_msgfree(ads
, res
);
3091 return ADS_ERROR_SYSTEM(ENOENT
);
3093 ads_msgfree(ads
, res
);
3099 * find our site name
3100 * @param ads connection to ads server
3101 * @param mem_ctx Pointer to talloc context
3102 * @param site_name Pointer to the sitename
3103 * @return status of search
3105 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3109 const char *dn
, *service_name
;
3110 const char *attrs
[] = { "dsServiceName", NULL
};
3112 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3113 if (!ADS_ERR_OK(status
)) {
3117 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3118 if (service_name
== NULL
) {
3119 ads_msgfree(ads
, res
);
3120 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3123 ads_msgfree(ads
, res
);
3125 /* go up three levels */
3126 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3128 return ADS_ERROR(LDAP_NO_MEMORY
);
3131 *site_name
= talloc_strdup(mem_ctx
, dn
);
3132 if (*site_name
== NULL
) {
3133 return ADS_ERROR(LDAP_NO_MEMORY
);
3138 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3143 * find the site dn where a machine resides
3144 * @param ads connection to ads server
3145 * @param mem_ctx Pointer to talloc context
3146 * @param computer_name name of the machine
3147 * @param site_name Pointer to the sitename
3148 * @return status of search
3150 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3154 const char *parent
, *filter
;
3155 char *config_context
= NULL
;
3158 /* shortcut a query */
3159 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3160 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3163 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3164 if (!ADS_ERR_OK(status
)) {
3168 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3169 if (filter
== NULL
) {
3170 return ADS_ERROR(LDAP_NO_MEMORY
);
3173 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3174 filter
, NULL
, &res
);
3175 if (!ADS_ERR_OK(status
)) {
3179 if (ads_count_replies(ads
, res
) != 1) {
3180 ads_msgfree(ads
, res
);
3181 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3184 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3186 ads_msgfree(ads
, res
);
3187 return ADS_ERROR(LDAP_NO_MEMORY
);
3190 /* go up three levels */
3191 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3192 if (parent
== NULL
) {
3193 ads_msgfree(ads
, res
);
3195 return ADS_ERROR(LDAP_NO_MEMORY
);
3198 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3199 if (*site_dn
== NULL
) {
3200 ads_msgfree(ads
, res
);
3202 return ADS_ERROR(LDAP_NO_MEMORY
);
3206 ads_msgfree(ads
, res
);
3212 * get the upn suffixes for a domain
3213 * @param ads connection to ads server
3214 * @param mem_ctx Pointer to talloc context
3215 * @param suffixes Pointer to an array of suffixes
3216 * @param num_suffixes Pointer to the number of suffixes
3217 * @return status of search
3219 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3224 char *config_context
= NULL
;
3225 const char *attrs
[] = { "uPNSuffixes", NULL
};
3227 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3228 if (!ADS_ERR_OK(status
)) {
3232 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3234 return ADS_ERROR(LDAP_NO_MEMORY
);
3237 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3238 if (!ADS_ERR_OK(status
)) {
3242 if (ads_count_replies(ads
, res
) != 1) {
3243 ads_msgfree(ads
, res
);
3244 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3247 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3248 if ((*suffixes
) == NULL
) {
3249 ads_msgfree(ads
, res
);
3250 return ADS_ERROR(LDAP_NO_MEMORY
);
3253 ads_msgfree(ads
, res
);
3259 * get the joinable ous for a domain
3260 * @param ads connection to ads server
3261 * @param mem_ctx Pointer to talloc context
3262 * @param ous Pointer to an array of ous
3263 * @param num_ous Pointer to the number of ous
3264 * @return status of search
3266 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3267 TALLOC_CTX
*mem_ctx
,
3272 LDAPMessage
*res
= NULL
;
3273 LDAPMessage
*msg
= NULL
;
3274 const char *attrs
[] = { "dn", NULL
};
3277 status
= ads_search(ads
, &res
,
3278 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3280 if (!ADS_ERR_OK(status
)) {
3284 count
= ads_count_replies(ads
, res
);
3286 ads_msgfree(ads
, res
);
3287 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3290 for (msg
= ads_first_entry(ads
, res
); msg
;
3291 msg
= ads_next_entry(ads
, msg
)) {
3295 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3297 ads_msgfree(ads
, res
);
3298 return ADS_ERROR(LDAP_NO_MEMORY
);
3301 if (!add_string_to_array(mem_ctx
, dn
,
3302 (const char ***)ous
,
3305 ads_msgfree(ads
, res
);
3306 return ADS_ERROR(LDAP_NO_MEMORY
);
3312 ads_msgfree(ads
, res
);
3319 * pull a struct dom_sid from an extended dn string
3320 * @param mem_ctx TALLOC_CTX
3321 * @param extended_dn string
3322 * @param flags string type of extended_dn
3323 * @param sid pointer to a struct dom_sid
3324 * @return NT_STATUS_OK on success,
3325 * NT_INVALID_PARAMETER on error,
3326 * NT_STATUS_NOT_FOUND if no SID present
3328 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3329 const char *extended_dn
,
3330 enum ads_extended_dn_flags flags
,
3331 struct dom_sid
*sid
)
3336 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3339 /* otherwise extended_dn gets stripped off */
3340 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3341 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3344 * ADS_EXTENDED_DN_HEX_STRING:
3345 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3347 * ADS_EXTENDED_DN_STRING (only with w2k3):
3348 * <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
3350 * Object with no SID, such as an Exchange Public Folder
3351 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3354 p
= strchr(dn
, ';');
3356 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3359 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3360 DEBUG(5,("No SID present in extended dn\n"));
3361 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3364 p
+= strlen(";<SID=");
3368 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3373 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3377 case ADS_EXTENDED_DN_STRING
:
3378 if (!string_to_sid(sid
, p
)) {
3379 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3382 case ADS_EXTENDED_DN_HEX_STRING
: {
3386 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3388 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3391 if (!sid_parse(buf
, buf_len
, sid
)) {
3392 DEBUG(10,("failed to parse sid\n"));
3393 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3398 DEBUG(10,("unknown extended dn format\n"));
3399 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3402 return ADS_ERROR_NT(NT_STATUS_OK
);
3405 /********************************************************************
3406 ********************************************************************/
3408 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3410 LDAPMessage
*res
= NULL
;
3415 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3416 if (!ADS_ERR_OK(status
)) {
3417 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3418 lp_netbios_name()));
3422 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3423 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3427 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3428 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3432 ads_msgfree(ads
, res
);
3437 /********************************************************************
3438 ********************************************************************/
3440 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3442 LDAPMessage
*res
= NULL
;
3447 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3448 if (!ADS_ERR_OK(status
)) {
3449 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3450 lp_netbios_name()));
3454 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3455 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3459 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3460 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3464 ads_msgfree(ads
, res
);
3469 /********************************************************************
3470 ********************************************************************/
3472 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3474 LDAPMessage
*res
= NULL
;
3479 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3480 if (!ADS_ERR_OK(status
)) {
3481 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3482 lp_netbios_name()));
3486 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3487 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3491 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3492 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3496 ads_msgfree(ads
, res
);
3503 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3506 * Join a machine to a realm
3507 * Creates the machine account and sets the machine password
3508 * @param ads connection to ads server
3509 * @param machine name of host to add
3510 * @param org_unit Organizational unit to place machine in
3511 * @return status of join
3513 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3514 uint32 account_type
, const char *org_unit
)
3517 LDAPMessage
*res
= NULL
;
3520 /* machine name must be lowercase */
3521 machine
= SMB_STRDUP(machine_name
);
3522 strlower_m(machine
);
3525 status = ads_find_machine_acct(ads, (void **)&res, machine);
3526 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3527 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3528 status = ads_leave_realm(ads, machine);
3529 if (!ADS_ERR_OK(status)) {
3530 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3531 machine, ads->config.realm));
3536 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3537 if (!ADS_ERR_OK(status
)) {
3538 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3543 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3544 if (!ADS_ERR_OK(status
)) {
3545 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3551 ads_msgfree(ads
, res
);
3558 * Delete a machine from the realm
3559 * @param ads connection to ads server
3560 * @param hostname Machine to remove
3561 * @return status of delete
3563 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3568 char *hostnameDN
, *host
;
3570 LDAPControl ldap_control
;
3571 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3573 pldap_control
[0] = &ldap_control
;
3574 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3575 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3577 /* hostname must be lowercase */
3578 host
= SMB_STRDUP(hostname
);
3579 if (!strlower_m(host
)) {
3581 return ADS_ERROR_SYSTEM(EINVAL
);
3584 status
= ads_find_machine_acct(ads
, &res
, host
);
3585 if (!ADS_ERR_OK(status
)) {
3586 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3591 msg
= ads_first_entry(ads
, res
);
3594 return ADS_ERROR_SYSTEM(ENOENT
);
3597 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3598 if (hostnameDN
== NULL
) {
3600 return ADS_ERROR_SYSTEM(ENOENT
);
3603 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3605 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3607 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3610 if (rc
!= LDAP_SUCCESS
) {
3611 const char *attrs
[] = { "cn", NULL
};
3612 LDAPMessage
*msg_sub
;
3614 /* we only search with scope ONE, we do not expect any further
3615 * objects to be created deeper */
3617 status
= ads_do_search_retry(ads
, hostnameDN
,
3618 LDAP_SCOPE_ONELEVEL
,
3619 "(objectclass=*)", attrs
, &res
);
3621 if (!ADS_ERR_OK(status
)) {
3623 TALLOC_FREE(hostnameDN
);
3627 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3628 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3632 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3634 TALLOC_FREE(hostnameDN
);
3635 return ADS_ERROR(LDAP_NO_MEMORY
);
3638 status
= ads_del_dn(ads
, dn
);
3639 if (!ADS_ERR_OK(status
)) {
3640 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3643 TALLOC_FREE(hostnameDN
);
3650 /* there should be no subordinate objects anymore */
3651 status
= ads_do_search_retry(ads
, hostnameDN
,
3652 LDAP_SCOPE_ONELEVEL
,
3653 "(objectclass=*)", attrs
, &res
);
3655 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3657 TALLOC_FREE(hostnameDN
);
3661 /* delete hostnameDN now */
3662 status
= ads_del_dn(ads
, hostnameDN
);
3663 if (!ADS_ERR_OK(status
)) {
3665 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3666 TALLOC_FREE(hostnameDN
);
3671 TALLOC_FREE(hostnameDN
);
3673 status
= ads_find_machine_acct(ads
, &res
, host
);
3674 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3675 DEBUG(3, ("Failed to remove host account.\n"));
3685 * pull all token-sids from an LDAP dn
3686 * @param ads connection to ads server
3687 * @param mem_ctx TALLOC_CTX for allocating sid array
3688 * @param dn of LDAP object
3689 * @param user_sid pointer to struct dom_sid (objectSid)
3690 * @param primary_group_sid pointer to struct dom_sid (self composed)
3691 * @param sids pointer to sid array to allocate
3692 * @param num_sids counter of SIDs pulled
3693 * @return status of token query
3695 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3696 TALLOC_CTX
*mem_ctx
,
3698 struct dom_sid
*user_sid
,
3699 struct dom_sid
*primary_group_sid
,
3700 struct dom_sid
**sids
,
3704 LDAPMessage
*res
= NULL
;
3706 size_t tmp_num_sids
;
3707 struct dom_sid
*tmp_sids
;
3708 struct dom_sid tmp_user_sid
;
3709 struct dom_sid tmp_primary_group_sid
;
3711 const char *attrs
[] = {
3718 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3719 if (!ADS_ERR_OK(status
)) {
3723 count
= ads_count_replies(ads
, res
);
3725 ads_msgfree(ads
, res
);
3726 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3729 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3730 ads_msgfree(ads
, res
);
3731 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3734 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3735 ads_msgfree(ads
, res
);
3736 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3740 /* hack to compose the primary group sid without knowing the
3743 struct dom_sid domsid
;
3745 sid_copy(&domsid
, &tmp_user_sid
);
3747 if (!sid_split_rid(&domsid
, NULL
)) {
3748 ads_msgfree(ads
, res
);
3749 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3752 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3753 ads_msgfree(ads
, res
);
3754 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3758 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3760 if (tmp_num_sids
== 0 || !tmp_sids
) {
3761 ads_msgfree(ads
, res
);
3762 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3766 *num_sids
= tmp_num_sids
;
3774 *user_sid
= tmp_user_sid
;
3777 if (primary_group_sid
) {
3778 *primary_group_sid
= tmp_primary_group_sid
;
3781 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3783 ads_msgfree(ads
, res
);
3784 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3788 * Find a sAMAccoutName in LDAP
3789 * @param ads connection to ads server
3790 * @param mem_ctx TALLOC_CTX for allocating sid array
3791 * @param samaccountname to search
3792 * @param uac_ret uint32 pointer userAccountControl attribute value
3793 * @param dn_ret pointer to dn
3794 * @return status of token query
3796 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3797 TALLOC_CTX
*mem_ctx
,
3798 const char *samaccountname
,
3800 const char **dn_ret
)
3803 const char *attrs
[] = { "userAccountControl", NULL
};
3805 LDAPMessage
*res
= NULL
;
3809 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3811 if (filter
== NULL
) {
3812 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3816 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3818 filter
, attrs
, &res
);
3820 if (!ADS_ERR_OK(status
)) {
3824 if (ads_count_replies(ads
, res
) != 1) {
3825 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3829 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3831 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3835 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3836 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3845 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3847 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3853 ads_msgfree(ads
, res
);
3859 * find our configuration path
3860 * @param ads connection to ads server
3861 * @param mem_ctx Pointer to talloc context
3862 * @param config_path Pointer to the config path
3863 * @return status of search
3865 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3866 TALLOC_CTX
*mem_ctx
,
3870 LDAPMessage
*res
= NULL
;
3871 const char *config_context
= NULL
;
3872 const char *attrs
[] = { "configurationNamingContext", NULL
};
3874 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3875 "(objectclass=*)", attrs
, &res
);
3876 if (!ADS_ERR_OK(status
)) {
3880 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3881 "configurationNamingContext");
3882 ads_msgfree(ads
, res
);
3883 if (!config_context
) {
3884 return ADS_ERROR(LDAP_NO_MEMORY
);
3888 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3889 if (!*config_path
) {
3890 return ADS_ERROR(LDAP_NO_MEMORY
);
3894 return ADS_ERROR(LDAP_SUCCESS
);
3898 * find the displayName of an extended right
3899 * @param ads connection to ads server
3900 * @param config_path The config path
3901 * @param mem_ctx Pointer to talloc context
3902 * @param GUID struct of the rightsGUID
3903 * @return status of search
3905 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3906 const char *config_path
,
3907 TALLOC_CTX
*mem_ctx
,
3908 const struct GUID
*rights_guid
)
3911 LDAPMessage
*res
= NULL
;
3913 const char *attrs
[] = { "displayName", NULL
};
3914 const char *result
= NULL
;
3917 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3921 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3922 GUID_string(mem_ctx
, rights_guid
));
3927 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3932 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3934 if (!ADS_ERR_OK(rc
)) {
3938 if (ads_count_replies(ads
, res
) != 1) {
3942 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3945 ads_msgfree(ads
, res
);
3950 * verify or build and verify an account ou
3951 * @param mem_ctx Pointer to talloc context
3952 * @param ads connection to ads server
3954 * @return status of search
3957 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3959 const char **account_ou
)
3965 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
3967 ldap_value_free(exploded_dn
);
3971 ou_string
= ads_ou_string(ads
, *account_ou
);
3973 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3976 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3977 ads
->config
.bind_path
);
3978 SAFE_FREE(ou_string
);
3981 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3984 exploded_dn
= ldap_explode_dn(name
, 0);
3986 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3988 ldap_value_free(exploded_dn
);