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;
122
* A bug in OpenLDAP means ldap_search_ext_s can return
123
* LDAP_SUCCESS but with a NULL res pointer. Cope with
124
* this. See bug #6279 for details. JRA.
128
return LDAP_TIMELIMIT_EXCEEDED;
134
/**********************************************
135
Do client and server sitename match ?
136
**********************************************/
138
bool ads_sitename_match(ADS_STRUCT *ads)
140
if (ads->config.server_site_name == NULL &&
141
ads->config.client_site_name == NULL ) {
142
DEBUG(10,("ads_sitename_match: both null\n"));
145
if (ads->config.server_site_name &&
146
ads->config.client_site_name &&
147
strequal(ads->config.server_site_name,
148
ads->config.client_site_name)) {
149
DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
152
DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
153
ads->config.server_site_name ? ads->config.server_site_name : "NULL",
154
ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
158
/**********************************************
159
Is this the closest DC ?
160
**********************************************/
162
bool ads_closest_dc(ADS_STRUCT *ads)
164
if (ads->config.flags & NBT_SERVER_CLOSEST) {
165
DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
169
/* not sure if this can ever happen */
170
if (ads_sitename_match(ads)) {
171
DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
175
if (ads->config.client_site_name == NULL) {
176
DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
180
DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
181
ads->config.ldap_server_name));
188
try a connection to a given ldap server, returning True and setting the servers IP
189
in the ads struct if successful
191
static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
194
struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
195
TALLOC_CTX *mem_ctx = NULL;
198
if (!server || !*server) {
202
DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
203
server, ads->server.realm));
205
mem_ctx = talloc_init("ads_try_connect");
207
DEBUG(0,("out of memory\n"));
211
/* this copes with inet_ntoa brokenness */
213
srv = SMB_STRDUP(server);
215
ZERO_STRUCT( cldap_reply );
217
if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
218
DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
223
/* Check the CLDAP reply flags */
225
if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
226
DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
232
/* Fill in the ads->config values */
234
SAFE_FREE(ads->config.realm);
235
SAFE_FREE(ads->config.bind_path);
236
SAFE_FREE(ads->config.ldap_server_name);
237
SAFE_FREE(ads->config.server_site_name);
238
SAFE_FREE(ads->config.client_site_name);
239
SAFE_FREE(ads->server.workgroup);
241
ads->config.flags = cldap_reply.server_type;
242
ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
243
ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
244
strupper_m(ads->config.realm);
245
ads->config.bind_path = ads_build_dn(ads->config.realm);
246
if (*cldap_reply.server_site) {
247
ads->config.server_site_name =
248
SMB_STRDUP(cldap_reply.server_site);
250
if (*cldap_reply.client_site) {
251
ads->config.client_site_name =
252
SMB_STRDUP(cldap_reply.client_site);
254
ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
256
ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
257
if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
258
DEBUG(1,("ads_try_connect: unable to convert %s "
265
/* Store our site name. */
266
sitename_store( cldap_reply.domain, cldap_reply.client_site);
267
sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
272
TALLOC_FREE(mem_ctx);
277
/**********************************************************************
278
Try to find an AD dc using our internal name resolution routines
279
Try the realm first and then then workgroup name if netbios is not
281
**********************************************************************/
283
static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
285
const char *c_domain;
288
struct ip_service *ip_list;
291
bool got_realm = False;
292
bool use_own_domain = False;
294
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
296
/* if the realm and workgroup are both empty, assume they are ours */
299
c_realm = ads->server.realm;
301
if ( !c_realm || !*c_realm ) {
302
/* special case where no realm and no workgroup means our own */
303
if ( !ads->server.workgroup || !*ads->server.workgroup ) {
304
use_own_domain = True;
305
c_realm = lp_realm();
309
if (c_realm && *c_realm)
312
/* we need to try once with the realm name and fallback to the
313
netbios domain name if we fail (if netbios has not been disabled */
315
if ( !got_realm && !lp_disable_netbios() ) {
316
c_realm = ads->server.workgroup;
317
if (!c_realm || !*c_realm) {
318
if ( use_own_domain )
319
c_realm = lp_workgroup();
323
if ( !c_realm || !*c_realm ) {
324
DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
325
return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
328
if ( use_own_domain ) {
329
c_domain = lp_workgroup();
331
c_domain = ads->server.workgroup;
338
* In case of LDAP we use get_dc_name() as that
339
* creates the custom krb5.conf file
341
if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
343
struct sockaddr_storage ip_out;
345
DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
346
(got_realm ? "realm" : "domain"), realm));
348
if (get_dc_name(domain, realm, srv_name, &ip_out)) {
350
* we call ads_try_connect() to fill in the
351
* ads->config details
353
if (ads_try_connect(ads, srv_name, false)) {
358
return NT_STATUS_NO_LOGON_SERVERS;
361
sitename = sitename_fetch(realm);
365
DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
366
(got_realm ? "realm" : "domain"), realm));
368
status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
369
if (!NT_STATUS_IS_OK(status)) {
370
/* fall back to netbios if we can */
371
if ( got_realm && !lp_disable_netbios() ) {
380
/* if we fail this loop, then giveup since all the IP addresses returned were dead */
381
for ( i=0; i<count; i++ ) {
382
char server[INET6_ADDRSTRLEN];
384
print_sockaddr(server, sizeof(server), &ip_list[i].ss);
386
if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
390
/* realm in this case is a workgroup name. We need
391
to ignore any IP addresses in the negative connection
392
cache that match ip addresses returned in the ad realm
393
case. It sucks that I have to reproduce the logic above... */
394
c_realm = ads->server.realm;
395
if ( !c_realm || !*c_realm ) {
396
if ( !ads->server.workgroup || !*ads->server.workgroup ) {
397
c_realm = lp_realm();
400
if (c_realm && *c_realm &&
401
!NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
402
/* Ensure we add the workgroup name for this
403
IP address as negative too. */
404
add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
409
if ( ads_try_connect(ads, server, false) ) {
415
/* keep track of failures */
416
add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
421
/* In case we failed to contact one of our closest DC on our site we
422
* need to try to find another DC, retry with a site-less SRV DNS query
426
DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
427
"trying to find another DC\n", sitename));
429
namecache_delete(realm, 0x1C);
433
return NT_STATUS_NO_LOGON_SERVERS;
436
/*********************************************************************
437
*********************************************************************/
439
static NTSTATUS ads_lookup_site(void)
441
ADS_STRUCT *ads = NULL;
442
ADS_STATUS ads_status;
443
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
445
ads = ads_init(lp_realm(), NULL, NULL);
447
return NT_STATUS_NO_MEMORY;
450
/* The NO_BIND here will find a DC and set the client site
451
but not establish the TCP connection */
453
ads->auth.flags = ADS_AUTH_NO_BIND;
454
ads_status = ads_connect(ads);
455
if (!ADS_ERR_OK(ads_status)) {
456
DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
457
ads_errstr(ads_status)));
459
nt_status = ads_ntstatus(ads_status);
468
/*********************************************************************
469
*********************************************************************/
471
static const char* host_dns_domain(const char *fqdn)
473
const char *p = fqdn;
475
/* go to next char following '.' */
477
if ((p = strchr_m(fqdn, '.')) != NULL) {
486
* Connect to the Global Catalog server
487
* @param ads Pointer to an existing ADS_STRUCT
488
* @return status of connection
490
* Simple wrapper around ads_connect() that fills in the
491
* GC ldap server information
494
ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
496
TALLOC_CTX *frame = talloc_stackframe();
497
struct dns_rr_srv *gcs_list;
499
char *realm = ads->server.realm;
500
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
501
ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
504
char *sitename = NULL;
509
if ((sitename = sitename_fetch(realm)) == NULL) {
511
sitename = sitename_fetch(realm);
515
/* We try once with a sitename and once without
516
(unless we don't have a sitename and then we're
519
if (sitename == NULL)
522
nt_status = ads_dns_query_gcs(frame, realm, sitename,
523
&gcs_list, &num_gcs);
527
if (!NT_STATUS_IS_OK(nt_status)) {
528
ads_status = ADS_ERROR_NT(nt_status);
532
/* Loop until we get a successful connection or have gone
533
through them all. When connecting a GC server, make sure that
534
the realm is the server's DNS name and not the forest root */
536
for (i=0; i<num_gcs; i++) {
537
ads->server.gc = true;
538
ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
539
ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
540
ads_status = ads_connect(ads);
541
if (ADS_ERR_OK(ads_status)) {
542
/* Reset the bind_dn to "". A Global Catalog server
543
may host multiple domain trees in a forest.
544
Windows 2003 GC server will accept "" as the search
545
path to imply search all domain trees in the forest */
547
SAFE_FREE(ads->config.bind_path);
548
ads->config.bind_path = SMB_STRDUP("");
553
SAFE_FREE(ads->server.ldap_server);
554
SAFE_FREE(ads->server.realm);
557
TALLOC_FREE(gcs_list);
563
talloc_destroy(frame);
570
* Connect to the LDAP server
571
* @param ads Pointer to an existing ADS_STRUCT
572
* @return status of connection
574
ADS_STATUS ads_connect(ADS_STRUCT *ads)
576
int version = LDAP_VERSION3;
579
char addr[INET6_ADDRSTRLEN];
581
ZERO_STRUCT(ads->ldap);
582
ads->ldap.last_attempt = time(NULL);
583
ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
585
/* try with a user specified server */
587
if (DEBUGLEVEL >= 11) {
588
char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
589
DEBUG(11,("ads_connect: entering\n"));
590
DEBUGADD(11,("%s\n", s));
594
if (ads->server.ldap_server)
596
if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
600
/* The choice of which GC use is handled one level up in
601
ads_connect_gc(). If we continue on from here with
602
ads_find_dc() we will get GC searches on port 389 which
603
doesn't work. --jerry */
605
if (ads->server.gc == true) {
606
return ADS_ERROR(LDAP_OPERATIONS_ERROR);
610
ntstatus = ads_find_dc(ads);
611
if (NT_STATUS_IS_OK(ntstatus)) {
615
status = ADS_ERROR_NT(ntstatus);
620
print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
621
DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
623
if (!ads->auth.user_name) {
624
/* Must use the userPrincipalName value here or sAMAccountName
625
and not servicePrincipalName; found by Guenther Deschner */
627
if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
628
DEBUG(0,("ads_connect: asprintf fail.\n"));
629
ads->auth.user_name = NULL;
633
if (!ads->auth.realm) {
634
ads->auth.realm = SMB_STRDUP(ads->config.realm);
637
if (!ads->auth.kdc_server) {
638
print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
639
ads->auth.kdc_server = SMB_STRDUP(addr);
643
/* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
644
to MIT kerberos to work (tridge) */
647
if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
648
setenv(env, ads->auth.kdc_server, 1);
654
/* If the caller() requested no LDAP bind, then we are done */
656
if (ads->auth.flags & ADS_AUTH_NO_BIND) {
657
status = ADS_SUCCESS;
661
ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
662
if (!ads->ldap.mem_ctx) {
663
status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
667
/* Otherwise setup the TCP LDAP session */
669
ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
670
ads->ldap.port, lp_ldap_timeout());
671
if (ads->ldap.ld == NULL) {
672
status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
675
DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
677
/* cache the successful connection for workgroup and realm */
678
if (ads_closest_dc(ads)) {
679
saf_store( ads->server.workgroup, ads->config.ldap_server_name);
680
saf_store( ads->server.realm, ads->config.ldap_server_name);
683
ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
685
if ( lp_ldap_ssl_ads() ) {
686
status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
687
if (!ADS_ERR_OK(status)) {
692
/* fill in the current time and offsets */
694
status = ads_current_time( ads );
695
if ( !ADS_ERR_OK(status) ) {
699
/* Now do the bind */
701
if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
702
status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
706
if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
707
status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
711
status = ads_sasl_bind(ads);
714
if (DEBUGLEVEL >= 11) {
715
char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
716
DEBUG(11,("ads_connect: leaving with: %s\n",
717
ads_errstr(status)));
718
DEBUGADD(11,("%s\n", s));
726
* Connect to the LDAP server using given credentials
727
* @param ads Pointer to an existing ADS_STRUCT
728
* @return status of connection
730
ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
732
ads->auth.flags |= ADS_AUTH_USER_CREDS;
734
return ads_connect(ads);
738
* Disconnect the LDAP server
739
* @param ads Pointer to an existing ADS_STRUCT
741
void ads_disconnect(ADS_STRUCT *ads)
744
ldap_unbind(ads->ldap.ld);
747
if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
748
ads->ldap.wrap_ops->disconnect(ads);
750
if (ads->ldap.mem_ctx) {
751
talloc_free(ads->ldap.mem_ctx);
753
ZERO_STRUCT(ads->ldap);
757
Duplicate a struct berval into talloc'ed memory
759
static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
761
struct berval *value;
763
if (!in_val) return NULL;
765
value = TALLOC_ZERO_P(ctx, struct berval);
768
if (in_val->bv_len == 0) return value;
770
value->bv_len = in_val->bv_len;
771
value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
777
Make a values list out of an array of (struct berval *)
779
static struct berval **ads_dup_values(TALLOC_CTX *ctx,
780
const struct berval **in_vals)
782
struct berval **values;
785
if (!in_vals) return NULL;
786
for (i=0; in_vals[i]; i++)
788
values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
789
if (!values) return NULL;
791
for (i=0; in_vals[i]; i++) {
792
values[i] = dup_berval(ctx, in_vals[i]);
798
UTF8-encode a values list out of an array of (char *)
800
static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
806
if (!in_vals) return NULL;
807
for (i=0; in_vals[i]; i++)
809
values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
810
if (!values) return NULL;
812
for (i=0; in_vals[i]; i++) {
813
if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
822
Pull a (char *) array out of a UTF8-encoded values list
824
static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
828
size_t converted_size;
830
if (!in_vals) return NULL;
831
for (i=0; in_vals[i]; i++)
833
values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
834
if (!values) return NULL;
836
for (i=0; in_vals[i]; i++) {
837
if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
839
DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
840
"%s", strerror(errno)));
847
* Do a search with paged results. cookie must be null on the first
848
* call, and then returned on each subsequent call. It will be null
849
* again when the entire search is complete
850
* @param ads connection to ads server
851
* @param bind_path Base dn for the search
852
* @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
853
* @param expr Search expression - specified in local charset
854
* @param attrs Attributes to retrieve - specified in utf8 or ascii
855
* @param res ** which will contain results - free res* with ads_msgfree()
856
* @param count Number of entries retrieved on this page
857
* @param cookie The paged results cookie to be returned on subsequent calls
858
* @return status of search
860
static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
861
const char *bind_path,
862
int scope, const char *expr,
863
const char **attrs, void *args,
865
int *count, struct berval **cookie)
868
char *utf8_expr, *utf8_path, **search_attrs = NULL;
869
size_t converted_size;
870
LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
871
BerElement *cookie_be = NULL;
872
struct berval *cookie_bv= NULL;
873
BerElement *ext_be = NULL;
874
struct berval *ext_bv= NULL;
877
ads_control *external_control = (ads_control *) args;
881
if (!(ctx = talloc_init("ads_do_paged_search_args")))
882
return ADS_ERROR(LDAP_NO_MEMORY);
884
/* 0 means the conversion worked but the result was empty
885
so we only fail if it's -1. In any case, it always
886
at least nulls out the dest */
887
if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
888
!push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
894
if (!attrs || !(*attrs))
897
/* This would be the utf8-encoded version...*/
898
/* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
899
if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
905
/* Paged results only available on ldap v3 or later */
906
ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
907
if (version < LDAP_VERSION3) {
908
rc = LDAP_NOT_SUPPORTED;
912
cookie_be = ber_alloc_t(LBER_USE_DER);
914
ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
915
ber_bvfree(*cookie); /* don't need it from last time */
918
ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
920
ber_flatten(cookie_be, &cookie_bv);
921
PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
922
PagedResults.ldctl_iscritical = (char) 1;
923
PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
924
PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
926
NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
927
NoReferrals.ldctl_iscritical = (char) 0;
928
NoReferrals.ldctl_value.bv_len = 0;
929
NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
931
if (external_control &&
932
(strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
933
strequal(external_control->control, ADS_SD_FLAGS_OID))) {
935
ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
936
ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
938
/* win2k does not accept a ldctl_value beeing passed in */
940
if (external_control->val != 0) {
942
if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
947
if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
951
if ((ber_flatten(ext_be, &ext_bv)) == -1) {
956
ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
957
ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
960
ExternalCtrl.ldctl_value.bv_len = 0;
961
ExternalCtrl.ldctl_value.bv_val = NULL;
964
controls[0] = &NoReferrals;
965
controls[1] = &PagedResults;
966
controls[2] = &ExternalCtrl;
970
controls[0] = &NoReferrals;
971
controls[1] = &PagedResults;
975
/* we need to disable referrals as the openldap libs don't
976
handle them and paged results at the same time. Using them
977
together results in the result record containing the server
978
page control being removed from the result list (tridge/jmcd)
980
leaving this in despite the control that says don't generate
981
referrals, in case the server doesn't support it (jmcd)
983
ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
985
rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
986
search_attrs, 0, controls,
988
(LDAPMessage **)res);
990
ber_free(cookie_be, 1);
991
ber_bvfree(cookie_bv);
994
DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
995
ldap_err2string(rc)));
999
rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1000
NULL, &rcontrols, 0);
1006
for (i=0; rcontrols[i]; i++) {
1007
if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1008
cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1009
ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1011
/* the berval is the cookie, but must be freed when
1013
if (cookie_bv->bv_len) /* still more to do */
1014
*cookie=ber_bvdup(cookie_bv);
1017
ber_bvfree(cookie_bv);
1018
ber_free(cookie_be, 1);
1022
ldap_controls_free(rcontrols);
1025
talloc_destroy(ctx);
1028
ber_free(ext_be, 1);
1035
/* if/when we decide to utf8-encode attrs, take out this next line */
1036
TALLOC_FREE(search_attrs);
1038
return ADS_ERROR(rc);
1041
static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1042
int scope, const char *expr,
1043
const char **attrs, LDAPMessage **res,
1044
int *count, struct berval **cookie)
1046
return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1051
* Get all results for a search. This uses ads_do_paged_search() to return
1052
* all entries in a large search.
1053
* @param ads connection to ads server
1054
* @param bind_path Base dn for the search
1055
* @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1056
* @param expr Search expression
1057
* @param attrs Attributes to retrieve
1058
* @param res ** which will contain results - free res* with ads_msgfree()
1059
* @return status of search
1061
ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1062
int scope, const char *expr,
1063
const char **attrs, void *args,
1066
struct berval *cookie = NULL;
1071
status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1074
if (!ADS_ERR_OK(status))
1077
#ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1079
LDAPMessage *res2 = NULL;
1081
LDAPMessage *msg, *next;
1083
status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1084
attrs, args, &res2, &count, &cookie);
1086
if (!ADS_ERR_OK(status2)) break;
1088
/* this relies on the way that ldap_add_result_entry() works internally. I hope
1089
that this works on all ldap libs, but I have only tested with openldap */
1090
for (msg = ads_first_message(ads, res2); msg; msg = next) {
1091
next = ads_next_message(ads, msg);
1092
ldap_add_result_entry((LDAPMessage **)res, msg);
1094
/* note that we do not free res2, as the memory is now
1095
part of the main returned list */
1098
DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1099
status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1105
ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1106
int scope, const char *expr,
1107
const char **attrs, LDAPMessage **res)
1109
return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1112
ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1113
int scope, const char *expr,
1114
const char **attrs, uint32 sd_flags,
1119
args.control = ADS_SD_FLAGS_OID;
1120
args.val = sd_flags;
1121
args.critical = True;
1123
return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1128
* Run a function on all results for a search. Uses ads_do_paged_search() and
1129
* runs the function as each page is returned, using ads_process_results()
1130
* @param ads connection to ads server
1131
* @param bind_path Base dn for the search
1132
* @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1133
* @param expr Search expression - specified in local charset
1134
* @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1135
* @param fn Function which takes attr name, values list, and data_area
1136
* @param data_area Pointer which is passed to function on each call
1137
* @return status of search
1139
ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1140
int scope, const char *expr, const char **attrs,
1141
bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1144
struct berval *cookie = NULL;
1149
status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1152
if (!ADS_ERR_OK(status)) return status;
1154
ads_process_results(ads, res, fn, data_area);
1155
ads_msgfree(ads, res);
1158
status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1159
&res, &count, &cookie);
1161
if (!ADS_ERR_OK(status)) break;
1163
ads_process_results(ads, res, fn, data_area);
1164
ads_msgfree(ads, res);
1171
* Do a search with a timeout.
1172
* @param ads connection to ads server
1173
* @param bind_path Base dn for the search
1174
* @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1175
* @param expr Search expression
1176
* @param attrs Attributes to retrieve
1177
* @param res ** which will contain results - free res* with ads_msgfree()
1178
* @return status of search
1180
ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1182
const char **attrs, LDAPMessage **res)
1185
char *utf8_expr, *utf8_path, **search_attrs = NULL;
1186
size_t converted_size;
1190
if (!(ctx = talloc_init("ads_do_search"))) {
1191
DEBUG(1,("ads_do_search: talloc_init() failed!"));
1192
return ADS_ERROR(LDAP_NO_MEMORY);
1195
/* 0 means the conversion worked but the result was empty
1196
so we only fail if it's negative. In any case, it always
1197
at least nulls out the dest */
1198
if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1199
!push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1201
DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1202
rc = LDAP_NO_MEMORY;
1206
if (!attrs || !(*attrs))
1207
search_attrs = NULL;
1209
/* This would be the utf8-encoded version...*/
1210
/* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1211
if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1213
DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1214
rc = LDAP_NO_MEMORY;
1219
/* see the note in ads_do_paged_search - we *must* disable referrals */
1220
ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1222
rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1223
search_attrs, 0, NULL, NULL,
1225
(LDAPMessage **)res);
1227
if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1228
DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1233
talloc_destroy(ctx);
1234
/* if/when we decide to utf8-encode attrs, take out this next line */
1235
TALLOC_FREE(search_attrs);
1236
return ADS_ERROR(rc);
1239
* Do a general ADS search
1240
* @param ads connection to ads server
1241
* @param res ** which will contain results - free res* with ads_msgfree()
1242
* @param expr Search expression
1243
* @param attrs Attributes to retrieve
1244
* @return status of search
1246
ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1247
const char *expr, const char **attrs)
1249
return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1254
* Do a search on a specific DistinguishedName
1255
* @param ads connection to ads server
1256
* @param res ** which will contain results - free res* with ads_msgfree()
1257
* @param dn DistinguishName to search
1258
* @param attrs Attributes to retrieve
1259
* @return status of search
1261
ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1262
const char *dn, const char **attrs)
1264
return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1269
* Free up memory from a ads_search
1270
* @param ads connection to ads server
1271
* @param msg Search results to free
1273
void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1280
* Get a dn from search results
1281
* @param ads connection to ads server
1282
* @param msg Search result
1285
char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1287
char *utf8_dn, *unix_dn;
1288
size_t converted_size;
1290
utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1293
DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1297
if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1298
DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1302
ldap_memfree(utf8_dn);
1307
* Get the parent from a dn
1308
* @param dn the dn to return the parent from
1309
* @return parent dn string
1311
char *ads_parent_dn(const char *dn)
1319
p = strchr(dn, ',');
1329
* Find a machine account given a hostname
1330
* @param ads connection to ads server
1331
* @param res ** which will contain results - free res* with ads_msgfree()
1332
* @param host Hostname to search for
1333
* @return status of search
1335
ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1336
const char *machine)
1340
const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1344
/* the easiest way to find a machine account anywhere in the tree
1345
is to look for hostname$ */
1346
if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1347
DEBUG(1, ("asprintf failed!\n"));
1348
return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1351
status = ads_search(ads, res, expr, attrs);
1357
* Initialize a list of mods to be used in a modify request
1358
* @param ctx An initialized TALLOC_CTX
1359
* @return allocated ADS_MODLIST
1361
ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1363
#define ADS_MODLIST_ALLOC_SIZE 10
1366
if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1367
/* -1 is safety to make sure we don't go over the end.
1368
need to reset it to NULL before doing ldap modify */
1369
mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1371
return (ADS_MODLIST)mods;
1376
add an attribute to the list, with values list already constructed
1378
static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1379
int mod_op, const char *name,
1380
const void *_invals)
1382
const void **invals = (const void **)_invals;
1384
LDAPMod **modlist = (LDAPMod **) *mods;
1385
struct berval **ber_values = NULL;
1386
char **char_values = NULL;
1389
mod_op = LDAP_MOD_DELETE;
1391
if (mod_op & LDAP_MOD_BVALUES)
1392
ber_values = ads_dup_values(ctx,
1393
(const struct berval **)invals);
1395
char_values = ads_push_strvals(ctx,
1396
(const char **) invals);
1399
/* find the first empty slot */
1400
for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1402
if (modlist[curmod] == (LDAPMod *) -1) {
1403
if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1404
curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1405
return ADS_ERROR(LDAP_NO_MEMORY);
1406
memset(&modlist[curmod], 0,
1407
ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1408
modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1409
*mods = (ADS_MODLIST)modlist;
1412
if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1413
return ADS_ERROR(LDAP_NO_MEMORY);
1414
modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1415
if (mod_op & LDAP_MOD_BVALUES) {
1416
modlist[curmod]->mod_bvalues = ber_values;
1417
} else if (mod_op & LDAP_MOD_DELETE) {
1418
modlist[curmod]->mod_values = NULL;
1420
modlist[curmod]->mod_values = char_values;
1423
modlist[curmod]->mod_op = mod_op;
1424
return ADS_ERROR(LDAP_SUCCESS);
1428
* Add a single string value to a mod list
1429
* @param ctx An initialized TALLOC_CTX
1430
* @param mods An initialized ADS_MODLIST
1431
* @param name The attribute name to add
1432
* @param val The value to add - NULL means DELETE
1433
* @return ADS STATUS indicating success of add
1435
ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1436
const char *name, const char *val)
1438
const char *values[2];
1444
return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1445
return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1449
* Add an array of string values to a mod list
1450
* @param ctx An initialized TALLOC_CTX
1451
* @param mods An initialized ADS_MODLIST
1452
* @param name The attribute name to add
1453
* @param vals The array of string values to add - NULL means DELETE
1454
* @return ADS STATUS indicating success of add
1456
ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1457
const char *name, const char **vals)
1460
return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1461
return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1462
name, (const void **) vals);
1467
* Add a single ber-encoded value to a mod list
1468
* @param ctx An initialized TALLOC_CTX
1469
* @param mods An initialized ADS_MODLIST
1470
* @param name The attribute name to add
1471
* @param val The value to add - NULL means DELETE
1472
* @return ADS STATUS indicating success of add
1474
static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1475
const char *name, const struct berval *val)
1477
const struct berval *values[2];
1482
return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1483
return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1484
name, (const void **) values);
1489
* Perform an ldap modify
1490
* @param ads connection to ads server
1491
* @param mod_dn DistinguishedName to modify
1492
* @param mods list of modifications to perform
1493
* @return status of modify
1495
ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1498
char *utf8_dn = NULL;
1499
size_t converted_size;
1501
this control is needed to modify that contains a currently
1502
non-existent attribute (but allowable for the object) to run
1504
LDAPControl PermitModify = {
1505
CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1508
LDAPControl *controls[2];
1510
controls[0] = &PermitModify;
1513
if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1514
return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1517
/* find the end of the list, marked by NULL or -1 */
1518
for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1519
/* make sure the end of the list is NULL */
1521
ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1522
(LDAPMod **) mods, controls, NULL);
1524
return ADS_ERROR(ret);
1528
* Perform an ldap add
1529
* @param ads connection to ads server
1530
* @param new_dn DistinguishedName to add
1531
* @param mods list of attributes and values for DN
1532
* @return status of add
1534
ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1537
char *utf8_dn = NULL;
1538
size_t converted_size;
1540
if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1541
DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1542
return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1545
/* find the end of the list, marked by NULL or -1 */
1546
for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1547
/* make sure the end of the list is NULL */
1550
ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1552
return ADS_ERROR(ret);
1556
* Delete a DistinguishedName
1557
* @param ads connection to ads server
1558
* @param new_dn DistinguishedName to delete
1559
* @return status of delete
1561
ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1564
char *utf8_dn = NULL;
1565
size_t converted_size;
1566
if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1567
DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1568
return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1571
ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1573
return ADS_ERROR(ret);
1577
* Build an org unit string
1578
* if org unit is Computers or blank then assume a container, otherwise
1579
* assume a / separated list of organisational units.
1580
* jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1581
* @param ads connection to ads server
1582
* @param org_unit Organizational unit
1583
* @return org unit string - caller must free
1585
char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1589
if (!org_unit || !*org_unit) {
1591
ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1593
/* samba4 might not yet respond to a wellknownobject-query */
1594
return ret ? ret : SMB_STRDUP("cn=Computers");
1597
if (strequal(org_unit, "Computers")) {
1598
return SMB_STRDUP("cn=Computers");
1601
/* jmcd: removed "\\" from the separation chars, because it is
1602
needed as an escape for chars like '#' which are valid in an
1604
return ads_build_path(org_unit, "/", "ou=", 1);
1608
* Get a org unit string for a well-known GUID
1609
* @param ads connection to ads server
1610
* @param wknguid Well known GUID
1611
* @return org unit string - caller must free
1613
char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1616
LDAPMessage *res = NULL;
1617
char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1618
**bind_dn_exp = NULL;
1619
const char *attrs[] = {"distinguishedName", NULL};
1620
int new_ln, wkn_ln, bind_ln, i;
1622
if (wknguid == NULL) {
1626
if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1627
DEBUG(1, ("asprintf failed!\n"));
1631
status = ads_search_dn(ads, &res, base, attrs);
1632
if (!ADS_ERR_OK(status)) {
1633
DEBUG(1,("Failed while searching for: %s\n", base));
1637
if (ads_count_replies(ads, res) != 1) {
1641
/* substitute the bind-path from the well-known-guid-search result */
1642
wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1647
wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1652
bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1657
for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1659
for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1662
new_ln = wkn_ln - bind_ln;
1664
ret = SMB_STRDUP(wkn_dn_exp[0]);
1669
for (i=1; i < new_ln; i++) {
1672
if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1678
ret = SMB_STRDUP(s);
1687
ads_msgfree(ads, res);
1688
TALLOC_FREE(wkn_dn);
1690
ldap_value_free(wkn_dn_exp);
1693
ldap_value_free(bind_dn_exp);
1700
* Adds (appends) an item to an attribute array, rather then
1701
* replacing the whole list
1702
* @param ctx An initialized TALLOC_CTX
1703
* @param mods An initialized ADS_MODLIST
1704
* @param name name of the ldap attribute to append to
1705
* @param vals an array of values to add
1706
* @return status of addition
1709
ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1710
const char *name, const char **vals)
1712
return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1713
(const void *) vals);
1717
* Determines the an account's current KVNO via an LDAP lookup
1718
* @param ads An initialized ADS_STRUCT
1719
* @param account_name the NT samaccountname.
1720
* @return the kvno for the account, or -1 in case of a failure.
1723
uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1725
LDAPMessage *res = NULL;
1726
uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1728
const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1729
char *dn_string = NULL;
1730
ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1732
DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1733
if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1736
ret = ads_search(ads, &res, filter, attrs);
1738
if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1739
DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1740
ads_msgfree(ads, res);
1744
dn_string = ads_get_dn(ads, talloc_tos(), res);
1746
DEBUG(0,("ads_get_kvno: out of memory.\n"));
1747
ads_msgfree(ads, res);
1750
DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1751
TALLOC_FREE(dn_string);
1753
/* ---------------------------------------------------------
1754
* 0 is returned as a default KVNO from this point on...
1755
* This is done because Windows 2000 does not support key
1756
* version numbers. Chances are that a failure in the next
1757
* step is simply due to Windows 2000 being used for a
1758
* domain controller. */
1761
if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1762
DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1763
DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1764
ads_msgfree(ads, res);
1769
DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1770
ads_msgfree(ads, res);
1775
* Determines the computer account's current KVNO via an LDAP lookup
1776
* @param ads An initialized ADS_STRUCT
1777
* @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1778
* @return the kvno for the computer account, or -1 in case of a failure.
1781
uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1783
char *computer_account = NULL;
1786
if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1790
kvno = ads_get_kvno(ads, computer_account);
1791
free(computer_account);
1797
* This clears out all registered spn's for a given hostname
1798
* @param ads An initilaized ADS_STRUCT
1799
* @param machine_name the NetBIOS name of the computer.
1800
* @return 0 upon success, non-zero otherwise.
1803
ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1806
LDAPMessage *res = NULL;
1808
const char *servicePrincipalName[1] = {NULL};
1809
ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1810
char *dn_string = NULL;
1812
ret = ads_find_machine_acct(ads, &res, machine_name);
1813
if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1814
DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1815
DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1816
ads_msgfree(ads, res);
1817
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1820
DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1821
ctx = talloc_init("ads_clear_service_principal_names");
1823
ads_msgfree(ads, res);
1824
return ADS_ERROR(LDAP_NO_MEMORY);
1827
if (!(mods = ads_init_mods(ctx))) {
1828
talloc_destroy(ctx);
1829
ads_msgfree(ads, res);
1830
return ADS_ERROR(LDAP_NO_MEMORY);
1832
ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1833
if (!ADS_ERR_OK(ret)) {
1834
DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1835
ads_msgfree(ads, res);
1836
talloc_destroy(ctx);
1839
dn_string = ads_get_dn(ads, talloc_tos(), res);
1841
talloc_destroy(ctx);
1842
ads_msgfree(ads, res);
1843
return ADS_ERROR(LDAP_NO_MEMORY);
1845
ret = ads_gen_mod(ads, dn_string, mods);
1846
TALLOC_FREE(dn_string);
1847
if (!ADS_ERR_OK(ret)) {
1848
DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1850
ads_msgfree(ads, res);
1851
talloc_destroy(ctx);
1855
ads_msgfree(ads, res);
1856
talloc_destroy(ctx);
1861
* This adds a service principal name to an existing computer account
1862
* (found by hostname) in AD.
1863
* @param ads An initialized ADS_STRUCT
1864
* @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1865
* @param my_fqdn The fully qualified DNS name of the machine
1866
* @param spn A string of the service principal to add, i.e. 'host'
1867
* @return 0 upon sucess, or non-zero if a failure occurs
1870
ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1871
const char *my_fqdn, const char *spn)
1875
LDAPMessage *res = NULL;
1878
char *dn_string = NULL;
1879
const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1881
ret = ads_find_machine_acct(ads, &res, machine_name);
1882
if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1883
DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1885
DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1886
spn, machine_name, ads->config.realm));
1887
ads_msgfree(ads, res);
1888
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1891
DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1892
if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1893
ads_msgfree(ads, res);
1894
return ADS_ERROR(LDAP_NO_MEMORY);
1897
/* add short name spn */
1899
if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1900
talloc_destroy(ctx);
1901
ads_msgfree(ads, res);
1902
return ADS_ERROR(LDAP_NO_MEMORY);
1905
strlower_m(&psp1[strlen(spn)]);
1906
servicePrincipalName[0] = psp1;
1908
DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1909
psp1, machine_name));
1912
/* add fully qualified spn */
1914
if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1915
ret = ADS_ERROR(LDAP_NO_MEMORY);
1919
strlower_m(&psp2[strlen(spn)]);
1920
servicePrincipalName[1] = psp2;
1922
DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1923
psp2, machine_name));
1925
if ( (mods = ads_init_mods(ctx)) == NULL ) {
1926
ret = ADS_ERROR(LDAP_NO_MEMORY);
1930
ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1931
if (!ADS_ERR_OK(ret)) {
1932
DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1936
if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1937
ret = ADS_ERROR(LDAP_NO_MEMORY);
1941
ret = ads_gen_mod(ads, dn_string, mods);
1942
if (!ADS_ERR_OK(ret)) {
1943
DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1949
ads_msgfree(ads, res);
1954
* adds a machine account to the ADS server
1955
* @param ads An intialized ADS_STRUCT
1956
* @param machine_name - the NetBIOS machine name of this account.
1957
* @param account_type A number indicating the type of account to create
1958
* @param org_unit The LDAP path in which to place this account
1959
* @return 0 upon success, or non-zero otherwise
1962
ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1963
const char *org_unit)
1966
char *samAccountName, *controlstr;
1969
char *machine_escaped = NULL;
1971
const char *objectClass[] = {"top", "person", "organizationalPerson",
1972
"user", "computer", NULL};
1973
LDAPMessage *res = NULL;
1974
uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1975
UF_DONT_EXPIRE_PASSWD |\
1976
UF_ACCOUNTDISABLE );
1978
if (!(ctx = talloc_init("ads_add_machine_acct")))
1979
return ADS_ERROR(LDAP_NO_MEMORY);
1981
ret = ADS_ERROR(LDAP_NO_MEMORY);
1983
machine_escaped = escape_rdn_val_string_alloc(machine_name);
1984
if (!machine_escaped) {
1988
new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1989
samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1991
if ( !new_dn || !samAccountName ) {
1995
#ifndef ENCTYPE_ARCFOUR_HMAC
1996
acct_control |= UF_USE_DES_KEY_ONLY;
1999
if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2003
if (!(mods = ads_init_mods(ctx))) {
2007
ads_mod_str(ctx, &mods, "cn", machine_name);
2008
ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2009
ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2010
ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2012
ret = ads_gen_add(ads, new_dn, mods);
2015
SAFE_FREE(machine_escaped);
2016
ads_msgfree(ads, res);
2017
talloc_destroy(ctx);
2023
* move a machine account to another OU on the ADS server
2024
* @param ads - An intialized ADS_STRUCT
2025
* @param machine_name - the NetBIOS machine name of this account.
2026
* @param org_unit - The LDAP path in which to place this account
2027
* @param moved - whether we moved the machine account (optional)
2028
* @return 0 upon success, or non-zero otherwise
2031
ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2032
const char *org_unit, bool *moved)
2036
LDAPMessage *res = NULL;
2037
char *filter = NULL;
2038
char *computer_dn = NULL;
2040
char *computer_rdn = NULL;
2041
bool need_move = False;
2043
if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2044
rc = ADS_ERROR(LDAP_NO_MEMORY);
2048
/* Find pre-existing machine */
2049
rc = ads_search(ads, &res, filter, NULL);
2050
if (!ADS_ERR_OK(rc)) {
2054
computer_dn = ads_get_dn(ads, talloc_tos(), res);
2056
rc = ADS_ERROR(LDAP_NO_MEMORY);
2060
parent_dn = ads_parent_dn(computer_dn);
2061
if (strequal(parent_dn, org_unit)) {
2067
if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2068
rc = ADS_ERROR(LDAP_NO_MEMORY);
2072
ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2073
org_unit, 1, NULL, NULL);
2074
rc = ADS_ERROR(ldap_status);
2077
ads_msgfree(ads, res);
2079
TALLOC_FREE(computer_dn);
2080
SAFE_FREE(computer_rdn);
2082
if (!ADS_ERR_OK(rc)) {
2094
dump a binary result from ldap
2096
static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2099
for (i=0; values[i]; i++) {
2100
printf("%s: ", field);
2101
for (j=0; j<values[i]->bv_len; j++) {
2102
printf("%02X", (unsigned char)values[i]->bv_val[j]);
2108
static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2111
for (i=0; values[i]; i++) {
2116
memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2117
smb_uuid_unpack(guid, &tmp);
2118
printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2123
dump a sid result from ldap
2125
static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2128
for (i=0; values[i]; i++) {
2131
if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2134
printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2139
dump ntSecurityDescriptor
2141
static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2143
TALLOC_CTX *frame = talloc_stackframe();
2144
struct security_descriptor *psd;
2147
status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2148
values[0]->bv_len, &psd);
2149
if (!NT_STATUS_IS_OK(status)) {
2150
DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2151
nt_errstr(status)));
2157
ads_disp_sd(ads, talloc_tos(), psd);
2164
dump a string result from ldap
2166
static void dump_string(const char *field, char **values)
2169
for (i=0; values[i]; i++) {
2170
printf("%s: %s\n", field, values[i]);
2175
dump a field from LDAP on stdout
2179
static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2184
void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2186
{"objectGUID", False, dump_guid},
2187
{"netbootGUID", False, dump_guid},
2188
{"nTSecurityDescriptor", False, dump_sd},
2189
{"dnsRecord", False, dump_binary},
2190
{"objectSid", False, dump_sid},
2191
{"tokenGroups", False, dump_sid},
2192
{"tokenGroupsNoGCAcceptable", False, dump_sid},
2193
{"tokengroupsGlobalandUniversal", False, dump_sid},
2194
{"mS-DS-CreatorSID", False, dump_sid},
2195
{"msExchMailboxGuid", False, dump_guid},
2200
if (!field) { /* must be end of an entry */
2205
for (i=0; handlers[i].name; i++) {
2206
if (StrCaseCmp(handlers[i].name, field) == 0) {
2207
if (!values) /* first time, indicate string or not */
2208
return handlers[i].string;
2209
handlers[i].handler(ads, field, (struct berval **) values);
2213
if (!handlers[i].name) {
2214
if (!values) /* first time, indicate string conversion */
2216
dump_string(field, (char **)values);
2222
* Dump a result from LDAP on stdout
2223
* used for debugging
2224
* @param ads connection to ads server
2225
* @param res Results to dump
2228
void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2230
ads_process_results(ads, res, ads_dump_field, NULL);
2234
* Walk through results, calling a function for each entry found.
2235
* The function receives a field name, a berval * array of values,
2236
* and a data area passed through from the start. The function is
2237
* called once with null for field and values at the end of each
2239
* @param ads connection to ads server
2240
* @param res Results to process
2241
* @param fn Function for processing each result
2242
* @param data_area user-defined area to pass to function
2244
void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2245
bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2250
size_t converted_size;
2252
if (!(ctx = talloc_init("ads_process_results")))
2255
for (msg = ads_first_entry(ads, res); msg;
2256
msg = ads_next_entry(ads, msg)) {
2260
for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2261
(LDAPMessage *)msg,&b);
2263
utf8_field=ldap_next_attribute(ads->ldap.ld,
2264
(LDAPMessage *)msg,b)) {
2265
struct berval **ber_vals;
2266
char **str_vals, **utf8_vals;
2270
if (!pull_utf8_talloc(ctx, &field, utf8_field,
2273
DEBUG(0,("ads_process_results: "
2274
"pull_utf8_talloc failed: %s",
2278
string = fn(ads, field, NULL, data_area);
2281
utf8_vals = ldap_get_values(ads->ldap.ld,
2282
(LDAPMessage *)msg, field);
2283
str_vals = ads_pull_strvals(ctx,
2284
(const char **) utf8_vals);
2285
fn(ads, field, (void **) str_vals, data_area);
2286
ldap_value_free(utf8_vals);
2288
ber_vals = ldap_get_values_len(ads->ldap.ld,
2289
(LDAPMessage *)msg, field);
2290
fn(ads, field, (void **) ber_vals, data_area);
2292
ldap_value_free_len(ber_vals);
2294
ldap_memfree(utf8_field);
2297
talloc_free_children(ctx);
2298
fn(ads, NULL, NULL, data_area); /* completed an entry */
2301
talloc_destroy(ctx);
2305
* count how many replies are in a LDAPMessage
2306
* @param ads connection to ads server
2307
* @param res Results to count
2308
* @return number of replies
2310
int ads_count_replies(ADS_STRUCT *ads, void *res)
2312
return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2316
* pull the first entry from a ADS result
2317
* @param ads connection to ads server
2318
* @param res Results of search
2319
* @return first entry from result
2321
LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2323
return ldap_first_entry(ads->ldap.ld, res);
2327
* pull the next entry from a ADS result
2328
* @param ads connection to ads server
2329
* @param res Results of search
2330
* @return next entry from result
2332
LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2334
return ldap_next_entry(ads->ldap.ld, res);
2338
* pull the first message from a ADS result
2339
* @param ads connection to ads server
2340
* @param res Results of search
2341
* @return first message from result
2343
LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2345
return ldap_first_message(ads->ldap.ld, res);
2349
* pull the next message from a ADS result
2350
* @param ads connection to ads server
2351
* @param res Results of search
2352
* @return next message from result
2354
LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2356
return ldap_next_message(ads->ldap.ld, res);
2360
* pull a single string from a ADS result
2361
* @param ads connection to ads server
2362
* @param mem_ctx TALLOC_CTX to use for allocating result string
2363
* @param msg Results of search
2364
* @param field Attribute to retrieve
2365
* @return Result string in talloc context
2367
char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2373
size_t converted_size;
2375
values = ldap_get_values(ads->ldap.ld, msg, field);
2379
if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2384
ldap_value_free(values);
2389
* pull an array of strings from a ADS result
2390
* @param ads connection to ads server
2391
* @param mem_ctx TALLOC_CTX to use for allocating result string
2392
* @param msg Results of search
2393
* @param field Attribute to retrieve
2394
* @return Result strings in talloc context
2396
char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2397
LDAPMessage *msg, const char *field,
2403
size_t converted_size;
2405
values = ldap_get_values(ads->ldap.ld, msg, field);
2409
*num_values = ldap_count_values(values);
2411
ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2413
ldap_value_free(values);
2417
for (i=0;i<*num_values;i++) {
2418
if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2421
ldap_value_free(values);
2427
ldap_value_free(values);
2432
* pull an array of strings from a ADS result
2433
* (handle large multivalue attributes with range retrieval)
2434
* @param ads connection to ads server
2435
* @param mem_ctx TALLOC_CTX to use for allocating result string
2436
* @param msg Results of search
2437
* @param field Attribute to retrieve
2438
* @param current_strings strings returned by a previous call to this function
2439
* @param next_attribute The next query should ask for this attribute
2440
* @param num_values How many values did we get this time?
2441
* @param more_values Are there more values to get?
2442
* @return Result strings in talloc context
2444
char **ads_pull_strings_range(ADS_STRUCT *ads,
2445
TALLOC_CTX *mem_ctx,
2446
LDAPMessage *msg, const char *field,
2447
char **current_strings,
2448
const char **next_attribute,
2449
size_t *num_strings,
2453
char *expected_range_attrib, *range_attr;
2454
BerElement *ptr = NULL;
2457
size_t num_new_strings;
2458
unsigned long int range_start;
2459
unsigned long int range_end;
2461
/* we might have been given the whole lot anyway */
2462
if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2463
*more_strings = False;
2467
expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2469
/* look for Range result */
2470
for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2472
attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2473
/* we ignore the fact that this is utf8, as all attributes are ascii... */
2474
if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2482
/* nothing here - this field is just empty */
2483
*more_strings = False;
2487
if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2488
&range_start, &range_end) == 2) {
2489
*more_strings = True;
2491
if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2492
&range_start) == 1) {
2493
*more_strings = False;
2495
DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2497
ldap_memfree(range_attr);
2498
*more_strings = False;
2503
if ((*num_strings) != range_start) {
2504
DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2505
" - aborting range retreival\n",
2506
range_attr, (unsigned int)(*num_strings) + 1, range_start));
2507
ldap_memfree(range_attr);
2508
*more_strings = False;
2512
new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2514
if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2515
DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2516
"strings in this bunch, but we only got %lu - aborting range retreival\n",
2517
range_attr, (unsigned long int)range_end - range_start + 1,
2518
(unsigned long int)num_new_strings));
2519
ldap_memfree(range_attr);
2520
*more_strings = False;
2524
strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2525
*num_strings + num_new_strings);
2527
if (strings == NULL) {
2528
ldap_memfree(range_attr);
2529
*more_strings = False;
2533
if (new_strings && num_new_strings) {
2534
memcpy(&strings[*num_strings], new_strings,
2535
sizeof(*new_strings) * num_new_strings);
2538
(*num_strings) += num_new_strings;
2540
if (*more_strings) {
2541
*next_attribute = talloc_asprintf(mem_ctx,
2546
if (!*next_attribute) {
2547
DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2548
ldap_memfree(range_attr);
2549
*more_strings = False;
2554
ldap_memfree(range_attr);
2560
* pull a single uint32 from a ADS result
2561
* @param ads connection to ads server
2562
* @param msg Results of search
2563
* @param field Attribute to retrieve
2564
* @param v Pointer to int to store result
2565
* @return boolean inidicating success
2567
bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2572
values = ldap_get_values(ads->ldap.ld, msg, field);
2576
ldap_value_free(values);
2580
*v = atoi(values[0]);
2581
ldap_value_free(values);
2586
* pull a single objectGUID from an ADS result
2587
* @param ads connection to ADS server
2588
* @param msg results of search
2589
* @param guid 37-byte area to receive text guid
2590
* @return boolean indicating success
2592
bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2595
UUID_FLAT flat_guid;
2597
values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2602
memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2603
smb_uuid_unpack(flat_guid, guid);
2604
ldap_value_free(values);
2607
ldap_value_free(values);
2614
* pull a single DOM_SID from a ADS result
2615
* @param ads connection to ads server
2616
* @param msg Results of search
2617
* @param field Attribute to retrieve
2618
* @param sid Pointer to sid to store result
2619
* @return boolean inidicating success
2621
bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2624
struct berval **values;
2627
values = ldap_get_values_len(ads->ldap.ld, msg, field);
2633
ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2635
ldap_value_free_len(values);
2640
* pull an array of DOM_SIDs from a ADS result
2641
* @param ads connection to ads server
2642
* @param mem_ctx TALLOC_CTX for allocating sid array
2643
* @param msg Results of search
2644
* @param field Attribute to retrieve
2645
* @param sids pointer to sid array to allocate
2646
* @return the count of SIDs pulled
2648
int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2649
LDAPMessage *msg, const char *field, DOM_SID **sids)
2651
struct berval **values;
2655
values = ldap_get_values_len(ads->ldap.ld, msg, field);
2660
for (i=0; values[i]; i++)
2664
(*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2666
ldap_value_free_len(values);
2674
for (i=0; values[i]; i++) {
2675
ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2677
DEBUG(10, ("pulling SID: %s\n",
2678
sid_string_dbg(&(*sids)[count])));
2683
ldap_value_free_len(values);
2688
* pull a SEC_DESC from a ADS result
2689
* @param ads connection to ads server
2690
* @param mem_ctx TALLOC_CTX for allocating sid array
2691
* @param msg Results of search
2692
* @param field Attribute to retrieve
2693
* @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2694
* @return boolean inidicating success
2696
bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2697
LDAPMessage *msg, const char *field, SEC_DESC **sd)
2699
struct berval **values;
2702
values = ldap_get_values_len(ads->ldap.ld, msg, field);
2704
if (!values) return false;
2708
status = unmarshall_sec_desc(mem_ctx,
2709
(uint8 *)values[0]->bv_val,
2710
values[0]->bv_len, sd);
2711
if (!NT_STATUS_IS_OK(status)) {
2712
DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2713
nt_errstr(status)));
2718
ldap_value_free_len(values);
2723
* in order to support usernames longer than 21 characters we need to
2724
* use both the sAMAccountName and the userPrincipalName attributes
2725
* It seems that not all users have the userPrincipalName attribute set
2727
* @param ads connection to ads server
2728
* @param mem_ctx TALLOC_CTX for allocating sid array
2729
* @param msg Results of search
2730
* @return the username
2732
char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2738
/* lookup_name() only works on the sAMAccountName to
2739
returning the username portion of userPrincipalName
2740
breaks winbindd_getpwnam() */
2742
ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2743
if (ret && (p = strchr_m(ret, '@'))) {
2748
return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2753
* find the update serial number - this is the core of the ldap cache
2754
* @param ads connection to ads server
2755
* @param ads connection to ADS server
2756
* @param usn Pointer to retrieved update serial number
2757
* @return status of search
2759
ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2761
const char *attrs[] = {"highestCommittedUSN", NULL};
2765
status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2766
if (!ADS_ERR_OK(status))
2769
if (ads_count_replies(ads, res) != 1) {
2770
ads_msgfree(ads, res);
2771
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2774
if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2775
ads_msgfree(ads, res);
2776
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2779
ads_msgfree(ads, res);
2783
/* parse a ADS timestring - typical string is
2784
'20020917091222.0Z0' which means 09:12.22 17th September
2786
static time_t ads_parse_time(const char *str)
2792
if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2793
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2794
&tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2803
/********************************************************************
2804
********************************************************************/
2806
ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2808
const char *attrs[] = {"currentTime", NULL};
2813
ADS_STRUCT *ads_s = ads;
2815
if (!(ctx = talloc_init("ads_current_time"))) {
2816
return ADS_ERROR(LDAP_NO_MEMORY);
2819
/* establish a new ldap tcp session if necessary */
2821
if ( !ads->ldap.ld ) {
2822
if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2823
ads->server.ldap_server )) == NULL )
2827
ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2828
status = ads_connect( ads_s );
2829
if ( !ADS_ERR_OK(status))
2833
status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2834
if (!ADS_ERR_OK(status)) {
2838
timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2840
ads_msgfree(ads_s, res);
2841
status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2845
/* but save the time and offset in the original ADS_STRUCT */
2847
ads->config.current_time = ads_parse_time(timestr);
2849
if (ads->config.current_time != 0) {
2850
ads->auth.time_offset = ads->config.current_time - time(NULL);
2851
DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2854
ads_msgfree(ads, res);
2856
status = ADS_SUCCESS;
2859
/* free any temporary ads connections */
2860
if ( ads_s != ads ) {
2861
ads_destroy( &ads_s );
2863
talloc_destroy(ctx);
2868
/********************************************************************
2869
********************************************************************/
2871
ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2873
const char *attrs[] = {"domainFunctionality", NULL};
2876
ADS_STRUCT *ads_s = ads;
2878
*val = DS_DOMAIN_FUNCTION_2000;
2880
/* establish a new ldap tcp session if necessary */
2882
if ( !ads->ldap.ld ) {
2883
if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2884
ads->server.ldap_server )) == NULL )
2886
status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2889
ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2890
status = ads_connect( ads_s );
2891
if ( !ADS_ERR_OK(status))
2895
/* If the attribute does not exist assume it is a Windows 2000
2896
functional domain */
2898
status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2899
if (!ADS_ERR_OK(status)) {
2900
if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2901
status = ADS_SUCCESS;
2906
if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2907
DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2909
DEBUG(3,("ads_domain_func_level: %d\n", *val));
2912
ads_msgfree(ads, res);
2915
/* free any temporary ads connections */
2916
if ( ads_s != ads ) {
2917
ads_destroy( &ads_s );
2924
* find the domain sid for our domain
2925
* @param ads connection to ads server
2926
* @param sid Pointer to domain sid
2927
* @return status of search
2929
ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2931
const char *attrs[] = {"objectSid", NULL};
2935
rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2937
if (!ADS_ERR_OK(rc)) return rc;
2938
if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2939
ads_msgfree(ads, res);
2940
return ADS_ERROR_SYSTEM(ENOENT);
2942
ads_msgfree(ads, res);
2948
* find our site name
2949
* @param ads connection to ads server
2950
* @param mem_ctx Pointer to talloc context
2951
* @param site_name Pointer to the sitename
2952
* @return status of search
2954
ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2958
const char *dn, *service_name;
2959
const char *attrs[] = { "dsServiceName", NULL };
2961
status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2962
if (!ADS_ERR_OK(status)) {
2966
service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2967
if (service_name == NULL) {
2968
ads_msgfree(ads, res);
2969
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2972
ads_msgfree(ads, res);
2974
/* go up three levels */
2975
dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2977
return ADS_ERROR(LDAP_NO_MEMORY);
2980
*site_name = talloc_strdup(mem_ctx, dn);
2981
if (*site_name == NULL) {
2982
return ADS_ERROR(LDAP_NO_MEMORY);
2987
dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2992
* find the site dn where a machine resides
2993
* @param ads connection to ads server
2994
* @param mem_ctx Pointer to talloc context
2995
* @param computer_name name of the machine
2996
* @param site_name Pointer to the sitename
2997
* @return status of search
2999
ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3003
const char *parent, *filter;
3004
char *config_context = NULL;
3007
/* shortcut a query */
3008
if (strequal(computer_name, ads->config.ldap_server_name)) {
3009
return ads_site_dn(ads, mem_ctx, site_dn);
3012
status = ads_config_path(ads, mem_ctx, &config_context);
3013
if (!ADS_ERR_OK(status)) {
3017
filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3018
if (filter == NULL) {
3019
return ADS_ERROR(LDAP_NO_MEMORY);
3022
status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3023
filter, NULL, &res);
3024
if (!ADS_ERR_OK(status)) {
3028
if (ads_count_replies(ads, res) != 1) {
3029
ads_msgfree(ads, res);
3030
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3033
dn = ads_get_dn(ads, mem_ctx, res);
3035
ads_msgfree(ads, res);
3036
return ADS_ERROR(LDAP_NO_MEMORY);
3039
/* go up three levels */
3040
parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3041
if (parent == NULL) {
3042
ads_msgfree(ads, res);
3044
return ADS_ERROR(LDAP_NO_MEMORY);
3047
*site_dn = talloc_strdup(mem_ctx, parent);
3048
if (*site_dn == NULL) {
3049
ads_msgfree(ads, res);
3051
return ADS_ERROR(LDAP_NO_MEMORY);
3055
ads_msgfree(ads, res);
3061
* get the upn suffixes for a domain
3062
* @param ads connection to ads server
3063
* @param mem_ctx Pointer to talloc context
3064
* @param suffixes Pointer to an array of suffixes
3065
* @param num_suffixes Pointer to the number of suffixes
3066
* @return status of search
3068
ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3073
char *config_context = NULL;
3074
const char *attrs[] = { "uPNSuffixes", NULL };
3076
status = ads_config_path(ads, mem_ctx, &config_context);
3077
if (!ADS_ERR_OK(status)) {
3081
base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3083
return ADS_ERROR(LDAP_NO_MEMORY);
3086
status = ads_search_dn(ads, &res, base, attrs);
3087
if (!ADS_ERR_OK(status)) {
3091
if (ads_count_replies(ads, res) != 1) {
3092
ads_msgfree(ads, res);
3093
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3096
(*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3097
if ((*suffixes) == NULL) {
3098
ads_msgfree(ads, res);
3099
return ADS_ERROR(LDAP_NO_MEMORY);
3102
ads_msgfree(ads, res);
3108
* get the joinable ous for a domain
3109
* @param ads connection to ads server
3110
* @param mem_ctx Pointer to talloc context
3111
* @param ous Pointer to an array of ous
3112
* @param num_ous Pointer to the number of ous
3113
* @return status of search
3115
ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3116
TALLOC_CTX *mem_ctx,
3121
LDAPMessage *res = NULL;
3122
LDAPMessage *msg = NULL;
3123
const char *attrs[] = { "dn", NULL };
3126
status = ads_search(ads, &res,
3127
"(|(objectClass=domain)(objectclass=organizationalUnit))",
3129
if (!ADS_ERR_OK(status)) {
3133
count = ads_count_replies(ads, res);
3135
ads_msgfree(ads, res);
3136
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3139
for (msg = ads_first_entry(ads, res); msg;
3140
msg = ads_next_entry(ads, msg)) {
3144
dn = ads_get_dn(ads, talloc_tos(), msg);
3146
ads_msgfree(ads, res);
3147
return ADS_ERROR(LDAP_NO_MEMORY);
3150
if (!add_string_to_array(mem_ctx, dn,
3151
(const char ***)ous,
3154
ads_msgfree(ads, res);
3155
return ADS_ERROR(LDAP_NO_MEMORY);
3161
ads_msgfree(ads, res);
3168
* pull a DOM_SID from an extended dn string
3169
* @param mem_ctx TALLOC_CTX
3170
* @param extended_dn string
3171
* @param flags string type of extended_dn
3172
* @param sid pointer to a DOM_SID
3173
* @return NT_STATUS_OK on success,
3174
* NT_INVALID_PARAMETER on error,
3175
* NT_STATUS_NOT_FOUND if no SID present
3177
ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3178
const char *extended_dn,
3179
enum ads_extended_dn_flags flags,
3185
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3188
/* otherwise extended_dn gets stripped off */
3189
if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3190
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3193
* ADS_EXTENDED_DN_HEX_STRING:
3194
* <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3196
* ADS_EXTENDED_DN_STRING (only with w2k3):
3197
* <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
3199
* Object with no SID, such as an Exchange Public Folder
3200
* <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3203
p = strchr(dn, ';');
3205
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3208
if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3209
DEBUG(5,("No SID present in extended dn\n"));
3210
return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3213
p += strlen(";<SID=");
3217
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3222
DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3226
case ADS_EXTENDED_DN_STRING:
3227
if (!string_to_sid(sid, p)) {
3228
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3231
case ADS_EXTENDED_DN_HEX_STRING: {
3235
buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3237
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3240
if (!sid_parse(buf, buf_len, sid)) {
3241
DEBUG(10,("failed to parse sid\n"));
3242
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3247
DEBUG(10,("unknown extended dn format\n"));
3248
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3251
return ADS_ERROR_NT(NT_STATUS_OK);
3255
* pull an array of DOM_SIDs from a ADS result
3256
* @param ads connection to ads server
3257
* @param mem_ctx TALLOC_CTX for allocating sid array
3258
* @param msg Results of search
3259
* @param field Attribute to retrieve
3260
* @param flags string type of extended_dn
3261
* @param sids pointer to sid array to allocate
3262
* @return the count of SIDs pulled
3264
int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3265
TALLOC_CTX *mem_ctx,
3268
enum ads_extended_dn_flags flags,
3273
size_t dn_count, ret_count = 0;
3276
if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3277
&dn_count)) == NULL) {
3281
(*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3283
TALLOC_FREE(dn_strings);
3287
for (i=0; i<dn_count; i++) {
3288
rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3289
flags, &(*sids)[i]);
3290
if (!ADS_ERR_OK(rc)) {
3291
if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3292
NT_STATUS_NOT_FOUND)) {
3297
TALLOC_FREE(dn_strings);
3304
TALLOC_FREE(dn_strings);
3309
/********************************************************************
3310
********************************************************************/
3312
char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3314
LDAPMessage *res = NULL;
3319
status = ads_find_machine_acct(ads, &res, global_myname());
3320
if (!ADS_ERR_OK(status)) {
3321
DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3326
if ( (count = ads_count_replies(ads, res)) != 1 ) {
3327
DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3331
if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3332
DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3336
ads_msgfree(ads, res);
3341
/********************************************************************
3342
********************************************************************/
3344
char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3346
LDAPMessage *res = NULL;
3351
status = ads_find_machine_acct(ads, &res, machine_name);
3352
if (!ADS_ERR_OK(status)) {
3353
DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3358
if ( (count = ads_count_replies(ads, res)) != 1 ) {
3359
DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3363
if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3364
DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3368
ads_msgfree(ads, res);
3373
/********************************************************************
3374
********************************************************************/
3376
char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3378
LDAPMessage *res = NULL;
3383
status = ads_find_machine_acct(ads, &res, global_myname());
3384
if (!ADS_ERR_OK(status)) {
3385
DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3390
if ( (count = ads_count_replies(ads, res)) != 1 ) {
3391
DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3395
if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3396
DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3400
ads_msgfree(ads, res);
3407
SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3410
* Join a machine to a realm
3411
* Creates the machine account and sets the machine password
3412
* @param ads connection to ads server
3413
* @param machine name of host to add
3414
* @param org_unit Organizational unit to place machine in
3415
* @return status of join
3417
ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3418
uint32 account_type, const char *org_unit)
3421
LDAPMessage *res = NULL;
3424
/* machine name must be lowercase */
3425
machine = SMB_STRDUP(machine_name);
3426
strlower_m(machine);
3429
status = ads_find_machine_acct(ads, (void **)&res, machine);
3430
if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3431
DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3432
status = ads_leave_realm(ads, machine);
3433
if (!ADS_ERR_OK(status)) {
3434
DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3435
machine, ads->config.realm));
3440
status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3441
if (!ADS_ERR_OK(status)) {
3442
DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3447
status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3448
if (!ADS_ERR_OK(status)) {
3449
DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3455
ads_msgfree(ads, res);
3462
* Delete a machine from the realm
3463
* @param ads connection to ads server
3464
* @param hostname Machine to remove
3465
* @return status of delete
3467
ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3472
char *hostnameDN, *host;
3474
LDAPControl ldap_control;
3475
LDAPControl * pldap_control[2] = {NULL, NULL};
3477
pldap_control[0] = &ldap_control;
3478
memset(&ldap_control, 0, sizeof(LDAPControl));
3479
ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3481
/* hostname must be lowercase */
3482
host = SMB_STRDUP(hostname);
3485
status = ads_find_machine_acct(ads, &res, host);
3486
if (!ADS_ERR_OK(status)) {
3487
DEBUG(0, ("Host account for %s does not exist.\n", host));
3492
msg = ads_first_entry(ads, res);
3495
return ADS_ERROR_SYSTEM(ENOENT);
3498
hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3500
rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3502
DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3504
DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3507
if (rc != LDAP_SUCCESS) {
3508
const char *attrs[] = { "cn", NULL };
3509
LDAPMessage *msg_sub;
3511
/* we only search with scope ONE, we do not expect any further
3512
* objects to be created deeper */
3514
status = ads_do_search_retry(ads, hostnameDN,
3515
LDAP_SCOPE_ONELEVEL,
3516
"(objectclass=*)", attrs, &res);
3518
if (!ADS_ERR_OK(status)) {
3520
TALLOC_FREE(hostnameDN);
3524
for (msg_sub = ads_first_entry(ads, res); msg_sub;
3525
msg_sub = ads_next_entry(ads, msg_sub)) {
3529
if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3531
TALLOC_FREE(hostnameDN);
3532
return ADS_ERROR(LDAP_NO_MEMORY);
3535
status = ads_del_dn(ads, dn);
3536
if (!ADS_ERR_OK(status)) {
3537
DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3540
TALLOC_FREE(hostnameDN);
3547
/* there should be no subordinate objects anymore */
3548
status = ads_do_search_retry(ads, hostnameDN,
3549
LDAP_SCOPE_ONELEVEL,
3550
"(objectclass=*)", attrs, &res);
3552
if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3554
TALLOC_FREE(hostnameDN);
3558
/* delete hostnameDN now */
3559
status = ads_del_dn(ads, hostnameDN);
3560
if (!ADS_ERR_OK(status)) {
3562
DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3563
TALLOC_FREE(hostnameDN);
3568
TALLOC_FREE(hostnameDN);
3570
status = ads_find_machine_acct(ads, &res, host);
3571
if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3572
DEBUG(3, ("Failed to remove host account.\n"));
3582
* pull all token-sids from an LDAP dn
3583
* @param ads connection to ads server
3584
* @param mem_ctx TALLOC_CTX for allocating sid array
3585
* @param dn of LDAP object
3586
* @param user_sid pointer to DOM_SID (objectSid)
3587
* @param primary_group_sid pointer to DOM_SID (self composed)
3588
* @param sids pointer to sid array to allocate
3589
* @param num_sids counter of SIDs pulled
3590
* @return status of token query
3592
ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3593
TALLOC_CTX *mem_ctx,
3596
DOM_SID *primary_group_sid,
3601
LDAPMessage *res = NULL;
3603
size_t tmp_num_sids;
3605
DOM_SID tmp_user_sid;
3606
DOM_SID tmp_primary_group_sid;
3608
const char *attrs[] = {
3615
status = ads_search_retry_dn(ads, &res, dn, attrs);
3616
if (!ADS_ERR_OK(status)) {
3620
count = ads_count_replies(ads, res);
3622
ads_msgfree(ads, res);
3623
return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3626
if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3627
ads_msgfree(ads, res);
3628
return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3631
if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3632
ads_msgfree(ads, res);
3633
return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3637
/* hack to compose the primary group sid without knowing the
3643
sid_copy(&domsid, &tmp_user_sid);
3645
if (!sid_split_rid(&domsid, &dummy_rid)) {
3646
ads_msgfree(ads, res);
3647
return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3650
if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3651
ads_msgfree(ads, res);
3652
return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3656
tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3658
if (tmp_num_sids == 0 || !tmp_sids) {
3659
ads_msgfree(ads, res);
3660
return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3664
*num_sids = tmp_num_sids;
3672
*user_sid = tmp_user_sid;
3675
if (primary_group_sid) {
3676
*primary_group_sid = tmp_primary_group_sid;
3679
DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3681
ads_msgfree(ads, res);
3682
return ADS_ERROR_LDAP(LDAP_SUCCESS);
3686
* Find a sAMAccoutName in LDAP
3687
* @param ads connection to ads server
3688
* @param mem_ctx TALLOC_CTX for allocating sid array
3689
* @param samaccountname to search
3690
* @param uac_ret uint32 pointer userAccountControl attribute value
3691
* @param dn_ret pointer to dn
3692
* @return status of token query
3694
ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3695
TALLOC_CTX *mem_ctx,
3696
const char *samaccountname,
3698
const char **dn_ret)
3701
const char *attrs[] = { "userAccountControl", NULL };
3703
LDAPMessage *res = NULL;
3707
filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3709
if (filter == NULL) {
3710
status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3714
status = ads_do_search_all(ads, ads->config.bind_path,
3716
filter, attrs, &res);
3718
if (!ADS_ERR_OK(status)) {
3722
if (ads_count_replies(ads, res) != 1) {
3723
status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3727
dn = ads_get_dn(ads, talloc_tos(), res);
3729
status = ADS_ERROR(LDAP_NO_MEMORY);
3733
if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3734
status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3743
*dn_ret = talloc_strdup(mem_ctx, dn);
3745
status = ADS_ERROR(LDAP_NO_MEMORY);
3751
ads_msgfree(ads, res);
3757
* find our configuration path
3758
* @param ads connection to ads server
3759
* @param mem_ctx Pointer to talloc context
3760
* @param config_path Pointer to the config path
3761
* @return status of search
3763
ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3764
TALLOC_CTX *mem_ctx,
3768
LDAPMessage *res = NULL;
3769
const char *config_context = NULL;
3770
const char *attrs[] = { "configurationNamingContext", NULL };
3772
status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3773
"(objectclass=*)", attrs, &res);
3774
if (!ADS_ERR_OK(status)) {
3778
config_context = ads_pull_string(ads, mem_ctx, res,
3779
"configurationNamingContext");
3780
ads_msgfree(ads, res);
3781
if (!config_context) {
3782
return ADS_ERROR(LDAP_NO_MEMORY);
3786
*config_path = talloc_strdup(mem_ctx, config_context);
3787
if (!*config_path) {
3788
return ADS_ERROR(LDAP_NO_MEMORY);
3792
return ADS_ERROR(LDAP_SUCCESS);
3796
* find the displayName of an extended right
3797
* @param ads connection to ads server
3798
* @param config_path The config path
3799
* @param mem_ctx Pointer to talloc context
3800
* @param GUID struct of the rightsGUID
3801
* @return status of search
3803
const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3804
const char *config_path,
3805
TALLOC_CTX *mem_ctx,
3806
const struct GUID *rights_guid)
3809
LDAPMessage *res = NULL;
3811
const char *attrs[] = { "displayName", NULL };
3812
const char *result = NULL;
3815
if (!ads || !mem_ctx || !rights_guid) {
3819
expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3820
GUID_string(mem_ctx, rights_guid));
3825
path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3830
rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3832
if (!ADS_ERR_OK(rc)) {
3836
if (ads_count_replies(ads, res) != 1) {
3840
result = ads_pull_string(ads, mem_ctx, res, "displayName");
3843
ads_msgfree(ads, res);
3849
* verify or build and verify an account ou
3850
* @param mem_ctx Pointer to talloc context
3851
* @param ads connection to ads server
3853
* @return status of search
3856
ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3858
const char **account_ou)
3860
struct ldb_dn *name_dn = NULL;
3861
const char *name = NULL;
3862
char *ou_string = NULL;
3864
name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3869
ou_string = ads_ou_string(ads, *account_ou);
3871
return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3874
name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3875
ads->config.bind_path);
3876
SAFE_FREE(ou_string);
3878
return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3881
name_dn = ldb_dn_explode(mem_ctx, name);
3883
return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3886
*account_ou = talloc_strdup(mem_ctx, name);
3888
return ADS_ERROR_LDAP(LDAP_NO_MEMORY);