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/>.
25 #include "lib/ldb/include/includes.h"
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm
;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP
*ldap_open_with_timeout(const char *server
, int port
, unsigned int to
)
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server
, port
, to
));
66 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
68 /* End setup timeout. */
70 ldp
= ldap_open(server
, port
);
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server
, port
, strerror(errno
)));
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server
, port
));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
86 static int ldap_search_with_timeout(LDAP
*ld
,
87 LDAP_CONST
char *base
,
89 LDAP_CONST
char *filter
,
97 struct timeval timeout
;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout
.tv_sec
= lp_ldap_timeout();
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
106 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
111 attrsonly
, sctrls
, cctrls
, &timeout
,
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
119 return LDAP_TIMELIMIT_EXCEEDED
;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT
*ads
)
130 if (ads
->config
.server_site_name
== NULL
&&
131 ads
->config
.client_site_name
== NULL
) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
135 if (ads
->config
.server_site_name
&&
136 ads
->config
.client_site_name
&&
137 strequal(ads
->config
.server_site_name
,
138 ads
->config
.client_site_name
)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
144 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT
*ads
)
154 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads
)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
165 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
166 ads
->config
.ldap_server_name
));
173 try a connection to a given ldap server, returning True and setting the servers IP
174 in the ads struct if successful
176 static bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
, bool gc
)
179 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
;
180 TALLOC_CTX
*mem_ctx
= NULL
;
183 if (!server
|| !*server
) {
187 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
188 server
, ads
->server
.realm
));
190 mem_ctx
= talloc_init("ads_try_connect");
192 DEBUG(0,("out of memory\n"));
196 /* this copes with inet_ntoa brokenness */
198 srv
= SMB_STRDUP(server
);
200 ZERO_STRUCT( cldap_reply
);
202 if ( !ads_cldap_netlogon_5(mem_ctx
, srv
, ads
->server
.realm
, &cldap_reply
) ) {
203 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
208 /* Check the CLDAP reply flags */
210 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
211 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
217 /* Fill in the ads->config values */
219 SAFE_FREE(ads
->config
.realm
);
220 SAFE_FREE(ads
->config
.bind_path
);
221 SAFE_FREE(ads
->config
.ldap_server_name
);
222 SAFE_FREE(ads
->config
.server_site_name
);
223 SAFE_FREE(ads
->config
.client_site_name
);
224 SAFE_FREE(ads
->server
.workgroup
);
226 ads
->config
.flags
= cldap_reply
.server_type
;
227 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
228 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
229 strupper_m(ads
->config
.realm
);
230 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
231 if (*cldap_reply
.server_site
) {
232 ads
->config
.server_site_name
=
233 SMB_STRDUP(cldap_reply
.server_site
);
235 if (*cldap_reply
.client_site
) {
236 ads
->config
.client_site_name
=
237 SMB_STRDUP(cldap_reply
.client_site
);
239 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain
);
241 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
242 if (!interpret_string_addr(&ads
->ldap
.ss
, srv
, 0)) {
243 DEBUG(1,("ads_try_connect: unable to convert %s "
250 /* Store our site name. */
251 sitename_store( cldap_reply
.domain
, cldap_reply
.client_site
);
252 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
257 TALLOC_FREE(mem_ctx
);
262 /**********************************************************************
263 Try to find an AD dc using our internal name resolution routines
264 Try the realm first and then then workgroup name if netbios is not
266 **********************************************************************/
268 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
272 struct ip_service
*ip_list
;
274 bool got_realm
= False
;
275 bool use_own_domain
= False
;
277 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
279 /* if the realm and workgroup are both empty, assume they are ours */
282 c_realm
= ads
->server
.realm
;
284 if ( !c_realm
|| !*c_realm
) {
285 /* special case where no realm and no workgroup means our own */
286 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
287 use_own_domain
= True
;
288 c_realm
= lp_realm();
292 if (c_realm
&& *c_realm
)
295 /* we need to try once with the realm name and fallback to the
296 netbios domain name if we fail (if netbios has not been disabled */
298 if ( !got_realm
&& !lp_disable_netbios() ) {
299 c_realm
= ads
->server
.workgroup
;
300 if (!c_realm
|| !*c_realm
) {
301 if ( use_own_domain
)
302 c_realm
= lp_workgroup();
306 if ( !c_realm
|| !*c_realm
) {
307 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
308 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
313 sitename
= sitename_fetch(realm
);
317 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
318 (got_realm
? "realm" : "domain"), realm
));
320 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
321 if (!NT_STATUS_IS_OK(status
)) {
322 /* fall back to netbios if we can */
323 if ( got_realm
&& !lp_disable_netbios() ) {
332 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
333 for ( i
=0; i
<count
; i
++ ) {
334 char server
[INET6_ADDRSTRLEN
];
336 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
338 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
342 /* realm in this case is a workgroup name. We need
343 to ignore any IP addresses in the negative connection
344 cache that match ip addresses returned in the ad realm
345 case. It sucks that I have to reproduce the logic above... */
346 c_realm
= ads
->server
.realm
;
347 if ( !c_realm
|| !*c_realm
) {
348 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
349 c_realm
= lp_realm();
352 if (c_realm
&& *c_realm
&&
353 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
354 /* Ensure we add the workgroup name for this
355 IP address as negative too. */
356 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
361 if ( ads_try_connect(ads
, server
, false) ) {
367 /* keep track of failures */
368 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
373 /* In case we failed to contact one of our closest DC on our site we
374 * need to try to find another DC, retry with a site-less SRV DNS query
378 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
379 "trying to find another DC\n", sitename
));
381 namecache_delete(realm
, 0x1C);
385 return NT_STATUS_NO_LOGON_SERVERS
;
388 /*********************************************************************
389 *********************************************************************/
391 static NTSTATUS
ads_lookup_site(void)
393 ADS_STRUCT
*ads
= NULL
;
394 ADS_STATUS ads_status
;
395 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
397 ads
= ads_init(lp_realm(), NULL
, NULL
);
399 return NT_STATUS_NO_MEMORY
;
402 /* The NO_BIND here will find a DC and set the client site
403 but not establish the TCP connection */
405 ads
->auth
.flags
= ADS_AUTH_NO_BIND
;
406 ads_status
= ads_connect(ads
);
407 if (!ADS_ERR_OK(ads_status
)) {
408 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
409 ads_errstr(ads_status
)));
411 nt_status
= ads_ntstatus(ads_status
);
420 /*********************************************************************
421 *********************************************************************/
423 static const char* host_dns_domain(const char *fqdn
)
425 const char *p
= fqdn
;
427 /* go to next char following '.' */
429 if ((p
= strchr_m(fqdn
, '.')) != NULL
) {
438 * Connect to the Global Catalog server
439 * @param ads Pointer to an existing ADS_STRUCT
440 * @return status of connection
442 * Simple wrapper around ads_connect() that fills in the
443 * GC ldap server information
446 ADS_STATUS
ads_connect_gc(ADS_STRUCT
*ads
)
448 TALLOC_CTX
*frame
= talloc_stackframe();
449 struct dns_rr_srv
*gcs_list
;
451 char *realm
= ads
->server
.realm
;
452 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
453 ADS_STATUS ads_status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
456 char *sitename
= NULL
;
461 if ((sitename
= sitename_fetch(realm
)) == NULL
) {
463 sitename
= sitename_fetch(realm
);
467 /* We try once with a sitename and once without
468 (unless we don't have a sitename and then we're
471 if (sitename
== NULL
)
474 nt_status
= ads_dns_query_gcs(frame
, realm
, sitename
,
475 &gcs_list
, &num_gcs
);
479 if (!NT_STATUS_IS_OK(nt_status
)) {
480 ads_status
= ADS_ERROR_NT(nt_status
);
484 /* Loop until we get a successful connection or have gone
485 through them all. When connecting a GC server, make sure that
486 the realm is the server's DNS name and not the forest root */
488 for (i
=0; i
<num_gcs
; i
++) {
489 ads
->server
.gc
= true;
490 ads
->server
.ldap_server
= SMB_STRDUP(gcs_list
[i
].hostname
);
491 ads
->server
.realm
= SMB_STRDUP(host_dns_domain(ads
->server
.ldap_server
));
492 ads_status
= ads_connect(ads
);
493 if (ADS_ERR_OK(ads_status
)) {
494 /* Reset the bind_dn to "". A Global Catalog server
495 may host multiple domain trees in a forest.
496 Windows 2003 GC server will accept "" as the search
497 path to imply search all domain trees in the forest */
499 SAFE_FREE(ads
->config
.bind_path
);
500 ads
->config
.bind_path
= SMB_STRDUP("");
505 SAFE_FREE(ads
->server
.ldap_server
);
506 SAFE_FREE(ads
->server
.realm
);
509 TALLOC_FREE(gcs_list
);
515 talloc_destroy(frame
);
522 * Connect to the LDAP server
523 * @param ads Pointer to an existing ADS_STRUCT
524 * @return status of connection
526 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
528 int version
= LDAP_VERSION3
;
531 char addr
[INET6_ADDRSTRLEN
];
533 ZERO_STRUCT(ads
->ldap
);
534 ads
->ldap
.last_attempt
= time(NULL
);
535 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
537 /* try with a user specified server */
539 if (DEBUGLEVEL
>= 11) {
540 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
541 DEBUG(11,("ads_connect: entering\n"));
542 DEBUGADD(11,("%s\n", s
));
546 if (ads
->server
.ldap_server
&&
547 ads_try_connect(ads
, ads
->server
.ldap_server
, ads
->server
.gc
)) {
551 ntstatus
= ads_find_dc(ads
);
552 if (NT_STATUS_IS_OK(ntstatus
)) {
556 status
= ADS_ERROR_NT(ntstatus
);
561 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
562 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
564 if (!ads
->auth
.user_name
) {
565 /* Must use the userPrincipalName value here or sAMAccountName
566 and not servicePrincipalName; found by Guenther Deschner */
568 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
571 if (!ads
->auth
.realm
) {
572 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
575 if (!ads
->auth
.kdc_server
) {
576 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
577 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
581 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
582 to MIT kerberos to work (tridge) */
585 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
586 setenv(env
, ads
->auth
.kdc_server
, 1);
591 /* If the caller() requested no LDAP bind, then we are done */
593 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
594 status
= ADS_SUCCESS
;
598 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
599 if (!ads
->ldap
.mem_ctx
) {
600 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
604 /* Otherwise setup the TCP LDAP session */
606 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
607 ads
->ldap
.port
, lp_ldap_timeout());
608 if (ads
->ldap
.ld
== NULL
) {
609 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
612 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
614 /* cache the successful connection for workgroup and realm */
615 if (ads_closest_dc(ads
)) {
616 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
617 saf_store( ads
->server
.workgroup
, addr
);
618 saf_store( ads
->server
.realm
, addr
);
621 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
623 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
624 if (!ADS_ERR_OK(status
)) {
628 /* fill in the current time and offsets */
630 status
= ads_current_time( ads
);
631 if ( !ADS_ERR_OK(status
) ) {
635 /* Now do the bind */
637 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
638 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
642 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
643 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
647 status
= ads_sasl_bind(ads
);
650 if (DEBUGLEVEL
>= 11) {
651 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
652 DEBUG(11,("ads_connect: leaving with: %s\n",
653 ads_errstr(status
)));
654 DEBUGADD(11,("%s\n", s
));
662 * Connect to the LDAP server using given credentials
663 * @param ads Pointer to an existing ADS_STRUCT
664 * @return status of connection
666 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
668 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
670 return ads_connect(ads
);
674 * Disconnect the LDAP server
675 * @param ads Pointer to an existing ADS_STRUCT
677 void ads_disconnect(ADS_STRUCT
*ads
)
680 ldap_unbind(ads
->ldap
.ld
);
683 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
684 ads
->ldap
.wrap_ops
->disconnect(ads
);
686 if (ads
->ldap
.mem_ctx
) {
687 talloc_free(ads
->ldap
.mem_ctx
);
689 ZERO_STRUCT(ads
->ldap
);
693 Duplicate a struct berval into talloc'ed memory
695 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
697 struct berval
*value
;
699 if (!in_val
) return NULL
;
701 value
= TALLOC_ZERO_P(ctx
, struct berval
);
704 if (in_val
->bv_len
== 0) return value
;
706 value
->bv_len
= in_val
->bv_len
;
707 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
713 Make a values list out of an array of (struct berval *)
715 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
716 const struct berval
**in_vals
)
718 struct berval
**values
;
721 if (!in_vals
) return NULL
;
722 for (i
=0; in_vals
[i
]; i
++)
724 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
725 if (!values
) return NULL
;
727 for (i
=0; in_vals
[i
]; i
++) {
728 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
734 UTF8-encode a values list out of an array of (char *)
736 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
742 if (!in_vals
) return NULL
;
743 for (i
=0; in_vals
[i
]; i
++)
745 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
746 if (!values
) return NULL
;
748 for (i
=0; in_vals
[i
]; i
++) {
749 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
758 Pull a (char *) array out of a UTF8-encoded values list
760 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
764 size_t converted_size
;
766 if (!in_vals
) return NULL
;
767 for (i
=0; in_vals
[i
]; i
++)
769 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
770 if (!values
) return NULL
;
772 for (i
=0; in_vals
[i
]; i
++) {
773 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
775 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
776 "%s", strerror(errno
)));
783 * Do a search with paged results. cookie must be null on the first
784 * call, and then returned on each subsequent call. It will be null
785 * again when the entire search is complete
786 * @param ads connection to ads server
787 * @param bind_path Base dn for the search
788 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
789 * @param expr Search expression - specified in local charset
790 * @param attrs Attributes to retrieve - specified in utf8 or ascii
791 * @param res ** which will contain results - free res* with ads_msgfree()
792 * @param count Number of entries retrieved on this page
793 * @param cookie The paged results cookie to be returned on subsequent calls
794 * @return status of search
796 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
797 const char *bind_path
,
798 int scope
, const char *expr
,
799 const char **attrs
, void *args
,
801 int *count
, struct berval
**cookie
)
804 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
805 size_t converted_size
;
806 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
807 BerElement
*cookie_be
= NULL
;
808 struct berval
*cookie_bv
= NULL
;
809 BerElement
*ext_be
= NULL
;
810 struct berval
*ext_bv
= NULL
;
813 ads_control
*external_control
= (ads_control
*) args
;
817 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
818 return ADS_ERROR(LDAP_NO_MEMORY
);
820 /* 0 means the conversion worked but the result was empty
821 so we only fail if it's -1. In any case, it always
822 at least nulls out the dest */
823 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
824 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
830 if (!attrs
|| !(*attrs
))
833 /* This would be the utf8-encoded version...*/
834 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
835 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
841 /* Paged results only available on ldap v3 or later */
842 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
843 if (version
< LDAP_VERSION3
) {
844 rc
= LDAP_NOT_SUPPORTED
;
848 cookie_be
= ber_alloc_t(LBER_USE_DER
);
850 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
851 ber_bvfree(*cookie
); /* don't need it from last time */
854 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
856 ber_flatten(cookie_be
, &cookie_bv
);
857 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
858 PagedResults
.ldctl_iscritical
= (char) 1;
859 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
860 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
862 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
863 NoReferrals
.ldctl_iscritical
= (char) 0;
864 NoReferrals
.ldctl_value
.bv_len
= 0;
865 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
867 if (external_control
&&
868 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
869 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
871 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
872 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
874 /* win2k does not accept a ldctl_value beeing passed in */
876 if (external_control
->val
!= 0) {
878 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
883 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
887 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
892 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
893 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
896 ExternalCtrl
.ldctl_value
.bv_len
= 0;
897 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
900 controls
[0] = &NoReferrals
;
901 controls
[1] = &PagedResults
;
902 controls
[2] = &ExternalCtrl
;
906 controls
[0] = &NoReferrals
;
907 controls
[1] = &PagedResults
;
911 /* we need to disable referrals as the openldap libs don't
912 handle them and paged results at the same time. Using them
913 together results in the result record containing the server
914 page control being removed from the result list (tridge/jmcd)
916 leaving this in despite the control that says don't generate
917 referrals, in case the server doesn't support it (jmcd)
919 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
921 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
922 search_attrs
, 0, controls
,
924 (LDAPMessage
**)res
);
926 ber_free(cookie_be
, 1);
927 ber_bvfree(cookie_bv
);
930 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
931 ldap_err2string(rc
)));
935 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
936 NULL
, &rcontrols
, 0);
942 for (i
=0; rcontrols
[i
]; i
++) {
943 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
944 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
945 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
947 /* the berval is the cookie, but must be freed when
949 if (cookie_bv
->bv_len
) /* still more to do */
950 *cookie
=ber_bvdup(cookie_bv
);
953 ber_bvfree(cookie_bv
);
954 ber_free(cookie_be
, 1);
958 ldap_controls_free(rcontrols
);
971 /* if/when we decide to utf8-encode attrs, take out this next line */
972 TALLOC_FREE(search_attrs
);
974 return ADS_ERROR(rc
);
977 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
978 int scope
, const char *expr
,
979 const char **attrs
, LDAPMessage
**res
,
980 int *count
, struct berval
**cookie
)
982 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
987 * Get all results for a search. This uses ads_do_paged_search() to return
988 * all entries in a large search.
989 * @param ads connection to ads server
990 * @param bind_path Base dn for the search
991 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
992 * @param expr Search expression
993 * @param attrs Attributes to retrieve
994 * @param res ** which will contain results - free res* with ads_msgfree()
995 * @return status of search
997 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
998 int scope
, const char *expr
,
999 const char **attrs
, void *args
,
1002 struct berval
*cookie
= NULL
;
1007 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1010 if (!ADS_ERR_OK(status
))
1013 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1015 LDAPMessage
*res2
= NULL
;
1017 LDAPMessage
*msg
, *next
;
1019 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1020 attrs
, args
, &res2
, &count
, &cookie
);
1022 if (!ADS_ERR_OK(status2
)) break;
1024 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1025 that this works on all ldap libs, but I have only tested with openldap */
1026 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1027 next
= ads_next_message(ads
, msg
);
1028 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1030 /* note that we do not free res2, as the memory is now
1031 part of the main returned list */
1034 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1035 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1041 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1042 int scope
, const char *expr
,
1043 const char **attrs
, LDAPMessage
**res
)
1045 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1048 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1049 int scope
, const char *expr
,
1050 const char **attrs
, uint32 sd_flags
,
1055 args
.control
= ADS_SD_FLAGS_OID
;
1056 args
.val
= sd_flags
;
1057 args
.critical
= True
;
1059 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1064 * Run a function on all results for a search. Uses ads_do_paged_search() and
1065 * runs the function as each page is returned, using ads_process_results()
1066 * @param ads connection to ads server
1067 * @param bind_path Base dn for the search
1068 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1069 * @param expr Search expression - specified in local charset
1070 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1071 * @param fn Function which takes attr name, values list, and data_area
1072 * @param data_area Pointer which is passed to function on each call
1073 * @return status of search
1075 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1076 int scope
, const char *expr
, const char **attrs
,
1077 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1080 struct berval
*cookie
= NULL
;
1085 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1088 if (!ADS_ERR_OK(status
)) return status
;
1090 ads_process_results(ads
, res
, fn
, data_area
);
1091 ads_msgfree(ads
, res
);
1094 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1095 &res
, &count
, &cookie
);
1097 if (!ADS_ERR_OK(status
)) break;
1099 ads_process_results(ads
, res
, fn
, data_area
);
1100 ads_msgfree(ads
, res
);
1107 * Do a search with a timeout.
1108 * @param ads connection to ads server
1109 * @param bind_path Base dn for the search
1110 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1111 * @param expr Search expression
1112 * @param attrs Attributes to retrieve
1113 * @param res ** which will contain results - free res* with ads_msgfree()
1114 * @return status of search
1116 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1118 const char **attrs
, LDAPMessage
**res
)
1121 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1122 size_t converted_size
;
1126 if (!(ctx
= talloc_init("ads_do_search"))) {
1127 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1128 return ADS_ERROR(LDAP_NO_MEMORY
);
1131 /* 0 means the conversion worked but the result was empty
1132 so we only fail if it's negative. In any case, it always
1133 at least nulls out the dest */
1134 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1135 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1137 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1138 rc
= LDAP_NO_MEMORY
;
1142 if (!attrs
|| !(*attrs
))
1143 search_attrs
= NULL
;
1145 /* This would be the utf8-encoded version...*/
1146 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1147 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1149 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1150 rc
= LDAP_NO_MEMORY
;
1155 /* see the note in ads_do_paged_search - we *must* disable referrals */
1156 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1158 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1159 search_attrs
, 0, NULL
, NULL
,
1161 (LDAPMessage
**)res
);
1163 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1164 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1169 talloc_destroy(ctx
);
1170 /* if/when we decide to utf8-encode attrs, take out this next line */
1171 TALLOC_FREE(search_attrs
);
1172 return ADS_ERROR(rc
);
1175 * Do a general ADS search
1176 * @param ads connection to ads server
1177 * @param res ** which will contain results - free res* with ads_msgfree()
1178 * @param expr Search expression
1179 * @param attrs Attributes to retrieve
1180 * @return status of search
1182 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1183 const char *expr
, const char **attrs
)
1185 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1190 * Do a search on a specific DistinguishedName
1191 * @param ads connection to ads server
1192 * @param res ** which will contain results - free res* with ads_msgfree()
1193 * @param dn DistinguishName to search
1194 * @param attrs Attributes to retrieve
1195 * @return status of search
1197 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1198 const char *dn
, const char **attrs
)
1200 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1205 * Free up memory from a ads_search
1206 * @param ads connection to ads server
1207 * @param msg Search results to free
1209 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1216 * Free up memory from various ads requests
1217 * @param ads connection to ads server
1218 * @param mem Area to free
1220 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1226 * Get a dn from search results
1227 * @param ads connection to ads server
1228 * @param msg Search result
1231 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1233 char *utf8_dn
, *unix_dn
;
1234 size_t converted_size
;
1236 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1239 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1243 if (!pull_utf8_allocate(&unix_dn
, utf8_dn
, &converted_size
)) {
1244 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1248 ldap_memfree(utf8_dn
);
1253 * Get the parent from a dn
1254 * @param dn the dn to return the parent from
1255 * @return parent dn string
1257 char *ads_parent_dn(const char *dn
)
1265 p
= strchr(dn
, ',');
1275 * Find a machine account given a hostname
1276 * @param ads connection to ads server
1277 * @param res ** which will contain results - free res* with ads_msgfree()
1278 * @param host Hostname to search for
1279 * @return status of search
1281 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1282 const char *machine
)
1286 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1290 /* the easiest way to find a machine account anywhere in the tree
1291 is to look for hostname$ */
1292 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1293 DEBUG(1, ("asprintf failed!\n"));
1294 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1297 status
= ads_search(ads
, res
, expr
, attrs
);
1303 * Initialize a list of mods to be used in a modify request
1304 * @param ctx An initialized TALLOC_CTX
1305 * @return allocated ADS_MODLIST
1307 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1309 #define ADS_MODLIST_ALLOC_SIZE 10
1312 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1313 /* -1 is safety to make sure we don't go over the end.
1314 need to reset it to NULL before doing ldap modify */
1315 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1317 return (ADS_MODLIST
)mods
;
1322 add an attribute to the list, with values list already constructed
1324 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1325 int mod_op
, const char *name
,
1326 const void *_invals
)
1328 const void **invals
= (const void **)_invals
;
1330 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1331 struct berval
**ber_values
= NULL
;
1332 char **char_values
= NULL
;
1335 mod_op
= LDAP_MOD_DELETE
;
1337 if (mod_op
& LDAP_MOD_BVALUES
)
1338 ber_values
= ads_dup_values(ctx
,
1339 (const struct berval
**)invals
);
1341 char_values
= ads_push_strvals(ctx
,
1342 (const char **) invals
);
1345 /* find the first empty slot */
1346 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1348 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1349 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1350 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1351 return ADS_ERROR(LDAP_NO_MEMORY
);
1352 memset(&modlist
[curmod
], 0,
1353 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1354 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1355 *mods
= (ADS_MODLIST
)modlist
;
1358 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1359 return ADS_ERROR(LDAP_NO_MEMORY
);
1360 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1361 if (mod_op
& LDAP_MOD_BVALUES
) {
1362 modlist
[curmod
]->mod_bvalues
= ber_values
;
1363 } else if (mod_op
& LDAP_MOD_DELETE
) {
1364 modlist
[curmod
]->mod_values
= NULL
;
1366 modlist
[curmod
]->mod_values
= char_values
;
1369 modlist
[curmod
]->mod_op
= mod_op
;
1370 return ADS_ERROR(LDAP_SUCCESS
);
1374 * Add a single string value to a mod list
1375 * @param ctx An initialized TALLOC_CTX
1376 * @param mods An initialized ADS_MODLIST
1377 * @param name The attribute name to add
1378 * @param val The value to add - NULL means DELETE
1379 * @return ADS STATUS indicating success of add
1381 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1382 const char *name
, const char *val
)
1384 const char *values
[2];
1390 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1391 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1395 * Add an array of string values to a mod list
1396 * @param ctx An initialized TALLOC_CTX
1397 * @param mods An initialized ADS_MODLIST
1398 * @param name The attribute name to add
1399 * @param vals The array of string values to add - NULL means DELETE
1400 * @return ADS STATUS indicating success of add
1402 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1403 const char *name
, const char **vals
)
1406 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1407 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1408 name
, (const void **) vals
);
1413 * Add a single ber-encoded value to a mod list
1414 * @param ctx An initialized TALLOC_CTX
1415 * @param mods An initialized ADS_MODLIST
1416 * @param name The attribute name to add
1417 * @param val The value to add - NULL means DELETE
1418 * @return ADS STATUS indicating success of add
1420 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1421 const char *name
, const struct berval
*val
)
1423 const struct berval
*values
[2];
1428 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1429 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1430 name
, (const void **) values
);
1435 * Perform an ldap modify
1436 * @param ads connection to ads server
1437 * @param mod_dn DistinguishedName to modify
1438 * @param mods list of modifications to perform
1439 * @return status of modify
1441 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1444 char *utf8_dn
= NULL
;
1445 size_t converted_size
;
1447 this control is needed to modify that contains a currently
1448 non-existent attribute (but allowable for the object) to run
1450 LDAPControl PermitModify
= {
1451 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1454 LDAPControl
*controls
[2];
1456 controls
[0] = &PermitModify
;
1459 if (!push_utf8_allocate(&utf8_dn
, mod_dn
, &converted_size
)) {
1460 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1463 /* find the end of the list, marked by NULL or -1 */
1464 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1465 /* make sure the end of the list is NULL */
1467 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1468 (LDAPMod
**) mods
, controls
, NULL
);
1470 return ADS_ERROR(ret
);
1474 * Perform an ldap add
1475 * @param ads connection to ads server
1476 * @param new_dn DistinguishedName to add
1477 * @param mods list of attributes and values for DN
1478 * @return status of add
1480 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1483 char *utf8_dn
= NULL
;
1484 size_t converted_size
;
1486 if (!push_utf8_allocate(&utf8_dn
, new_dn
, &converted_size
)) {
1487 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1488 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1491 /* find the end of the list, marked by NULL or -1 */
1492 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1493 /* make sure the end of the list is NULL */
1496 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1498 return ADS_ERROR(ret
);
1502 * Delete a DistinguishedName
1503 * @param ads connection to ads server
1504 * @param new_dn DistinguishedName to delete
1505 * @return status of delete
1507 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1510 char *utf8_dn
= NULL
;
1511 size_t converted_size
;
1512 if (!push_utf8_allocate(&utf8_dn
, del_dn
, &converted_size
)) {
1513 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1514 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1517 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1519 return ADS_ERROR(ret
);
1523 * Build an org unit string
1524 * if org unit is Computers or blank then assume a container, otherwise
1525 * assume a / separated list of organisational units.
1526 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1527 * @param ads connection to ads server
1528 * @param org_unit Organizational unit
1529 * @return org unit string - caller must free
1531 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1535 if (!org_unit
|| !*org_unit
) {
1537 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1539 /* samba4 might not yet respond to a wellknownobject-query */
1540 return ret
? ret
: SMB_STRDUP("cn=Computers");
1543 if (strequal(org_unit
, "Computers")) {
1544 return SMB_STRDUP("cn=Computers");
1547 /* jmcd: removed "\\" from the separation chars, because it is
1548 needed as an escape for chars like '#' which are valid in an
1550 return ads_build_path(org_unit
, "/", "ou=", 1);
1554 * Get a org unit string for a well-known GUID
1555 * @param ads connection to ads server
1556 * @param wknguid Well known GUID
1557 * @return org unit string - caller must free
1559 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1562 LDAPMessage
*res
= NULL
;
1563 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1564 **bind_dn_exp
= NULL
;
1565 const char *attrs
[] = {"distinguishedName", NULL
};
1566 int new_ln
, wkn_ln
, bind_ln
, i
;
1568 if (wknguid
== NULL
) {
1572 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1573 DEBUG(1, ("asprintf failed!\n"));
1577 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1578 if (!ADS_ERR_OK(status
)) {
1579 DEBUG(1,("Failed while searching for: %s\n", base
));
1583 if (ads_count_replies(ads
, res
) != 1) {
1587 /* substitute the bind-path from the well-known-guid-search result */
1588 wkn_dn
= ads_get_dn(ads
, res
);
1593 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1598 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1603 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1605 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1608 new_ln
= wkn_ln
- bind_ln
;
1610 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1615 for (i
=1; i
< new_ln
; i
++) {
1618 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1624 ret
= SMB_STRDUP(s
);
1633 ads_msgfree(ads
, res
);
1634 ads_memfree(ads
, wkn_dn
);
1636 ldap_value_free(wkn_dn_exp
);
1639 ldap_value_free(bind_dn_exp
);
1646 * Adds (appends) an item to an attribute array, rather then
1647 * replacing the whole list
1648 * @param ctx An initialized TALLOC_CTX
1649 * @param mods An initialized ADS_MODLIST
1650 * @param name name of the ldap attribute to append to
1651 * @param vals an array of values to add
1652 * @return status of addition
1655 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1656 const char *name
, const char **vals
)
1658 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1659 (const void *) vals
);
1663 * Determines the an account's current KVNO via an LDAP lookup
1664 * @param ads An initialized ADS_STRUCT
1665 * @param account_name the NT samaccountname.
1666 * @return the kvno for the account, or -1 in case of a failure.
1669 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1671 LDAPMessage
*res
= NULL
;
1672 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1674 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1675 char *dn_string
= NULL
;
1676 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1678 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1679 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1682 ret
= ads_search(ads
, &res
, filter
, attrs
);
1684 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1685 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1686 ads_msgfree(ads
, res
);
1690 dn_string
= ads_get_dn(ads
, res
);
1692 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1693 ads_msgfree(ads
, res
);
1696 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1697 ads_memfree(ads
, dn_string
);
1699 /* ---------------------------------------------------------
1700 * 0 is returned as a default KVNO from this point on...
1701 * This is done because Windows 2000 does not support key
1702 * version numbers. Chances are that a failure in the next
1703 * step is simply due to Windows 2000 being used for a
1704 * domain controller. */
1707 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1708 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1709 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1710 ads_msgfree(ads
, res
);
1715 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1716 ads_msgfree(ads
, res
);
1721 * Determines the computer account's current KVNO via an LDAP lookup
1722 * @param ads An initialized ADS_STRUCT
1723 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1724 * @return the kvno for the computer account, or -1 in case of a failure.
1727 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1729 char *computer_account
= NULL
;
1732 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1736 kvno
= ads_get_kvno(ads
, computer_account
);
1737 free(computer_account
);
1743 * This clears out all registered spn's for a given hostname
1744 * @param ads An initilaized ADS_STRUCT
1745 * @param machine_name the NetBIOS name of the computer.
1746 * @return 0 upon success, non-zero otherwise.
1749 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1752 LDAPMessage
*res
= NULL
;
1754 const char *servicePrincipalName
[1] = {NULL
};
1755 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1756 char *dn_string
= NULL
;
1758 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1759 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1760 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1761 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1762 ads_msgfree(ads
, res
);
1763 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1766 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1767 ctx
= talloc_init("ads_clear_service_principal_names");
1769 ads_msgfree(ads
, res
);
1770 return ADS_ERROR(LDAP_NO_MEMORY
);
1773 if (!(mods
= ads_init_mods(ctx
))) {
1774 talloc_destroy(ctx
);
1775 ads_msgfree(ads
, res
);
1776 return ADS_ERROR(LDAP_NO_MEMORY
);
1778 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1779 if (!ADS_ERR_OK(ret
)) {
1780 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1781 ads_msgfree(ads
, res
);
1782 talloc_destroy(ctx
);
1785 dn_string
= ads_get_dn(ads
, res
);
1787 talloc_destroy(ctx
);
1788 ads_msgfree(ads
, res
);
1789 return ADS_ERROR(LDAP_NO_MEMORY
);
1791 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1792 ads_memfree(ads
,dn_string
);
1793 if (!ADS_ERR_OK(ret
)) {
1794 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1796 ads_msgfree(ads
, res
);
1797 talloc_destroy(ctx
);
1801 ads_msgfree(ads
, res
);
1802 talloc_destroy(ctx
);
1807 * This adds a service principal name to an existing computer account
1808 * (found by hostname) in AD.
1809 * @param ads An initialized ADS_STRUCT
1810 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1811 * @param my_fqdn The fully qualified DNS name of the machine
1812 * @param spn A string of the service principal to add, i.e. 'host'
1813 * @return 0 upon sucess, or non-zero if a failure occurs
1816 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1817 const char *my_fqdn
, const char *spn
)
1821 LDAPMessage
*res
= NULL
;
1824 char *dn_string
= NULL
;
1825 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1827 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1828 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1829 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1831 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1832 spn
, machine_name
, ads
->config
.realm
));
1833 ads_msgfree(ads
, res
);
1834 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1837 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1838 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1839 ads_msgfree(ads
, res
);
1840 return ADS_ERROR(LDAP_NO_MEMORY
);
1843 /* add short name spn */
1845 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1846 talloc_destroy(ctx
);
1847 ads_msgfree(ads
, res
);
1848 return ADS_ERROR(LDAP_NO_MEMORY
);
1851 strlower_m(&psp1
[strlen(spn
)]);
1852 servicePrincipalName
[0] = psp1
;
1854 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1855 psp1
, machine_name
));
1858 /* add fully qualified spn */
1860 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1861 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1865 strlower_m(&psp2
[strlen(spn
)]);
1866 servicePrincipalName
[1] = psp2
;
1868 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1869 psp2
, machine_name
));
1871 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1872 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1876 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1877 if (!ADS_ERR_OK(ret
)) {
1878 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1882 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1883 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1887 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1888 ads_memfree(ads
,dn_string
);
1889 if (!ADS_ERR_OK(ret
)) {
1890 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1896 ads_msgfree(ads
, res
);
1901 * adds a machine account to the ADS server
1902 * @param ads An intialized ADS_STRUCT
1903 * @param machine_name - the NetBIOS machine name of this account.
1904 * @param account_type A number indicating the type of account to create
1905 * @param org_unit The LDAP path in which to place this account
1906 * @return 0 upon success, or non-zero otherwise
1909 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1910 const char *org_unit
)
1913 char *samAccountName
, *controlstr
;
1916 char *machine_escaped
= NULL
;
1918 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1919 "user", "computer", NULL
};
1920 LDAPMessage
*res
= NULL
;
1921 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1922 UF_DONT_EXPIRE_PASSWD
|\
1923 UF_ACCOUNTDISABLE
);
1925 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1926 return ADS_ERROR(LDAP_NO_MEMORY
);
1928 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1930 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1931 if (!machine_escaped
) {
1935 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1936 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1938 if ( !new_dn
|| !samAccountName
) {
1942 #ifndef ENCTYPE_ARCFOUR_HMAC
1943 acct_control
|= UF_USE_DES_KEY_ONLY
;
1946 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1950 if (!(mods
= ads_init_mods(ctx
))) {
1954 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1955 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1956 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1957 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1959 ret
= ads_gen_add(ads
, new_dn
, mods
);
1962 SAFE_FREE(machine_escaped
);
1963 ads_msgfree(ads
, res
);
1964 talloc_destroy(ctx
);
1970 * move a machine account to another OU on the ADS server
1971 * @param ads - An intialized ADS_STRUCT
1972 * @param machine_name - the NetBIOS machine name of this account.
1973 * @param org_unit - The LDAP path in which to place this account
1974 * @param moved - whether we moved the machine account (optional)
1975 * @return 0 upon success, or non-zero otherwise
1978 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1979 const char *org_unit
, bool *moved
)
1983 LDAPMessage
*res
= NULL
;
1984 char *filter
= NULL
;
1985 char *computer_dn
= NULL
;
1987 char *computer_rdn
= NULL
;
1988 bool need_move
= False
;
1990 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1991 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1995 /* Find pre-existing machine */
1996 rc
= ads_search(ads
, &res
, filter
, NULL
);
1997 if (!ADS_ERR_OK(rc
)) {
2001 computer_dn
= ads_get_dn(ads
, res
);
2003 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2007 parent_dn
= ads_parent_dn(computer_dn
);
2008 if (strequal(parent_dn
, org_unit
)) {
2014 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2015 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2019 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2020 org_unit
, 1, NULL
, NULL
);
2021 rc
= ADS_ERROR(ldap_status
);
2024 ads_msgfree(ads
, res
);
2026 SAFE_FREE(computer_dn
);
2027 SAFE_FREE(computer_rdn
);
2029 if (!ADS_ERR_OK(rc
)) {
2041 dump a binary result from ldap
2043 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2046 for (i
=0; values
[i
]; i
++) {
2047 printf("%s: ", field
);
2048 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2049 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2055 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2058 for (i
=0; values
[i
]; i
++) {
2063 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
2064 smb_uuid_unpack(guid
, &tmp
);
2065 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &tmp
));
2070 dump a sid result from ldap
2072 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2075 for (i
=0; values
[i
]; i
++) {
2078 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
2079 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2084 dump ntSecurityDescriptor
2086 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2088 TALLOC_CTX
*frame
= talloc_stackframe();
2089 struct security_descriptor
*psd
;
2092 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2093 values
[0]->bv_len
, &psd
);
2094 if (!NT_STATUS_IS_OK(status
)) {
2095 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2096 nt_errstr(status
)));
2102 ads_disp_sd(ads
, talloc_tos(), psd
);
2109 dump a string result from ldap
2111 static void dump_string(const char *field
, char **values
)
2114 for (i
=0; values
[i
]; i
++) {
2115 printf("%s: %s\n", field
, values
[i
]);
2120 dump a field from LDAP on stdout
2124 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2129 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2131 {"objectGUID", False
, dump_guid
},
2132 {"netbootGUID", False
, dump_guid
},
2133 {"nTSecurityDescriptor", False
, dump_sd
},
2134 {"dnsRecord", False
, dump_binary
},
2135 {"objectSid", False
, dump_sid
},
2136 {"tokenGroups", False
, dump_sid
},
2137 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2138 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2139 {"mS-DS-CreatorSID", False
, dump_sid
},
2140 {"msExchMailboxGuid", False
, dump_guid
},
2145 if (!field
) { /* must be end of an entry */
2150 for (i
=0; handlers
[i
].name
; i
++) {
2151 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
2152 if (!values
) /* first time, indicate string or not */
2153 return handlers
[i
].string
;
2154 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2158 if (!handlers
[i
].name
) {
2159 if (!values
) /* first time, indicate string conversion */
2161 dump_string(field
, (char **)values
);
2167 * Dump a result from LDAP on stdout
2168 * used for debugging
2169 * @param ads connection to ads server
2170 * @param res Results to dump
2173 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2175 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2179 * Walk through results, calling a function for each entry found.
2180 * The function receives a field name, a berval * array of values,
2181 * and a data area passed through from the start. The function is
2182 * called once with null for field and values at the end of each
2184 * @param ads connection to ads server
2185 * @param res Results to process
2186 * @param fn Function for processing each result
2187 * @param data_area user-defined area to pass to function
2189 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2190 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2195 size_t converted_size
;
2197 if (!(ctx
= talloc_init("ads_process_results")))
2200 for (msg
= ads_first_entry(ads
, res
); msg
;
2201 msg
= ads_next_entry(ads
, msg
)) {
2205 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2206 (LDAPMessage
*)msg
,&b
);
2208 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2209 (LDAPMessage
*)msg
,b
)) {
2210 struct berval
**ber_vals
;
2211 char **str_vals
, **utf8_vals
;
2215 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2218 DEBUG(0,("ads_process_results: "
2219 "pull_utf8_talloc failed: %s",
2223 string
= fn(ads
, field
, NULL
, data_area
);
2226 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2227 (LDAPMessage
*)msg
, field
);
2228 str_vals
= ads_pull_strvals(ctx
,
2229 (const char **) utf8_vals
);
2230 fn(ads
, field
, (void **) str_vals
, data_area
);
2231 ldap_value_free(utf8_vals
);
2233 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2234 (LDAPMessage
*)msg
, field
);
2235 fn(ads
, field
, (void **) ber_vals
, data_area
);
2237 ldap_value_free_len(ber_vals
);
2239 ldap_memfree(utf8_field
);
2242 talloc_free_children(ctx
);
2243 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2246 talloc_destroy(ctx
);
2250 * count how many replies are in a LDAPMessage
2251 * @param ads connection to ads server
2252 * @param res Results to count
2253 * @return number of replies
2255 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2257 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2261 * pull the first entry from a ADS result
2262 * @param ads connection to ads server
2263 * @param res Results of search
2264 * @return first entry from result
2266 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2268 return ldap_first_entry(ads
->ldap
.ld
, res
);
2272 * pull the next entry from a ADS result
2273 * @param ads connection to ads server
2274 * @param res Results of search
2275 * @return next entry from result
2277 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2279 return ldap_next_entry(ads
->ldap
.ld
, res
);
2283 * pull the first message from a ADS result
2284 * @param ads connection to ads server
2285 * @param res Results of search
2286 * @return first message from result
2288 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2290 return ldap_first_message(ads
->ldap
.ld
, res
);
2294 * pull the next message from a ADS result
2295 * @param ads connection to ads server
2296 * @param res Results of search
2297 * @return next message from result
2299 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2301 return ldap_next_message(ads
->ldap
.ld
, res
);
2305 * pull a single string from a ADS result
2306 * @param ads connection to ads server
2307 * @param mem_ctx TALLOC_CTX to use for allocating result string
2308 * @param msg Results of search
2309 * @param field Attribute to retrieve
2310 * @return Result string in talloc context
2312 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2318 size_t converted_size
;
2320 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2324 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2329 ldap_value_free(values
);
2334 * pull an array of strings from a ADS result
2335 * @param ads connection to ads server
2336 * @param mem_ctx TALLOC_CTX to use for allocating result string
2337 * @param msg Results of search
2338 * @param field Attribute to retrieve
2339 * @return Result strings in talloc context
2341 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2342 LDAPMessage
*msg
, const char *field
,
2348 size_t converted_size
;
2350 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2354 *num_values
= ldap_count_values(values
);
2356 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2358 ldap_value_free(values
);
2362 for (i
=0;i
<*num_values
;i
++) {
2363 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2366 ldap_value_free(values
);
2372 ldap_value_free(values
);
2377 * pull an array of strings from a ADS result
2378 * (handle large multivalue attributes with range retrieval)
2379 * @param ads connection to ads server
2380 * @param mem_ctx TALLOC_CTX to use for allocating result string
2381 * @param msg Results of search
2382 * @param field Attribute to retrieve
2383 * @param current_strings strings returned by a previous call to this function
2384 * @param next_attribute The next query should ask for this attribute
2385 * @param num_values How many values did we get this time?
2386 * @param more_values Are there more values to get?
2387 * @return Result strings in talloc context
2389 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2390 TALLOC_CTX
*mem_ctx
,
2391 LDAPMessage
*msg
, const char *field
,
2392 char **current_strings
,
2393 const char **next_attribute
,
2394 size_t *num_strings
,
2398 char *expected_range_attrib
, *range_attr
;
2399 BerElement
*ptr
= NULL
;
2402 size_t num_new_strings
;
2403 unsigned long int range_start
;
2404 unsigned long int range_end
;
2406 /* we might have been given the whole lot anyway */
2407 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2408 *more_strings
= False
;
2412 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2414 /* look for Range result */
2415 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2417 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2418 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2419 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2427 /* nothing here - this field is just empty */
2428 *more_strings
= False
;
2432 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2433 &range_start
, &range_end
) == 2) {
2434 *more_strings
= True
;
2436 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2437 &range_start
) == 1) {
2438 *more_strings
= False
;
2440 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2442 ldap_memfree(range_attr
);
2443 *more_strings
= False
;
2448 if ((*num_strings
) != range_start
) {
2449 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2450 " - aborting range retreival\n",
2451 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2452 ldap_memfree(range_attr
);
2453 *more_strings
= False
;
2457 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2459 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2460 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2461 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2462 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2463 (unsigned long int)num_new_strings
));
2464 ldap_memfree(range_attr
);
2465 *more_strings
= False
;
2469 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2470 *num_strings
+ num_new_strings
);
2472 if (strings
== NULL
) {
2473 ldap_memfree(range_attr
);
2474 *more_strings
= False
;
2478 if (new_strings
&& num_new_strings
) {
2479 memcpy(&strings
[*num_strings
], new_strings
,
2480 sizeof(*new_strings
) * num_new_strings
);
2483 (*num_strings
) += num_new_strings
;
2485 if (*more_strings
) {
2486 *next_attribute
= talloc_asprintf(mem_ctx
,
2491 if (!*next_attribute
) {
2492 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2493 ldap_memfree(range_attr
);
2494 *more_strings
= False
;
2499 ldap_memfree(range_attr
);
2505 * pull a single uint32 from a ADS result
2506 * @param ads connection to ads server
2507 * @param msg Results of search
2508 * @param field Attribute to retrieve
2509 * @param v Pointer to int to store result
2510 * @return boolean inidicating success
2512 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2517 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2521 ldap_value_free(values
);
2525 *v
= atoi(values
[0]);
2526 ldap_value_free(values
);
2531 * pull a single objectGUID from an ADS result
2532 * @param ads connection to ADS server
2533 * @param msg results of search
2534 * @param guid 37-byte area to receive text guid
2535 * @return boolean indicating success
2537 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2540 UUID_FLAT flat_guid
;
2542 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2547 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2548 smb_uuid_unpack(flat_guid
, guid
);
2549 ldap_value_free(values
);
2552 ldap_value_free(values
);
2559 * pull a single DOM_SID from a ADS result
2560 * @param ads connection to ads server
2561 * @param msg Results of search
2562 * @param field Attribute to retrieve
2563 * @param sid Pointer to sid to store result
2564 * @return boolean inidicating success
2566 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2569 struct berval
**values
;
2572 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2578 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2580 ldap_value_free_len(values
);
2585 * pull an array of DOM_SIDs from a ADS result
2586 * @param ads connection to ads server
2587 * @param mem_ctx TALLOC_CTX for allocating sid array
2588 * @param msg Results of search
2589 * @param field Attribute to retrieve
2590 * @param sids pointer to sid array to allocate
2591 * @return the count of SIDs pulled
2593 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2594 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2596 struct berval
**values
;
2600 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2605 for (i
=0; values
[i
]; i
++)
2609 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2611 ldap_value_free_len(values
);
2619 for (i
=0; values
[i
]; i
++) {
2620 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2622 DEBUG(10, ("pulling SID: %s\n",
2623 sid_string_dbg(&(*sids
)[count
])));
2628 ldap_value_free_len(values
);
2633 * pull a SEC_DESC from a ADS result
2634 * @param ads connection to ads server
2635 * @param mem_ctx TALLOC_CTX for allocating sid array
2636 * @param msg Results of search
2637 * @param field Attribute to retrieve
2638 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2639 * @return boolean inidicating success
2641 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2642 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2644 struct berval
**values
;
2647 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2649 if (!values
) return false;
2653 status
= unmarshall_sec_desc(mem_ctx
,
2654 (uint8
*)values
[0]->bv_val
,
2655 values
[0]->bv_len
, sd
);
2656 if (!NT_STATUS_IS_OK(status
)) {
2657 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2658 nt_errstr(status
)));
2663 ldap_value_free_len(values
);
2668 * in order to support usernames longer than 21 characters we need to
2669 * use both the sAMAccountName and the userPrincipalName attributes
2670 * It seems that not all users have the userPrincipalName attribute set
2672 * @param ads connection to ads server
2673 * @param mem_ctx TALLOC_CTX for allocating sid array
2674 * @param msg Results of search
2675 * @return the username
2677 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2683 /* lookup_name() only works on the sAMAccountName to
2684 returning the username portion of userPrincipalName
2685 breaks winbindd_getpwnam() */
2687 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2688 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2693 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2698 * find the update serial number - this is the core of the ldap cache
2699 * @param ads connection to ads server
2700 * @param ads connection to ADS server
2701 * @param usn Pointer to retrieved update serial number
2702 * @return status of search
2704 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2706 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2710 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2711 if (!ADS_ERR_OK(status
))
2714 if (ads_count_replies(ads
, res
) != 1) {
2715 ads_msgfree(ads
, res
);
2716 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2719 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2720 ads_msgfree(ads
, res
);
2721 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2724 ads_msgfree(ads
, res
);
2728 /* parse a ADS timestring - typical string is
2729 '20020917091222.0Z0' which means 09:12.22 17th September
2731 static time_t ads_parse_time(const char *str
)
2737 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2738 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2739 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2748 /********************************************************************
2749 ********************************************************************/
2751 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2753 const char *attrs
[] = {"currentTime", NULL
};
2758 ADS_STRUCT
*ads_s
= ads
;
2760 if (!(ctx
= talloc_init("ads_current_time"))) {
2761 return ADS_ERROR(LDAP_NO_MEMORY
);
2764 /* establish a new ldap tcp session if necessary */
2766 if ( !ads
->ldap
.ld
) {
2767 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2768 ads
->server
.ldap_server
)) == NULL
)
2772 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2773 status
= ads_connect( ads_s
);
2774 if ( !ADS_ERR_OK(status
))
2778 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2779 if (!ADS_ERR_OK(status
)) {
2783 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2785 ads_msgfree(ads_s
, res
);
2786 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2790 /* but save the time and offset in the original ADS_STRUCT */
2792 ads
->config
.current_time
= ads_parse_time(timestr
);
2794 if (ads
->config
.current_time
!= 0) {
2795 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2796 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2799 ads_msgfree(ads
, res
);
2801 status
= ADS_SUCCESS
;
2804 /* free any temporary ads connections */
2805 if ( ads_s
!= ads
) {
2806 ads_destroy( &ads_s
);
2808 talloc_destroy(ctx
);
2813 /********************************************************************
2814 ********************************************************************/
2816 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2818 const char *attrs
[] = {"domainFunctionality", NULL
};
2821 ADS_STRUCT
*ads_s
= ads
;
2823 *val
= DS_DOMAIN_FUNCTION_2000
;
2825 /* establish a new ldap tcp session if necessary */
2827 if ( !ads
->ldap
.ld
) {
2828 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2829 ads
->server
.ldap_server
)) == NULL
)
2831 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2834 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2835 status
= ads_connect( ads_s
);
2836 if ( !ADS_ERR_OK(status
))
2840 /* If the attribute does not exist assume it is a Windows 2000
2841 functional domain */
2843 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2844 if (!ADS_ERR_OK(status
)) {
2845 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2846 status
= ADS_SUCCESS
;
2851 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2852 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2854 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2857 ads_msgfree(ads
, res
);
2860 /* free any temporary ads connections */
2861 if ( ads_s
!= ads
) {
2862 ads_destroy( &ads_s
);
2869 * find the domain sid for our domain
2870 * @param ads connection to ads server
2871 * @param sid Pointer to domain sid
2872 * @return status of search
2874 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2876 const char *attrs
[] = {"objectSid", NULL
};
2880 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2882 if (!ADS_ERR_OK(rc
)) return rc
;
2883 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2884 ads_msgfree(ads
, res
);
2885 return ADS_ERROR_SYSTEM(ENOENT
);
2887 ads_msgfree(ads
, res
);
2893 * find our site name
2894 * @param ads connection to ads server
2895 * @param mem_ctx Pointer to talloc context
2896 * @param site_name Pointer to the sitename
2897 * @return status of search
2899 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2903 const char *dn
, *service_name
;
2904 const char *attrs
[] = { "dsServiceName", NULL
};
2906 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2907 if (!ADS_ERR_OK(status
)) {
2911 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2912 if (service_name
== NULL
) {
2913 ads_msgfree(ads
, res
);
2914 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2917 ads_msgfree(ads
, res
);
2919 /* go up three levels */
2920 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2922 return ADS_ERROR(LDAP_NO_MEMORY
);
2925 *site_name
= talloc_strdup(mem_ctx
, dn
);
2926 if (*site_name
== NULL
) {
2927 return ADS_ERROR(LDAP_NO_MEMORY
);
2932 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2937 * find the site dn where a machine resides
2938 * @param ads connection to ads server
2939 * @param mem_ctx Pointer to talloc context
2940 * @param computer_name name of the machine
2941 * @param site_name Pointer to the sitename
2942 * @return status of search
2944 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2948 const char *parent
, *filter
;
2949 char *config_context
= NULL
;
2952 /* shortcut a query */
2953 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2954 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2957 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2958 if (!ADS_ERR_OK(status
)) {
2962 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2963 if (filter
== NULL
) {
2964 return ADS_ERROR(LDAP_NO_MEMORY
);
2967 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2968 filter
, NULL
, &res
);
2969 if (!ADS_ERR_OK(status
)) {
2973 if (ads_count_replies(ads
, res
) != 1) {
2974 ads_msgfree(ads
, res
);
2975 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2978 dn
= ads_get_dn(ads
, res
);
2980 ads_msgfree(ads
, res
);
2981 return ADS_ERROR(LDAP_NO_MEMORY
);
2984 /* go up three levels */
2985 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2986 if (parent
== NULL
) {
2987 ads_msgfree(ads
, res
);
2988 ads_memfree(ads
, dn
);
2989 return ADS_ERROR(LDAP_NO_MEMORY
);
2992 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2993 if (*site_dn
== NULL
) {
2994 ads_msgfree(ads
, res
);
2995 ads_memfree(ads
, dn
);
2996 return ADS_ERROR(LDAP_NO_MEMORY
);
2999 ads_memfree(ads
, dn
);
3000 ads_msgfree(ads
, res
);
3006 * get the upn suffixes for a domain
3007 * @param ads connection to ads server
3008 * @param mem_ctx Pointer to talloc context
3009 * @param suffixes Pointer to an array of suffixes
3010 * @param num_suffixes Pointer to the number of suffixes
3011 * @return status of search
3013 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3018 char *config_context
= NULL
;
3019 const char *attrs
[] = { "uPNSuffixes", NULL
};
3021 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3022 if (!ADS_ERR_OK(status
)) {
3026 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3028 return ADS_ERROR(LDAP_NO_MEMORY
);
3031 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3032 if (!ADS_ERR_OK(status
)) {
3036 if (ads_count_replies(ads
, res
) != 1) {
3037 ads_msgfree(ads
, res
);
3038 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3041 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3042 if ((*suffixes
) == NULL
) {
3043 ads_msgfree(ads
, res
);
3044 return ADS_ERROR(LDAP_NO_MEMORY
);
3047 ads_msgfree(ads
, res
);
3053 * get the joinable ous for a domain
3054 * @param ads connection to ads server
3055 * @param mem_ctx Pointer to talloc context
3056 * @param ous Pointer to an array of ous
3057 * @param num_ous Pointer to the number of ous
3058 * @return status of search
3060 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3061 TALLOC_CTX
*mem_ctx
,
3066 LDAPMessage
*res
= NULL
;
3067 LDAPMessage
*msg
= NULL
;
3068 const char *attrs
[] = { "dn", NULL
};
3071 status
= ads_search(ads
, &res
,
3072 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3074 if (!ADS_ERR_OK(status
)) {
3078 count
= ads_count_replies(ads
, res
);
3080 ads_msgfree(ads
, res
);
3081 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3084 for (msg
= ads_first_entry(ads
, res
); msg
;
3085 msg
= ads_next_entry(ads
, msg
)) {
3089 dn
= ads_get_dn(ads
, msg
);
3091 ads_msgfree(ads
, res
);
3092 return ADS_ERROR(LDAP_NO_MEMORY
);
3095 if (!add_string_to_array(mem_ctx
, dn
,
3096 (const char ***)ous
,
3098 ads_memfree(ads
, dn
);
3099 ads_msgfree(ads
, res
);
3100 return ADS_ERROR(LDAP_NO_MEMORY
);
3103 ads_memfree(ads
, dn
);
3106 ads_msgfree(ads
, res
);
3113 * pull a DOM_SID from an extended dn string
3114 * @param mem_ctx TALLOC_CTX
3115 * @param extended_dn string
3116 * @param flags string type of extended_dn
3117 * @param sid pointer to a DOM_SID
3118 * @return NT_STATUS_OK on success,
3119 * NT_INVALID_PARAMETER on error,
3120 * NT_STATUS_NOT_FOUND if no SID present
3122 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3123 const char *extended_dn
,
3124 enum ads_extended_dn_flags flags
,
3130 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3133 /* otherwise extended_dn gets stripped off */
3134 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3135 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3138 * ADS_EXTENDED_DN_HEX_STRING:
3139 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3141 * ADS_EXTENDED_DN_STRING (only with w2k3):
3142 * <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
3144 * Object with no SID, such as an Exchange Public Folder
3145 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3148 p
= strchr(dn
, ';');
3150 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3153 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3154 DEBUG(5,("No SID present in extended dn\n"));
3155 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3158 p
+= strlen(";<SID=");
3162 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3167 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3171 case ADS_EXTENDED_DN_STRING
:
3172 if (!string_to_sid(sid
, p
)) {
3173 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3176 case ADS_EXTENDED_DN_HEX_STRING
: {
3180 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3182 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3185 if (!sid_parse(buf
, buf_len
, sid
)) {
3186 DEBUG(10,("failed to parse sid\n"));
3187 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3192 DEBUG(10,("unknown extended dn format\n"));
3193 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3196 return ADS_ERROR_NT(NT_STATUS_OK
);
3200 * pull an array of DOM_SIDs from a ADS result
3201 * @param ads connection to ads server
3202 * @param mem_ctx TALLOC_CTX for allocating sid array
3203 * @param msg Results of search
3204 * @param field Attribute to retrieve
3205 * @param flags string type of extended_dn
3206 * @param sids pointer to sid array to allocate
3207 * @return the count of SIDs pulled
3209 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
3210 TALLOC_CTX
*mem_ctx
,
3213 enum ads_extended_dn_flags flags
,
3218 size_t dn_count
, ret_count
= 0;
3221 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
3222 &dn_count
)) == NULL
) {
3226 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
3228 TALLOC_FREE(dn_strings
);
3232 for (i
=0; i
<dn_count
; i
++) {
3233 rc
= ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3234 flags
, &(*sids
)[i
]);
3235 if (!ADS_ERR_OK(rc
)) {
3236 if (NT_STATUS_EQUAL(ads_ntstatus(rc
),
3237 NT_STATUS_NOT_FOUND
)) {
3242 TALLOC_FREE(dn_strings
);
3249 TALLOC_FREE(dn_strings
);
3254 /********************************************************************
3255 ********************************************************************/
3257 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3259 LDAPMessage
*res
= NULL
;
3264 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3265 if (!ADS_ERR_OK(status
)) {
3266 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3271 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3272 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3276 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3277 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3281 ads_msgfree(ads
, res
);
3286 /********************************************************************
3287 ********************************************************************/
3289 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3291 LDAPMessage
*res
= NULL
;
3296 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3297 if (!ADS_ERR_OK(status
)) {
3298 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3303 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3304 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3308 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3309 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3313 ads_msgfree(ads
, res
);
3318 /********************************************************************
3319 ********************************************************************/
3321 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3323 LDAPMessage
*res
= NULL
;
3328 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3329 if (!ADS_ERR_OK(status
)) {
3330 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3335 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3336 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3340 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3341 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3345 ads_msgfree(ads
, res
);
3352 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3355 * Join a machine to a realm
3356 * Creates the machine account and sets the machine password
3357 * @param ads connection to ads server
3358 * @param machine name of host to add
3359 * @param org_unit Organizational unit to place machine in
3360 * @return status of join
3362 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3363 uint32 account_type
, const char *org_unit
)
3366 LDAPMessage
*res
= NULL
;
3369 /* machine name must be lowercase */
3370 machine
= SMB_STRDUP(machine_name
);
3371 strlower_m(machine
);
3374 status = ads_find_machine_acct(ads, (void **)&res, machine);
3375 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3376 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3377 status = ads_leave_realm(ads, machine);
3378 if (!ADS_ERR_OK(status)) {
3379 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3380 machine, ads->config.realm));
3385 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3386 if (!ADS_ERR_OK(status
)) {
3387 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3392 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3393 if (!ADS_ERR_OK(status
)) {
3394 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3400 ads_msgfree(ads
, res
);
3407 * Delete a machine from the realm
3408 * @param ads connection to ads server
3409 * @param hostname Machine to remove
3410 * @return status of delete
3412 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3417 char *hostnameDN
, *host
;
3419 LDAPControl ldap_control
;
3420 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3422 pldap_control
[0] = &ldap_control
;
3423 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3424 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3426 /* hostname must be lowercase */
3427 host
= SMB_STRDUP(hostname
);
3430 status
= ads_find_machine_acct(ads
, &res
, host
);
3431 if (!ADS_ERR_OK(status
)) {
3432 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3437 msg
= ads_first_entry(ads
, res
);
3440 return ADS_ERROR_SYSTEM(ENOENT
);
3443 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3445 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3447 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3449 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3452 if (rc
!= LDAP_SUCCESS
) {
3453 const char *attrs
[] = { "cn", NULL
};
3454 LDAPMessage
*msg_sub
;
3456 /* we only search with scope ONE, we do not expect any further
3457 * objects to be created deeper */
3459 status
= ads_do_search_retry(ads
, hostnameDN
,
3460 LDAP_SCOPE_ONELEVEL
,
3461 "(objectclass=*)", attrs
, &res
);
3463 if (!ADS_ERR_OK(status
)) {
3465 ads_memfree(ads
, hostnameDN
);
3469 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3470 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3474 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3476 ads_memfree(ads
, hostnameDN
);
3477 return ADS_ERROR(LDAP_NO_MEMORY
);
3480 status
= ads_del_dn(ads
, dn
);
3481 if (!ADS_ERR_OK(status
)) {
3482 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3484 ads_memfree(ads
, dn
);
3485 ads_memfree(ads
, hostnameDN
);
3489 ads_memfree(ads
, dn
);
3492 /* there should be no subordinate objects anymore */
3493 status
= ads_do_search_retry(ads
, hostnameDN
,
3494 LDAP_SCOPE_ONELEVEL
,
3495 "(objectclass=*)", attrs
, &res
);
3497 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3499 ads_memfree(ads
, hostnameDN
);
3503 /* delete hostnameDN now */
3504 status
= ads_del_dn(ads
, hostnameDN
);
3505 if (!ADS_ERR_OK(status
)) {
3507 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3508 ads_memfree(ads
, hostnameDN
);
3513 ads_memfree(ads
, hostnameDN
);
3515 status
= ads_find_machine_acct(ads
, &res
, host
);
3516 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3517 DEBUG(3, ("Failed to remove host account.\n"));
3527 * pull all token-sids from an LDAP dn
3528 * @param ads connection to ads server
3529 * @param mem_ctx TALLOC_CTX for allocating sid array
3530 * @param dn of LDAP object
3531 * @param user_sid pointer to DOM_SID (objectSid)
3532 * @param primary_group_sid pointer to DOM_SID (self composed)
3533 * @param sids pointer to sid array to allocate
3534 * @param num_sids counter of SIDs pulled
3535 * @return status of token query
3537 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3538 TALLOC_CTX
*mem_ctx
,
3541 DOM_SID
*primary_group_sid
,
3546 LDAPMessage
*res
= NULL
;
3548 size_t tmp_num_sids
;
3550 DOM_SID tmp_user_sid
;
3551 DOM_SID tmp_primary_group_sid
;
3553 const char *attrs
[] = {
3560 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3561 if (!ADS_ERR_OK(status
)) {
3565 count
= ads_count_replies(ads
, res
);
3567 ads_msgfree(ads
, res
);
3568 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3571 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3572 ads_msgfree(ads
, res
);
3573 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3576 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3577 ads_msgfree(ads
, res
);
3578 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3582 /* hack to compose the primary group sid without knowing the
3588 sid_copy(&domsid
, &tmp_user_sid
);
3590 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3591 ads_msgfree(ads
, res
);
3592 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3595 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3596 ads_msgfree(ads
, res
);
3597 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3601 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3603 if (tmp_num_sids
== 0 || !tmp_sids
) {
3604 ads_msgfree(ads
, res
);
3605 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3609 *num_sids
= tmp_num_sids
;
3617 *user_sid
= tmp_user_sid
;
3620 if (primary_group_sid
) {
3621 *primary_group_sid
= tmp_primary_group_sid
;
3624 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3626 ads_msgfree(ads
, res
);
3627 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3631 * Find a sAMAccoutName in LDAP
3632 * @param ads connection to ads server
3633 * @param mem_ctx TALLOC_CTX for allocating sid array
3634 * @param samaccountname to search
3635 * @param uac_ret uint32 pointer userAccountControl attribute value
3636 * @param dn_ret pointer to dn
3637 * @return status of token query
3639 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3640 TALLOC_CTX
*mem_ctx
,
3641 const char *samaccountname
,
3643 const char **dn_ret
)
3646 const char *attrs
[] = { "userAccountControl", NULL
};
3648 LDAPMessage
*res
= NULL
;
3652 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3654 if (filter
== NULL
) {
3655 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3659 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3661 filter
, attrs
, &res
);
3663 if (!ADS_ERR_OK(status
)) {
3667 if (ads_count_replies(ads
, res
) != 1) {
3668 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3672 dn
= ads_get_dn(ads
, res
);
3674 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3678 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3679 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3688 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3690 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3695 ads_memfree(ads
, dn
);
3696 ads_msgfree(ads
, res
);
3702 * find our configuration path
3703 * @param ads connection to ads server
3704 * @param mem_ctx Pointer to talloc context
3705 * @param config_path Pointer to the config path
3706 * @return status of search
3708 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3709 TALLOC_CTX
*mem_ctx
,
3713 LDAPMessage
*res
= NULL
;
3714 const char *config_context
= NULL
;
3715 const char *attrs
[] = { "configurationNamingContext", NULL
};
3717 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3718 "(objectclass=*)", attrs
, &res
);
3719 if (!ADS_ERR_OK(status
)) {
3723 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3724 "configurationNamingContext");
3725 ads_msgfree(ads
, res
);
3726 if (!config_context
) {
3727 return ADS_ERROR(LDAP_NO_MEMORY
);
3731 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3732 if (!*config_path
) {
3733 return ADS_ERROR(LDAP_NO_MEMORY
);
3737 return ADS_ERROR(LDAP_SUCCESS
);
3741 * find the displayName of an extended right
3742 * @param ads connection to ads server
3743 * @param config_path The config path
3744 * @param mem_ctx Pointer to talloc context
3745 * @param GUID struct of the rightsGUID
3746 * @return status of search
3748 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3749 const char *config_path
,
3750 TALLOC_CTX
*mem_ctx
,
3751 const struct GUID
*rights_guid
)
3754 LDAPMessage
*res
= NULL
;
3756 const char *attrs
[] = { "displayName", NULL
};
3757 const char *result
= NULL
;
3760 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3764 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3765 GUID_string(mem_ctx
, rights_guid
));
3770 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3775 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3777 if (!ADS_ERR_OK(rc
)) {
3781 if (ads_count_replies(ads
, res
) != 1) {
3785 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3788 ads_msgfree(ads
, res
);
3794 * verify or build and verify an account ou
3795 * @param mem_ctx Pointer to talloc context
3796 * @param ads connection to ads server
3798 * @return status of search
3801 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3803 const char **account_ou
)
3805 struct ldb_dn
*name_dn
= NULL
;
3806 const char *name
= NULL
;
3807 char *ou_string
= NULL
;
3809 name_dn
= ldb_dn_explode(mem_ctx
, *account_ou
);
3814 ou_string
= ads_ou_string(ads
, *account_ou
);
3816 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3819 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3820 ads
->config
.bind_path
);
3821 SAFE_FREE(ou_string
);
3823 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3826 name_dn
= ldb_dn_explode(mem_ctx
, name
);
3828 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3831 *account_ou
= talloc_strdup(mem_ctx
, name
);
3833 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);