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 nbt_cldap_netlogon_5 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();
305 if ( !c_realm
|| !*c_realm
) {
306 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
307 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
;
396 struct nbt_cldap_netlogon_5 cldap_reply
;
398 ZERO_STRUCT(cldap_reply
);
400 ads
= ads_init(lp_realm(), NULL
, NULL
);
402 return NT_STATUS_NO_MEMORY
;
405 /* The NO_BIND here will find a DC and set the client site
406 but not establish the TCP connection */
408 ads
->auth
.flags
= ADS_AUTH_NO_BIND
;
409 ads_status
= ads_connect(ads
);
410 if (!ADS_ERR_OK(ads_status
)) {
411 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
412 ads_errstr(ads_status
)));
414 nt_status
= ads_ntstatus(ads_status
);
423 /*********************************************************************
424 *********************************************************************/
426 static const char* host_dns_domain(const char *fqdn
)
428 const char *p
= fqdn
;
430 /* go to next char following '.' */
432 if ((p
= strchr_m(fqdn
, '.')) != NULL
) {
441 * Connect to the Global Catalog server
442 * @param ads Pointer to an existing ADS_STRUCT
443 * @return status of connection
445 * Simple wrapper around ads_connect() that fills in the
446 * GC ldap server information
449 ADS_STATUS
ads_connect_gc(ADS_STRUCT
*ads
)
451 TALLOC_CTX
*frame
= talloc_stackframe();
452 struct dns_rr_srv
*gcs_list
;
454 char *realm
= ads
->server
.realm
;
455 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
456 ADS_STATUS ads_status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
459 char *sitename
= NULL
;
464 if ((sitename
= sitename_fetch(realm
)) == NULL
) {
466 sitename
= sitename_fetch(realm
);
470 /* We try once with a sitename and once without
471 (unless we don't have a sitename and then we're
474 if (sitename
== NULL
)
477 nt_status
= ads_dns_query_gcs(frame
, realm
, sitename
,
478 &gcs_list
, &num_gcs
);
482 if (!NT_STATUS_IS_OK(nt_status
)) {
483 ads_status
= ADS_ERROR_NT(nt_status
);
487 /* Loop until we get a successful connection or have gone
488 through them all. When connecting a GC server, make sure that
489 the realm is the server's DNS name and not the forest root */
491 for (i
=0; i
<num_gcs
; i
++) {
492 ads
->server
.gc
= true;
493 ads
->server
.ldap_server
= SMB_STRDUP(gcs_list
[i
].hostname
);
494 ads
->server
.realm
= SMB_STRDUP(host_dns_domain(ads
->server
.ldap_server
));
495 ads_status
= ads_connect(ads
);
496 if (ADS_ERR_OK(ads_status
)) {
497 /* Reset the bind_dn to "". A Global Catalog server
498 may host multiple domain trees in a forest.
499 Windows 2003 GC server will accept "" as the search
500 path to imply search all domain trees in the forest */
502 SAFE_FREE(ads
->config
.bind_path
);
503 ads
->config
.bind_path
= SMB_STRDUP("");
508 SAFE_FREE(ads
->server
.ldap_server
);
509 SAFE_FREE(ads
->server
.realm
);
512 TALLOC_FREE(gcs_list
);
518 talloc_destroy(frame
);
525 * Connect to the LDAP server
526 * @param ads Pointer to an existing ADS_STRUCT
527 * @return status of connection
529 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
531 int version
= LDAP_VERSION3
;
534 char addr
[INET6_ADDRSTRLEN
];
536 ZERO_STRUCT(ads
->ldap
);
537 ads
->ldap
.last_attempt
= time(NULL
);
538 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
540 /* try with a user specified server */
542 if (DEBUGLEVEL
>= 11) {
543 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
544 DEBUG(11,("ads_connect: entering\n"));
545 DEBUGADD(11,("%s\n", s
));
549 if (ads
->server
.ldap_server
&&
550 ads_try_connect(ads
, ads
->server
.ldap_server
, ads
->server
.gc
)) {
554 ntstatus
= ads_find_dc(ads
);
555 if (NT_STATUS_IS_OK(ntstatus
)) {
559 status
= ADS_ERROR_NT(ntstatus
);
564 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
565 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
567 if (!ads
->auth
.user_name
) {
568 /* Must use the userPrincipalName value here or sAMAccountName
569 and not servicePrincipalName; found by Guenther Deschner */
571 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
574 if (!ads
->auth
.realm
) {
575 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
578 if (!ads
->auth
.kdc_server
) {
579 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
580 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
584 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
585 to MIT kerberos to work (tridge) */
588 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
589 setenv(env
, ads
->auth
.kdc_server
, 1);
594 /* If the caller() requested no LDAP bind, then we are done */
596 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
597 status
= ADS_SUCCESS
;
601 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
602 if (!ads
->ldap
.mem_ctx
) {
603 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
607 /* Otherwise setup the TCP LDAP session */
609 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
610 ads
->ldap
.port
, lp_ldap_timeout());
611 if (ads
->ldap
.ld
== NULL
) {
612 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
615 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
617 /* cache the successful connection for workgroup and realm */
618 if (ads_closest_dc(ads
)) {
619 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
620 saf_store( ads
->server
.workgroup
, addr
);
621 saf_store( ads
->server
.realm
, addr
);
624 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
626 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
627 if (!ADS_ERR_OK(status
)) {
631 /* fill in the current time and offsets */
633 status
= ads_current_time( ads
);
634 if ( !ADS_ERR_OK(status
) ) {
638 /* Now do the bind */
640 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
641 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
645 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
646 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
650 status
= ads_sasl_bind(ads
);
653 if (DEBUGLEVEL
>= 11) {
654 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
655 DEBUG(11,("ads_connect: leaving with: %s\n",
656 ads_errstr(status
)));
657 DEBUGADD(11,("%s\n", s
));
665 * Connect to the LDAP server using given credentials
666 * @param ads Pointer to an existing ADS_STRUCT
667 * @return status of connection
669 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
671 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
673 return ads_connect(ads
);
677 * Disconnect the LDAP server
678 * @param ads Pointer to an existing ADS_STRUCT
680 void ads_disconnect(ADS_STRUCT
*ads
)
683 ldap_unbind(ads
->ldap
.ld
);
686 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
687 ads
->ldap
.wrap_ops
->disconnect(ads
);
689 if (ads
->ldap
.mem_ctx
) {
690 talloc_free(ads
->ldap
.mem_ctx
);
692 ZERO_STRUCT(ads
->ldap
);
696 Duplicate a struct berval into talloc'ed memory
698 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
700 struct berval
*value
;
702 if (!in_val
) return NULL
;
704 value
= TALLOC_ZERO_P(ctx
, struct berval
);
707 if (in_val
->bv_len
== 0) return value
;
709 value
->bv_len
= in_val
->bv_len
;
710 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
716 Make a values list out of an array of (struct berval *)
718 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
719 const struct berval
**in_vals
)
721 struct berval
**values
;
724 if (!in_vals
) return NULL
;
725 for (i
=0; in_vals
[i
]; i
++)
727 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
728 if (!values
) return NULL
;
730 for (i
=0; in_vals
[i
]; i
++) {
731 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
737 UTF8-encode a values list out of an array of (char *)
739 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
745 if (!in_vals
) return NULL
;
746 for (i
=0; in_vals
[i
]; i
++)
748 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
749 if (!values
) return NULL
;
751 for (i
=0; in_vals
[i
]; i
++) {
752 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
761 Pull a (char *) array out of a UTF8-encoded values list
763 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
767 size_t converted_size
;
769 if (!in_vals
) return NULL
;
770 for (i
=0; in_vals
[i
]; i
++)
772 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
773 if (!values
) return NULL
;
775 for (i
=0; in_vals
[i
]; i
++) {
776 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
778 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
779 "%s", strerror(errno
)));
786 * Do a search with paged results. cookie must be null on the first
787 * call, and then returned on each subsequent call. It will be null
788 * again when the entire search is complete
789 * @param ads connection to ads server
790 * @param bind_path Base dn for the search
791 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
792 * @param expr Search expression - specified in local charset
793 * @param attrs Attributes to retrieve - specified in utf8 or ascii
794 * @param res ** which will contain results - free res* with ads_msgfree()
795 * @param count Number of entries retrieved on this page
796 * @param cookie The paged results cookie to be returned on subsequent calls
797 * @return status of search
799 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
800 const char *bind_path
,
801 int scope
, const char *expr
,
802 const char **attrs
, void *args
,
804 int *count
, struct berval
**cookie
)
807 char *utf8_expr
, *utf8_path
, **search_attrs
;
808 size_t converted_size
;
809 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
810 BerElement
*cookie_be
= NULL
;
811 struct berval
*cookie_bv
= NULL
;
812 BerElement
*ext_be
= NULL
;
813 struct berval
*ext_bv
= NULL
;
816 ads_control
*external_control
= (ads_control
*) args
;
820 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
821 return ADS_ERROR(LDAP_NO_MEMORY
);
823 /* 0 means the conversion worked but the result was empty
824 so we only fail if it's -1. In any case, it always
825 at least nulls out the dest */
826 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
827 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
833 if (!attrs
|| !(*attrs
))
836 /* This would be the utf8-encoded version...*/
837 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
838 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
))) {
844 /* Paged results only available on ldap v3 or later */
845 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
846 if (version
< LDAP_VERSION3
) {
847 rc
= LDAP_NOT_SUPPORTED
;
851 cookie_be
= ber_alloc_t(LBER_USE_DER
);
853 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
854 ber_bvfree(*cookie
); /* don't need it from last time */
857 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
859 ber_flatten(cookie_be
, &cookie_bv
);
860 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
861 PagedResults
.ldctl_iscritical
= (char) 1;
862 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
863 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
865 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
866 NoReferrals
.ldctl_iscritical
= (char) 0;
867 NoReferrals
.ldctl_value
.bv_len
= 0;
868 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
870 if (external_control
&&
871 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
872 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
874 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
875 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
877 /* win2k does not accept a ldctl_value beeing passed in */
879 if (external_control
->val
!= 0) {
881 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
886 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
890 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
895 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
896 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
899 ExternalCtrl
.ldctl_value
.bv_len
= 0;
900 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
903 controls
[0] = &NoReferrals
;
904 controls
[1] = &PagedResults
;
905 controls
[2] = &ExternalCtrl
;
909 controls
[0] = &NoReferrals
;
910 controls
[1] = &PagedResults
;
914 /* we need to disable referrals as the openldap libs don't
915 handle them and paged results at the same time. Using them
916 together results in the result record containing the server
917 page control being removed from the result list (tridge/jmcd)
919 leaving this in despite the control that says don't generate
920 referrals, in case the server doesn't support it (jmcd)
922 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
924 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
925 search_attrs
, 0, controls
,
927 (LDAPMessage
**)res
);
929 ber_free(cookie_be
, 1);
930 ber_bvfree(cookie_bv
);
933 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
934 ldap_err2string(rc
)));
938 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
939 NULL
, &rcontrols
, 0);
945 for (i
=0; rcontrols
[i
]; i
++) {
946 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
947 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
948 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
950 /* the berval is the cookie, but must be freed when
952 if (cookie_bv
->bv_len
) /* still more to do */
953 *cookie
=ber_bvdup(cookie_bv
);
956 ber_bvfree(cookie_bv
);
957 ber_free(cookie_be
, 1);
961 ldap_controls_free(rcontrols
);
974 /* if/when we decide to utf8-encode attrs, take out this next line */
975 TALLOC_FREE(search_attrs
);
977 return ADS_ERROR(rc
);
980 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
981 int scope
, const char *expr
,
982 const char **attrs
, LDAPMessage
**res
,
983 int *count
, struct berval
**cookie
)
985 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
990 * Get all results for a search. This uses ads_do_paged_search() to return
991 * all entries in a large search.
992 * @param ads connection to ads server
993 * @param bind_path Base dn for the search
994 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
995 * @param expr Search expression
996 * @param attrs Attributes to retrieve
997 * @param res ** which will contain results - free res* with ads_msgfree()
998 * @return status of search
1000 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1001 int scope
, const char *expr
,
1002 const char **attrs
, void *args
,
1005 struct berval
*cookie
= NULL
;
1010 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1013 if (!ADS_ERR_OK(status
))
1016 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1018 LDAPMessage
*res2
= NULL
;
1020 LDAPMessage
*msg
, *next
;
1022 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1023 attrs
, args
, &res2
, &count
, &cookie
);
1025 if (!ADS_ERR_OK(status2
)) break;
1027 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1028 that this works on all ldap libs, but I have only tested with openldap */
1029 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1030 next
= ads_next_message(ads
, msg
);
1031 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1033 /* note that we do not free res2, as the memory is now
1034 part of the main returned list */
1037 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1038 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1044 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1045 int scope
, const char *expr
,
1046 const char **attrs
, LDAPMessage
**res
)
1048 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1051 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1052 int scope
, const char *expr
,
1053 const char **attrs
, uint32 sd_flags
,
1058 args
.control
= ADS_SD_FLAGS_OID
;
1059 args
.val
= sd_flags
;
1060 args
.critical
= True
;
1062 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1067 * Run a function on all results for a search. Uses ads_do_paged_search() and
1068 * runs the function as each page is returned, using ads_process_results()
1069 * @param ads connection to ads server
1070 * @param bind_path Base dn for the search
1071 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1072 * @param expr Search expression - specified in local charset
1073 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1074 * @param fn Function which takes attr name, values list, and data_area
1075 * @param data_area Pointer which is passed to function on each call
1076 * @return status of search
1078 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1079 int scope
, const char *expr
, const char **attrs
,
1080 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1083 struct berval
*cookie
= NULL
;
1088 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1091 if (!ADS_ERR_OK(status
)) return status
;
1093 ads_process_results(ads
, res
, fn
, data_area
);
1094 ads_msgfree(ads
, res
);
1097 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1098 &res
, &count
, &cookie
);
1100 if (!ADS_ERR_OK(status
)) break;
1102 ads_process_results(ads
, res
, fn
, data_area
);
1103 ads_msgfree(ads
, res
);
1110 * Do a search with a timeout.
1111 * @param ads connection to ads server
1112 * @param bind_path Base dn for the search
1113 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1114 * @param expr Search expression
1115 * @param attrs Attributes to retrieve
1116 * @param res ** which will contain results - free res* with ads_msgfree()
1117 * @return status of search
1119 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1121 const char **attrs
, LDAPMessage
**res
)
1124 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1125 size_t converted_size
;
1129 if (!(ctx
= talloc_init("ads_do_search"))) {
1130 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1131 return ADS_ERROR(LDAP_NO_MEMORY
);
1134 /* 0 means the conversion worked but the result was empty
1135 so we only fail if it's negative. In any case, it always
1136 at least nulls out the dest */
1137 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1138 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1140 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1141 rc
= LDAP_NO_MEMORY
;
1145 if (!attrs
|| !(*attrs
))
1146 search_attrs
= NULL
;
1148 /* This would be the utf8-encoded version...*/
1149 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1150 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
)))
1152 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1153 rc
= LDAP_NO_MEMORY
;
1158 /* see the note in ads_do_paged_search - we *must* disable referrals */
1159 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1161 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1162 search_attrs
, 0, NULL
, NULL
,
1164 (LDAPMessage
**)res
);
1166 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1167 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1172 talloc_destroy(ctx
);
1173 /* if/when we decide to utf8-encode attrs, take out this next line */
1174 TALLOC_FREE(search_attrs
);
1175 return ADS_ERROR(rc
);
1178 * Do a general ADS search
1179 * @param ads connection to ads server
1180 * @param res ** which will contain results - free res* with ads_msgfree()
1181 * @param expr Search expression
1182 * @param attrs Attributes to retrieve
1183 * @return status of search
1185 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1186 const char *expr
, const char **attrs
)
1188 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1193 * Do a search on a specific DistinguishedName
1194 * @param ads connection to ads server
1195 * @param res ** which will contain results - free res* with ads_msgfree()
1196 * @param dn DistinguishName to search
1197 * @param attrs Attributes to retrieve
1198 * @return status of search
1200 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1201 const char *dn
, const char **attrs
)
1203 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1208 * Free up memory from a ads_search
1209 * @param ads connection to ads server
1210 * @param msg Search results to free
1212 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1219 * Free up memory from various ads requests
1220 * @param ads connection to ads server
1221 * @param mem Area to free
1223 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1229 * Get a dn from search results
1230 * @param ads connection to ads server
1231 * @param msg Search result
1234 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1236 char *utf8_dn
, *unix_dn
;
1237 size_t converted_size
;
1239 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1242 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1246 if (!pull_utf8_allocate(&unix_dn
, utf8_dn
, &converted_size
)) {
1247 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1251 ldap_memfree(utf8_dn
);
1256 * Get the parent from a dn
1257 * @param dn the dn to return the parent from
1258 * @return parent dn string
1260 char *ads_parent_dn(const char *dn
)
1268 p
= strchr(dn
, ',');
1278 * Find a machine account given a hostname
1279 * @param ads connection to ads server
1280 * @param res ** which will contain results - free res* with ads_msgfree()
1281 * @param host Hostname to search for
1282 * @return status of search
1284 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1285 const char *machine
)
1289 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1293 /* the easiest way to find a machine account anywhere in the tree
1294 is to look for hostname$ */
1295 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1296 DEBUG(1, ("asprintf failed!\n"));
1297 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1300 status
= ads_search(ads
, res
, expr
, attrs
);
1306 * Initialize a list of mods to be used in a modify request
1307 * @param ctx An initialized TALLOC_CTX
1308 * @return allocated ADS_MODLIST
1310 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1312 #define ADS_MODLIST_ALLOC_SIZE 10
1315 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1316 /* -1 is safety to make sure we don't go over the end.
1317 need to reset it to NULL before doing ldap modify */
1318 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1320 return (ADS_MODLIST
)mods
;
1325 add an attribute to the list, with values list already constructed
1327 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1328 int mod_op
, const char *name
,
1329 const void *_invals
)
1331 const void **invals
= (const void **)_invals
;
1333 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1334 struct berval
**ber_values
= NULL
;
1335 char **char_values
= NULL
;
1338 mod_op
= LDAP_MOD_DELETE
;
1340 if (mod_op
& LDAP_MOD_BVALUES
)
1341 ber_values
= ads_dup_values(ctx
,
1342 (const struct berval
**)invals
);
1344 char_values
= ads_push_strvals(ctx
,
1345 (const char **) invals
);
1348 /* find the first empty slot */
1349 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1351 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1352 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1353 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1354 return ADS_ERROR(LDAP_NO_MEMORY
);
1355 memset(&modlist
[curmod
], 0,
1356 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1357 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1358 *mods
= (ADS_MODLIST
)modlist
;
1361 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1362 return ADS_ERROR(LDAP_NO_MEMORY
);
1363 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1364 if (mod_op
& LDAP_MOD_BVALUES
) {
1365 modlist
[curmod
]->mod_bvalues
= ber_values
;
1366 } else if (mod_op
& LDAP_MOD_DELETE
) {
1367 modlist
[curmod
]->mod_values
= NULL
;
1369 modlist
[curmod
]->mod_values
= char_values
;
1372 modlist
[curmod
]->mod_op
= mod_op
;
1373 return ADS_ERROR(LDAP_SUCCESS
);
1377 * Add a single string value to a mod list
1378 * @param ctx An initialized TALLOC_CTX
1379 * @param mods An initialized ADS_MODLIST
1380 * @param name The attribute name to add
1381 * @param val The value to add - NULL means DELETE
1382 * @return ADS STATUS indicating success of add
1384 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1385 const char *name
, const char *val
)
1387 const char *values
[2];
1393 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1394 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1398 * Add an array of string values to a mod list
1399 * @param ctx An initialized TALLOC_CTX
1400 * @param mods An initialized ADS_MODLIST
1401 * @param name The attribute name to add
1402 * @param vals The array of string values to add - NULL means DELETE
1403 * @return ADS STATUS indicating success of add
1405 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1406 const char *name
, const char **vals
)
1409 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1410 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1411 name
, (const void **) vals
);
1416 * Add a single ber-encoded value to a mod list
1417 * @param ctx An initialized TALLOC_CTX
1418 * @param mods An initialized ADS_MODLIST
1419 * @param name The attribute name to add
1420 * @param val The value to add - NULL means DELETE
1421 * @return ADS STATUS indicating success of add
1423 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1424 const char *name
, const struct berval
*val
)
1426 const struct berval
*values
[2];
1431 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1432 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1433 name
, (const void **) values
);
1438 * Perform an ldap modify
1439 * @param ads connection to ads server
1440 * @param mod_dn DistinguishedName to modify
1441 * @param mods list of modifications to perform
1442 * @return status of modify
1444 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1447 char *utf8_dn
= NULL
;
1448 size_t converted_size
;
1450 this control is needed to modify that contains a currently
1451 non-existent attribute (but allowable for the object) to run
1453 LDAPControl PermitModify
= {
1454 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1457 LDAPControl
*controls
[2];
1459 controls
[0] = &PermitModify
;
1462 if (!push_utf8_allocate(&utf8_dn
, mod_dn
, &converted_size
)) {
1463 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1466 /* find the end of the list, marked by NULL or -1 */
1467 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1468 /* make sure the end of the list is NULL */
1470 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1471 (LDAPMod
**) mods
, controls
, NULL
);
1473 return ADS_ERROR(ret
);
1477 * Perform an ldap add
1478 * @param ads connection to ads server
1479 * @param new_dn DistinguishedName to add
1480 * @param mods list of attributes and values for DN
1481 * @return status of add
1483 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1486 char *utf8_dn
= NULL
;
1487 size_t converted_size
;
1489 if (!push_utf8_allocate(&utf8_dn
, new_dn
, &converted_size
)) {
1490 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1491 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1494 /* find the end of the list, marked by NULL or -1 */
1495 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1496 /* make sure the end of the list is NULL */
1499 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1501 return ADS_ERROR(ret
);
1505 * Delete a DistinguishedName
1506 * @param ads connection to ads server
1507 * @param new_dn DistinguishedName to delete
1508 * @return status of delete
1510 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1513 char *utf8_dn
= NULL
;
1514 size_t converted_size
;
1515 if (!push_utf8_allocate(&utf8_dn
, del_dn
, &converted_size
)) {
1516 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1517 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1520 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1522 return ADS_ERROR(ret
);
1526 * Build an org unit string
1527 * if org unit is Computers or blank then assume a container, otherwise
1528 * assume a / separated list of organisational units.
1529 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1530 * @param ads connection to ads server
1531 * @param org_unit Organizational unit
1532 * @return org unit string - caller must free
1534 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1538 if (!org_unit
|| !*org_unit
) {
1540 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1542 /* samba4 might not yet respond to a wellknownobject-query */
1543 return ret
? ret
: SMB_STRDUP("cn=Computers");
1546 if (strequal(org_unit
, "Computers")) {
1547 return SMB_STRDUP("cn=Computers");
1550 /* jmcd: removed "\\" from the separation chars, because it is
1551 needed as an escape for chars like '#' which are valid in an
1553 return ads_build_path(org_unit
, "/", "ou=", 1);
1557 * Get a org unit string for a well-known GUID
1558 * @param ads connection to ads server
1559 * @param wknguid Well known GUID
1560 * @return org unit string - caller must free
1562 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1565 LDAPMessage
*res
= NULL
;
1566 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1567 **bind_dn_exp
= NULL
;
1568 const char *attrs
[] = {"distinguishedName", NULL
};
1569 int new_ln
, wkn_ln
, bind_ln
, i
;
1571 if (wknguid
== NULL
) {
1575 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1576 DEBUG(1, ("asprintf failed!\n"));
1580 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1581 if (!ADS_ERR_OK(status
)) {
1582 DEBUG(1,("Failed while searching for: %s\n", base
));
1586 if (ads_count_replies(ads
, res
) != 1) {
1590 /* substitute the bind-path from the well-known-guid-search result */
1591 wkn_dn
= ads_get_dn(ads
, res
);
1596 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1601 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1606 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1608 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1611 new_ln
= wkn_ln
- bind_ln
;
1613 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1618 for (i
=1; i
< new_ln
; i
++) {
1621 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1627 ret
= SMB_STRDUP(s
);
1636 ads_msgfree(ads
, res
);
1637 ads_memfree(ads
, wkn_dn
);
1639 ldap_value_free(wkn_dn_exp
);
1642 ldap_value_free(bind_dn_exp
);
1649 * Adds (appends) an item to an attribute array, rather then
1650 * replacing the whole list
1651 * @param ctx An initialized TALLOC_CTX
1652 * @param mods An initialized ADS_MODLIST
1653 * @param name name of the ldap attribute to append to
1654 * @param vals an array of values to add
1655 * @return status of addition
1658 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1659 const char *name
, const char **vals
)
1661 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1662 (const void *) vals
);
1666 * Determines the an account's current KVNO via an LDAP lookup
1667 * @param ads An initialized ADS_STRUCT
1668 * @param account_name the NT samaccountname.
1669 * @return the kvno for the account, or -1 in case of a failure.
1672 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1674 LDAPMessage
*res
= NULL
;
1675 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1677 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1678 char *dn_string
= NULL
;
1679 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1681 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1682 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1685 ret
= ads_search(ads
, &res
, filter
, attrs
);
1687 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1688 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1689 ads_msgfree(ads
, res
);
1693 dn_string
= ads_get_dn(ads
, res
);
1695 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1696 ads_msgfree(ads
, res
);
1699 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1700 ads_memfree(ads
, dn_string
);
1702 /* ---------------------------------------------------------
1703 * 0 is returned as a default KVNO from this point on...
1704 * This is done because Windows 2000 does not support key
1705 * version numbers. Chances are that a failure in the next
1706 * step is simply due to Windows 2000 being used for a
1707 * domain controller. */
1710 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1711 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1712 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1713 ads_msgfree(ads
, res
);
1718 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1719 ads_msgfree(ads
, res
);
1724 * Determines the computer account's current KVNO via an LDAP lookup
1725 * @param ads An initialized ADS_STRUCT
1726 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1727 * @return the kvno for the computer account, or -1 in case of a failure.
1730 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1732 char *computer_account
= NULL
;
1735 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1739 kvno
= ads_get_kvno(ads
, computer_account
);
1740 free(computer_account
);
1746 * This clears out all registered spn's for a given hostname
1747 * @param ads An initilaized ADS_STRUCT
1748 * @param machine_name the NetBIOS name of the computer.
1749 * @return 0 upon success, non-zero otherwise.
1752 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1755 LDAPMessage
*res
= NULL
;
1757 const char *servicePrincipalName
[1] = {NULL
};
1758 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1759 char *dn_string
= NULL
;
1761 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1762 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1763 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1764 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1765 ads_msgfree(ads
, res
);
1766 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1769 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1770 ctx
= talloc_init("ads_clear_service_principal_names");
1772 ads_msgfree(ads
, res
);
1773 return ADS_ERROR(LDAP_NO_MEMORY
);
1776 if (!(mods
= ads_init_mods(ctx
))) {
1777 talloc_destroy(ctx
);
1778 ads_msgfree(ads
, res
);
1779 return ADS_ERROR(LDAP_NO_MEMORY
);
1781 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1782 if (!ADS_ERR_OK(ret
)) {
1783 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1784 ads_msgfree(ads
, res
);
1785 talloc_destroy(ctx
);
1788 dn_string
= ads_get_dn(ads
, res
);
1790 talloc_destroy(ctx
);
1791 ads_msgfree(ads
, res
);
1792 return ADS_ERROR(LDAP_NO_MEMORY
);
1794 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1795 ads_memfree(ads
,dn_string
);
1796 if (!ADS_ERR_OK(ret
)) {
1797 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1799 ads_msgfree(ads
, res
);
1800 talloc_destroy(ctx
);
1804 ads_msgfree(ads
, res
);
1805 talloc_destroy(ctx
);
1810 * This adds a service principal name to an existing computer account
1811 * (found by hostname) in AD.
1812 * @param ads An initialized ADS_STRUCT
1813 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1814 * @param my_fqdn The fully qualified DNS name of the machine
1815 * @param spn A string of the service principal to add, i.e. 'host'
1816 * @return 0 upon sucess, or non-zero if a failure occurs
1819 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1820 const char *my_fqdn
, const char *spn
)
1824 LDAPMessage
*res
= NULL
;
1827 char *dn_string
= NULL
;
1828 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1830 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1831 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1832 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1834 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1835 spn
, machine_name
, ads
->config
.realm
));
1836 ads_msgfree(ads
, res
);
1837 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1840 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1841 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1842 ads_msgfree(ads
, res
);
1843 return ADS_ERROR(LDAP_NO_MEMORY
);
1846 /* add short name spn */
1848 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1849 talloc_destroy(ctx
);
1850 ads_msgfree(ads
, res
);
1851 return ADS_ERROR(LDAP_NO_MEMORY
);
1854 strlower_m(&psp1
[strlen(spn
)]);
1855 servicePrincipalName
[0] = psp1
;
1857 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1858 psp1
, machine_name
));
1861 /* add fully qualified spn */
1863 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1864 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1868 strlower_m(&psp2
[strlen(spn
)]);
1869 servicePrincipalName
[1] = psp2
;
1871 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1872 psp2
, machine_name
));
1874 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1875 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1879 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1880 if (!ADS_ERR_OK(ret
)) {
1881 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1885 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1886 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1890 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1891 ads_memfree(ads
,dn_string
);
1892 if (!ADS_ERR_OK(ret
)) {
1893 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1899 ads_msgfree(ads
, res
);
1904 * adds a machine account to the ADS server
1905 * @param ads An intialized ADS_STRUCT
1906 * @param machine_name - the NetBIOS machine name of this account.
1907 * @param account_type A number indicating the type of account to create
1908 * @param org_unit The LDAP path in which to place this account
1909 * @return 0 upon success, or non-zero otherwise
1912 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1913 const char *org_unit
)
1916 char *samAccountName
, *controlstr
;
1919 char *machine_escaped
= NULL
;
1921 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1922 "user", "computer", NULL
};
1923 LDAPMessage
*res
= NULL
;
1924 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1925 UF_DONT_EXPIRE_PASSWD
|\
1926 UF_ACCOUNTDISABLE
);
1928 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1929 return ADS_ERROR(LDAP_NO_MEMORY
);
1931 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1933 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1934 if (!machine_escaped
) {
1938 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1939 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1941 if ( !new_dn
|| !samAccountName
) {
1945 #ifndef ENCTYPE_ARCFOUR_HMAC
1946 acct_control
|= UF_USE_DES_KEY_ONLY
;
1949 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1953 if (!(mods
= ads_init_mods(ctx
))) {
1957 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1958 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1959 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1960 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1962 ret
= ads_gen_add(ads
, new_dn
, mods
);
1965 SAFE_FREE(machine_escaped
);
1966 ads_msgfree(ads
, res
);
1967 talloc_destroy(ctx
);
1973 * move a machine account to another OU on the ADS server
1974 * @param ads - An intialized ADS_STRUCT
1975 * @param machine_name - the NetBIOS machine name of this account.
1976 * @param org_unit - The LDAP path in which to place this account
1977 * @param moved - whether we moved the machine account (optional)
1978 * @return 0 upon success, or non-zero otherwise
1981 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1982 const char *org_unit
, bool *moved
)
1986 LDAPMessage
*res
= NULL
;
1987 char *filter
= NULL
;
1988 char *computer_dn
= NULL
;
1990 char *computer_rdn
= NULL
;
1991 bool need_move
= False
;
1993 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1994 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1998 /* Find pre-existing machine */
1999 rc
= ads_search(ads
, &res
, filter
, NULL
);
2000 if (!ADS_ERR_OK(rc
)) {
2004 computer_dn
= ads_get_dn(ads
, res
);
2006 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2010 parent_dn
= ads_parent_dn(computer_dn
);
2011 if (strequal(parent_dn
, org_unit
)) {
2017 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2018 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2022 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2023 org_unit
, 1, NULL
, NULL
);
2024 rc
= ADS_ERROR(ldap_status
);
2027 ads_msgfree(ads
, res
);
2029 SAFE_FREE(computer_dn
);
2030 SAFE_FREE(computer_rdn
);
2032 if (!ADS_ERR_OK(rc
)) {
2044 dump a binary result from ldap
2046 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2049 for (i
=0; values
[i
]; i
++) {
2050 printf("%s: ", field
);
2051 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2052 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2058 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2061 for (i
=0; values
[i
]; i
++) {
2066 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
2067 smb_uuid_unpack(guid
, &tmp
);
2068 printf("%s: %s\n", field
, smb_uuid_string(talloc_tos(), tmp
));
2073 dump a sid result from ldap
2075 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2078 for (i
=0; values
[i
]; i
++) {
2081 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
2082 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2087 dump ntSecurityDescriptor
2089 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2091 TALLOC_CTX
*frame
= talloc_stackframe();
2092 struct security_descriptor
*psd
;
2095 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2096 values
[0]->bv_len
, &psd
);
2097 if (!NT_STATUS_IS_OK(status
)) {
2098 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2099 nt_errstr(status
)));
2105 ads_disp_sd(ads
, talloc_tos(), psd
);
2112 dump a string result from ldap
2114 static void dump_string(const char *field
, char **values
)
2117 for (i
=0; values
[i
]; i
++) {
2118 printf("%s: %s\n", field
, values
[i
]);
2123 dump a field from LDAP on stdout
2127 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2132 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2134 {"objectGUID", False
, dump_guid
},
2135 {"netbootGUID", False
, dump_guid
},
2136 {"nTSecurityDescriptor", False
, dump_sd
},
2137 {"dnsRecord", False
, dump_binary
},
2138 {"objectSid", False
, dump_sid
},
2139 {"tokenGroups", False
, dump_sid
},
2140 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2141 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2142 {"mS-DS-CreatorSID", False
, dump_sid
},
2143 {"msExchMailboxGuid", False
, dump_guid
},
2148 if (!field
) { /* must be end of an entry */
2153 for (i
=0; handlers
[i
].name
; i
++) {
2154 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
2155 if (!values
) /* first time, indicate string or not */
2156 return handlers
[i
].string
;
2157 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2161 if (!handlers
[i
].name
) {
2162 if (!values
) /* first time, indicate string conversion */
2164 dump_string(field
, (char **)values
);
2170 * Dump a result from LDAP on stdout
2171 * used for debugging
2172 * @param ads connection to ads server
2173 * @param res Results to dump
2176 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2178 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2182 * Walk through results, calling a function for each entry found.
2183 * The function receives a field name, a berval * array of values,
2184 * and a data area passed through from the start. The function is
2185 * called once with null for field and values at the end of each
2187 * @param ads connection to ads server
2188 * @param res Results to process
2189 * @param fn Function for processing each result
2190 * @param data_area user-defined area to pass to function
2192 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2193 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2198 size_t converted_size
;
2200 if (!(ctx
= talloc_init("ads_process_results")))
2203 for (msg
= ads_first_entry(ads
, res
); msg
;
2204 msg
= ads_next_entry(ads
, msg
)) {
2208 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2209 (LDAPMessage
*)msg
,&b
);
2211 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2212 (LDAPMessage
*)msg
,b
)) {
2213 struct berval
**ber_vals
;
2214 char **str_vals
, **utf8_vals
;
2218 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2221 DEBUG(0,("ads_process_results: "
2222 "pull_utf8_talloc failed: %s",
2226 string
= fn(ads
, field
, NULL
, data_area
);
2229 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2230 (LDAPMessage
*)msg
, field
);
2231 str_vals
= ads_pull_strvals(ctx
,
2232 (const char **) utf8_vals
);
2233 fn(ads
, field
, (void **) str_vals
, data_area
);
2234 ldap_value_free(utf8_vals
);
2236 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2237 (LDAPMessage
*)msg
, field
);
2238 fn(ads
, field
, (void **) ber_vals
, data_area
);
2240 ldap_value_free_len(ber_vals
);
2242 ldap_memfree(utf8_field
);
2245 talloc_free_children(ctx
);
2246 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2249 talloc_destroy(ctx
);
2253 * count how many replies are in a LDAPMessage
2254 * @param ads connection to ads server
2255 * @param res Results to count
2256 * @return number of replies
2258 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2260 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2264 * pull the first entry from a ADS result
2265 * @param ads connection to ads server
2266 * @param res Results of search
2267 * @return first entry from result
2269 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2271 return ldap_first_entry(ads
->ldap
.ld
, res
);
2275 * pull the next entry from a ADS result
2276 * @param ads connection to ads server
2277 * @param res Results of search
2278 * @return next entry from result
2280 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2282 return ldap_next_entry(ads
->ldap
.ld
, res
);
2286 * pull the first message from a ADS result
2287 * @param ads connection to ads server
2288 * @param res Results of search
2289 * @return first message from result
2291 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2293 return ldap_first_message(ads
->ldap
.ld
, res
);
2297 * pull the next message from a ADS result
2298 * @param ads connection to ads server
2299 * @param res Results of search
2300 * @return next message from result
2302 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2304 return ldap_next_message(ads
->ldap
.ld
, res
);
2308 * pull a single string from a ADS result
2309 * @param ads connection to ads server
2310 * @param mem_ctx TALLOC_CTX to use for allocating result string
2311 * @param msg Results of search
2312 * @param field Attribute to retrieve
2313 * @return Result string in talloc context
2315 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2321 size_t converted_size
;
2323 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2327 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2332 ldap_value_free(values
);
2337 * pull an array of strings from a ADS result
2338 * @param ads connection to ads server
2339 * @param mem_ctx TALLOC_CTX to use for allocating result string
2340 * @param msg Results of search
2341 * @param field Attribute to retrieve
2342 * @return Result strings in talloc context
2344 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2345 LDAPMessage
*msg
, const char *field
,
2351 size_t converted_size
;
2353 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2357 *num_values
= ldap_count_values(values
);
2359 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2361 ldap_value_free(values
);
2365 for (i
=0;i
<*num_values
;i
++) {
2366 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2369 ldap_value_free(values
);
2375 ldap_value_free(values
);
2380 * pull an array of strings from a ADS result
2381 * (handle large multivalue attributes with range retrieval)
2382 * @param ads connection to ads server
2383 * @param mem_ctx TALLOC_CTX to use for allocating result string
2384 * @param msg Results of search
2385 * @param field Attribute to retrieve
2386 * @param current_strings strings returned by a previous call to this function
2387 * @param next_attribute The next query should ask for this attribute
2388 * @param num_values How many values did we get this time?
2389 * @param more_values Are there more values to get?
2390 * @return Result strings in talloc context
2392 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2393 TALLOC_CTX
*mem_ctx
,
2394 LDAPMessage
*msg
, const char *field
,
2395 char **current_strings
,
2396 const char **next_attribute
,
2397 size_t *num_strings
,
2401 char *expected_range_attrib
, *range_attr
;
2402 BerElement
*ptr
= NULL
;
2405 size_t num_new_strings
;
2406 unsigned long int range_start
;
2407 unsigned long int range_end
;
2409 /* we might have been given the whole lot anyway */
2410 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2411 *more_strings
= False
;
2415 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2417 /* look for Range result */
2418 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2420 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2421 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2422 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2430 /* nothing here - this field is just empty */
2431 *more_strings
= False
;
2435 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2436 &range_start
, &range_end
) == 2) {
2437 *more_strings
= True
;
2439 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2440 &range_start
) == 1) {
2441 *more_strings
= False
;
2443 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2445 ldap_memfree(range_attr
);
2446 *more_strings
= False
;
2451 if ((*num_strings
) != range_start
) {
2452 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2453 " - aborting range retreival\n",
2454 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2455 ldap_memfree(range_attr
);
2456 *more_strings
= False
;
2460 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2462 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2463 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2464 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2465 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2466 (unsigned long int)num_new_strings
));
2467 ldap_memfree(range_attr
);
2468 *more_strings
= False
;
2472 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2473 *num_strings
+ num_new_strings
);
2475 if (strings
== NULL
) {
2476 ldap_memfree(range_attr
);
2477 *more_strings
= False
;
2481 if (new_strings
&& num_new_strings
) {
2482 memcpy(&strings
[*num_strings
], new_strings
,
2483 sizeof(*new_strings
) * num_new_strings
);
2486 (*num_strings
) += num_new_strings
;
2488 if (*more_strings
) {
2489 *next_attribute
= talloc_asprintf(mem_ctx
,
2494 if (!*next_attribute
) {
2495 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2496 ldap_memfree(range_attr
);
2497 *more_strings
= False
;
2502 ldap_memfree(range_attr
);
2508 * pull a single uint32 from a ADS result
2509 * @param ads connection to ads server
2510 * @param msg Results of search
2511 * @param field Attribute to retrieve
2512 * @param v Pointer to int to store result
2513 * @return boolean inidicating success
2515 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2520 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2524 ldap_value_free(values
);
2528 *v
= atoi(values
[0]);
2529 ldap_value_free(values
);
2534 * pull a single objectGUID from an ADS result
2535 * @param ads connection to ADS server
2536 * @param msg results of search
2537 * @param guid 37-byte area to receive text guid
2538 * @return boolean indicating success
2540 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2543 UUID_FLAT flat_guid
;
2545 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2550 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2551 smb_uuid_unpack(flat_guid
, guid
);
2552 ldap_value_free(values
);
2555 ldap_value_free(values
);
2562 * pull a single DOM_SID from a ADS result
2563 * @param ads connection to ads server
2564 * @param msg Results of search
2565 * @param field Attribute to retrieve
2566 * @param sid Pointer to sid to store result
2567 * @return boolean inidicating success
2569 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2572 struct berval
**values
;
2575 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2581 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2583 ldap_value_free_len(values
);
2588 * pull an array of DOM_SIDs from a ADS result
2589 * @param ads connection to ads server
2590 * @param mem_ctx TALLOC_CTX for allocating sid array
2591 * @param msg Results of search
2592 * @param field Attribute to retrieve
2593 * @param sids pointer to sid array to allocate
2594 * @return the count of SIDs pulled
2596 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2597 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2599 struct berval
**values
;
2603 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2608 for (i
=0; values
[i
]; i
++)
2612 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2614 ldap_value_free_len(values
);
2622 for (i
=0; values
[i
]; i
++) {
2623 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2625 DEBUG(10, ("pulling SID: %s\n",
2626 sid_string_dbg(&(*sids
)[count
])));
2631 ldap_value_free_len(values
);
2636 * pull a SEC_DESC from a ADS result
2637 * @param ads connection to ads server
2638 * @param mem_ctx TALLOC_CTX for allocating sid array
2639 * @param msg Results of search
2640 * @param field Attribute to retrieve
2641 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2642 * @return boolean inidicating success
2644 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2645 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2647 struct berval
**values
;
2650 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2652 if (!values
) return false;
2656 status
= unmarshall_sec_desc(mem_ctx
,
2657 (uint8
*)values
[0]->bv_val
,
2658 values
[0]->bv_len
, sd
);
2659 if (!NT_STATUS_IS_OK(status
)) {
2660 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2661 nt_errstr(status
)));
2666 ldap_value_free_len(values
);
2671 * in order to support usernames longer than 21 characters we need to
2672 * use both the sAMAccountName and the userPrincipalName attributes
2673 * It seems that not all users have the userPrincipalName attribute set
2675 * @param ads connection to ads server
2676 * @param mem_ctx TALLOC_CTX for allocating sid array
2677 * @param msg Results of search
2678 * @return the username
2680 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2686 /* lookup_name() only works on the sAMAccountName to
2687 returning the username portion of userPrincipalName
2688 breaks winbindd_getpwnam() */
2690 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2691 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2696 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2701 * find the update serial number - this is the core of the ldap cache
2702 * @param ads connection to ads server
2703 * @param ads connection to ADS server
2704 * @param usn Pointer to retrieved update serial number
2705 * @return status of search
2707 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2709 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2713 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2714 if (!ADS_ERR_OK(status
))
2717 if (ads_count_replies(ads
, res
) != 1) {
2718 ads_msgfree(ads
, res
);
2719 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2722 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2723 ads_msgfree(ads
, res
);
2724 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2727 ads_msgfree(ads
, res
);
2731 /* parse a ADS timestring - typical string is
2732 '20020917091222.0Z0' which means 09:12.22 17th September
2734 static time_t ads_parse_time(const char *str
)
2740 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2741 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2742 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2751 /********************************************************************
2752 ********************************************************************/
2754 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2756 const char *attrs
[] = {"currentTime", NULL
};
2761 ADS_STRUCT
*ads_s
= ads
;
2763 if (!(ctx
= talloc_init("ads_current_time"))) {
2764 return ADS_ERROR(LDAP_NO_MEMORY
);
2767 /* establish a new ldap tcp session if necessary */
2769 if ( !ads
->ldap
.ld
) {
2770 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2771 ads
->server
.ldap_server
)) == NULL
)
2775 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2776 status
= ads_connect( ads_s
);
2777 if ( !ADS_ERR_OK(status
))
2781 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2782 if (!ADS_ERR_OK(status
)) {
2786 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2788 ads_msgfree(ads_s
, res
);
2789 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2793 /* but save the time and offset in the original ADS_STRUCT */
2795 ads
->config
.current_time
= ads_parse_time(timestr
);
2797 if (ads
->config
.current_time
!= 0) {
2798 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2799 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2802 ads_msgfree(ads
, res
);
2804 status
= ADS_SUCCESS
;
2807 /* free any temporary ads connections */
2808 if ( ads_s
!= ads
) {
2809 ads_destroy( &ads_s
);
2811 talloc_destroy(ctx
);
2816 /********************************************************************
2817 ********************************************************************/
2819 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2821 const char *attrs
[] = {"domainFunctionality", NULL
};
2824 ADS_STRUCT
*ads_s
= ads
;
2826 *val
= DS_DOMAIN_FUNCTION_2000
;
2828 /* establish a new ldap tcp session if necessary */
2830 if ( !ads
->ldap
.ld
) {
2831 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2832 ads
->server
.ldap_server
)) == NULL
)
2836 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2837 status
= ads_connect( ads_s
);
2838 if ( !ADS_ERR_OK(status
))
2842 /* If the attribute does not exist assume it is a Windows 2000
2843 functional domain */
2845 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2846 if (!ADS_ERR_OK(status
)) {
2847 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2848 status
= ADS_SUCCESS
;
2853 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2854 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2856 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2859 ads_msgfree(ads
, res
);
2862 /* free any temporary ads connections */
2863 if ( ads_s
!= ads
) {
2864 ads_destroy( &ads_s
);
2871 * find the domain sid for our domain
2872 * @param ads connection to ads server
2873 * @param sid Pointer to domain sid
2874 * @return status of search
2876 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2878 const char *attrs
[] = {"objectSid", NULL
};
2882 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2884 if (!ADS_ERR_OK(rc
)) return rc
;
2885 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2886 ads_msgfree(ads
, res
);
2887 return ADS_ERROR_SYSTEM(ENOENT
);
2889 ads_msgfree(ads
, res
);
2895 * find our site name
2896 * @param ads connection to ads server
2897 * @param mem_ctx Pointer to talloc context
2898 * @param site_name Pointer to the sitename
2899 * @return status of search
2901 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2905 const char *dn
, *service_name
;
2906 const char *attrs
[] = { "dsServiceName", NULL
};
2908 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2909 if (!ADS_ERR_OK(status
)) {
2913 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2914 if (service_name
== NULL
) {
2915 ads_msgfree(ads
, res
);
2916 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2919 ads_msgfree(ads
, res
);
2921 /* go up three levels */
2922 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2924 return ADS_ERROR(LDAP_NO_MEMORY
);
2927 *site_name
= talloc_strdup(mem_ctx
, dn
);
2928 if (*site_name
== NULL
) {
2929 return ADS_ERROR(LDAP_NO_MEMORY
);
2934 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2939 * find the site dn where a machine resides
2940 * @param ads connection to ads server
2941 * @param mem_ctx Pointer to talloc context
2942 * @param computer_name name of the machine
2943 * @param site_name Pointer to the sitename
2944 * @return status of search
2946 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2950 const char *parent
, *filter
;
2951 char *config_context
= NULL
;
2954 /* shortcut a query */
2955 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2956 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2959 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2960 if (!ADS_ERR_OK(status
)) {
2964 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2965 if (filter
== NULL
) {
2966 return ADS_ERROR(LDAP_NO_MEMORY
);
2969 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2970 filter
, NULL
, &res
);
2971 if (!ADS_ERR_OK(status
)) {
2975 if (ads_count_replies(ads
, res
) != 1) {
2976 ads_msgfree(ads
, res
);
2977 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2980 dn
= ads_get_dn(ads
, res
);
2982 ads_msgfree(ads
, res
);
2983 return ADS_ERROR(LDAP_NO_MEMORY
);
2986 /* go up three levels */
2987 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2988 if (parent
== NULL
) {
2989 ads_msgfree(ads
, res
);
2990 ads_memfree(ads
, dn
);
2991 return ADS_ERROR(LDAP_NO_MEMORY
);
2994 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2995 if (*site_dn
== NULL
) {
2996 ads_msgfree(ads
, res
);
2997 ads_memfree(ads
, dn
);
2998 return ADS_ERROR(LDAP_NO_MEMORY
);
3001 ads_memfree(ads
, dn
);
3002 ads_msgfree(ads
, res
);
3008 * get the upn suffixes for a domain
3009 * @param ads connection to ads server
3010 * @param mem_ctx Pointer to talloc context
3011 * @param suffixes Pointer to an array of suffixes
3012 * @param num_suffixes Pointer to the number of suffixes
3013 * @return status of search
3015 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3020 char *config_context
= NULL
;
3021 const char *attrs
[] = { "uPNSuffixes", NULL
};
3023 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3024 if (!ADS_ERR_OK(status
)) {
3028 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3030 return ADS_ERROR(LDAP_NO_MEMORY
);
3033 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3034 if (!ADS_ERR_OK(status
)) {
3038 if (ads_count_replies(ads
, res
) != 1) {
3039 ads_msgfree(ads
, res
);
3040 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3043 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3044 if ((*suffixes
) == NULL
) {
3045 ads_msgfree(ads
, res
);
3046 return ADS_ERROR(LDAP_NO_MEMORY
);
3049 ads_msgfree(ads
, res
);
3055 * get the joinable ous for a domain
3056 * @param ads connection to ads server
3057 * @param mem_ctx Pointer to talloc context
3058 * @param ous Pointer to an array of ous
3059 * @param num_ous Pointer to the number of ous
3060 * @return status of search
3062 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3063 TALLOC_CTX
*mem_ctx
,
3068 LDAPMessage
*res
= NULL
;
3069 LDAPMessage
*msg
= NULL
;
3070 const char *attrs
[] = { "dn", NULL
};
3073 status
= ads_search(ads
, &res
,
3074 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3076 if (!ADS_ERR_OK(status
)) {
3080 count
= ads_count_replies(ads
, res
);
3082 ads_msgfree(ads
, res
);
3083 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3086 for (msg
= ads_first_entry(ads
, res
); msg
;
3087 msg
= ads_next_entry(ads
, msg
)) {
3091 dn
= ads_get_dn(ads
, msg
);
3093 ads_msgfree(ads
, res
);
3094 return ADS_ERROR(LDAP_NO_MEMORY
);
3097 if (!add_string_to_array(mem_ctx
, dn
,
3098 (const char ***)ous
,
3100 ads_memfree(ads
, dn
);
3101 ads_msgfree(ads
, res
);
3102 return ADS_ERROR(LDAP_NO_MEMORY
);
3105 ads_memfree(ads
, dn
);
3108 ads_msgfree(ads
, res
);
3115 * pull a DOM_SID from an extended dn string
3116 * @param mem_ctx TALLOC_CTX
3117 * @param extended_dn string
3118 * @param flags string type of extended_dn
3119 * @param sid pointer to a DOM_SID
3120 * @return boolean inidicating success
3122 bool ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3123 const char *extended_dn
,
3124 enum ads_extended_dn_flags flags
,
3133 /* otherwise extended_dn gets stripped off */
3134 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
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
3145 p
= strchr(dn
, ';');
3150 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3154 p
+= strlen(";<SID=");
3163 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3167 case ADS_EXTENDED_DN_STRING
:
3168 if (!string_to_sid(sid
, p
)) {
3172 case ADS_EXTENDED_DN_HEX_STRING
: {
3176 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3181 if (!sid_parse(buf
, buf_len
, sid
)) {
3182 DEBUG(10,("failed to parse sid\n"));
3188 DEBUG(10,("unknown extended dn format\n"));
3196 * pull an array of DOM_SIDs from a ADS result
3197 * @param ads connection to ads server
3198 * @param mem_ctx TALLOC_CTX for allocating sid array
3199 * @param msg Results of search
3200 * @param field Attribute to retrieve
3201 * @param flags string type of extended_dn
3202 * @param sids pointer to sid array to allocate
3203 * @return the count of SIDs pulled
3205 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
3206 TALLOC_CTX
*mem_ctx
,
3209 enum ads_extended_dn_flags flags
,
3216 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
3217 &dn_count
)) == NULL
) {
3221 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
3223 TALLOC_FREE(dn_strings
);
3227 for (i
=0; i
<dn_count
; i
++) {
3229 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3230 flags
, &(*sids
)[i
])) {
3232 TALLOC_FREE(dn_strings
);
3237 TALLOC_FREE(dn_strings
);
3242 /********************************************************************
3243 ********************************************************************/
3245 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3247 LDAPMessage
*res
= NULL
;
3252 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3253 if (!ADS_ERR_OK(status
)) {
3254 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3259 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3260 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3264 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3265 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3269 ads_msgfree(ads
, res
);
3274 /********************************************************************
3275 ********************************************************************/
3277 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3279 LDAPMessage
*res
= NULL
;
3284 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3285 if (!ADS_ERR_OK(status
)) {
3286 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3291 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3292 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3296 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3297 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3301 ads_msgfree(ads
, res
);
3306 /********************************************************************
3307 ********************************************************************/
3309 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3311 LDAPMessage
*res
= NULL
;
3316 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3317 if (!ADS_ERR_OK(status
)) {
3318 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3323 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3324 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3328 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3329 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3333 ads_msgfree(ads
, res
);
3340 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3343 * Join a machine to a realm
3344 * Creates the machine account and sets the machine password
3345 * @param ads connection to ads server
3346 * @param machine name of host to add
3347 * @param org_unit Organizational unit to place machine in
3348 * @return status of join
3350 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3351 uint32 account_type
, const char *org_unit
)
3354 LDAPMessage
*res
= NULL
;
3357 /* machine name must be lowercase */
3358 machine
= SMB_STRDUP(machine_name
);
3359 strlower_m(machine
);
3362 status = ads_find_machine_acct(ads, (void **)&res, machine);
3363 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3364 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3365 status = ads_leave_realm(ads, machine);
3366 if (!ADS_ERR_OK(status)) {
3367 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3368 machine, ads->config.realm));
3373 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3374 if (!ADS_ERR_OK(status
)) {
3375 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3380 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3381 if (!ADS_ERR_OK(status
)) {
3382 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3388 ads_msgfree(ads
, res
);
3395 * Delete a machine from the realm
3396 * @param ads connection to ads server
3397 * @param hostname Machine to remove
3398 * @return status of delete
3400 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3405 char *hostnameDN
, *host
;
3407 LDAPControl ldap_control
;
3408 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3410 pldap_control
[0] = &ldap_control
;
3411 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3412 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3414 /* hostname must be lowercase */
3415 host
= SMB_STRDUP(hostname
);
3418 status
= ads_find_machine_acct(ads
, &res
, host
);
3419 if (!ADS_ERR_OK(status
)) {
3420 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3425 msg
= ads_first_entry(ads
, res
);
3428 return ADS_ERROR_SYSTEM(ENOENT
);
3431 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3433 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3435 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3437 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3440 if (rc
!= LDAP_SUCCESS
) {
3441 const char *attrs
[] = { "cn", NULL
};
3442 LDAPMessage
*msg_sub
;
3444 /* we only search with scope ONE, we do not expect any further
3445 * objects to be created deeper */
3447 status
= ads_do_search_retry(ads
, hostnameDN
,
3448 LDAP_SCOPE_ONELEVEL
,
3449 "(objectclass=*)", attrs
, &res
);
3451 if (!ADS_ERR_OK(status
)) {
3453 ads_memfree(ads
, hostnameDN
);
3457 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3458 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3462 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3464 ads_memfree(ads
, hostnameDN
);
3465 return ADS_ERROR(LDAP_NO_MEMORY
);
3468 status
= ads_del_dn(ads
, dn
);
3469 if (!ADS_ERR_OK(status
)) {
3470 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3472 ads_memfree(ads
, dn
);
3473 ads_memfree(ads
, hostnameDN
);
3477 ads_memfree(ads
, dn
);
3480 /* there should be no subordinate objects anymore */
3481 status
= ads_do_search_retry(ads
, hostnameDN
,
3482 LDAP_SCOPE_ONELEVEL
,
3483 "(objectclass=*)", attrs
, &res
);
3485 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3487 ads_memfree(ads
, hostnameDN
);
3491 /* delete hostnameDN now */
3492 status
= ads_del_dn(ads
, hostnameDN
);
3493 if (!ADS_ERR_OK(status
)) {
3495 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3496 ads_memfree(ads
, hostnameDN
);
3501 ads_memfree(ads
, hostnameDN
);
3503 status
= ads_find_machine_acct(ads
, &res
, host
);
3504 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3505 DEBUG(3, ("Failed to remove host account.\n"));
3515 * pull all token-sids from an LDAP dn
3516 * @param ads connection to ads server
3517 * @param mem_ctx TALLOC_CTX for allocating sid array
3518 * @param dn of LDAP object
3519 * @param user_sid pointer to DOM_SID (objectSid)
3520 * @param primary_group_sid pointer to DOM_SID (self composed)
3521 * @param sids pointer to sid array to allocate
3522 * @param num_sids counter of SIDs pulled
3523 * @return status of token query
3525 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3526 TALLOC_CTX
*mem_ctx
,
3529 DOM_SID
*primary_group_sid
,
3534 LDAPMessage
*res
= NULL
;
3536 size_t tmp_num_sids
;
3538 DOM_SID tmp_user_sid
;
3539 DOM_SID tmp_primary_group_sid
;
3541 const char *attrs
[] = {
3548 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3549 if (!ADS_ERR_OK(status
)) {
3553 count
= ads_count_replies(ads
, res
);
3555 ads_msgfree(ads
, res
);
3556 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3559 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3560 ads_msgfree(ads
, res
);
3561 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3564 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3565 ads_msgfree(ads
, res
);
3566 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3570 /* hack to compose the primary group sid without knowing the
3576 sid_copy(&domsid
, &tmp_user_sid
);
3578 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3579 ads_msgfree(ads
, res
);
3580 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3583 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3584 ads_msgfree(ads
, res
);
3585 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3589 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3591 if (tmp_num_sids
== 0 || !tmp_sids
) {
3592 ads_msgfree(ads
, res
);
3593 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3597 *num_sids
= tmp_num_sids
;
3605 *user_sid
= tmp_user_sid
;
3608 if (primary_group_sid
) {
3609 *primary_group_sid
= tmp_primary_group_sid
;
3612 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3614 ads_msgfree(ads
, res
);
3615 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3619 * Find a sAMAccoutName in LDAP
3620 * @param ads connection to ads server
3621 * @param mem_ctx TALLOC_CTX for allocating sid array
3622 * @param samaccountname to search
3623 * @param uac_ret uint32 pointer userAccountControl attribute value
3624 * @param dn_ret pointer to dn
3625 * @return status of token query
3627 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3628 TALLOC_CTX
*mem_ctx
,
3629 const char *samaccountname
,
3631 const char **dn_ret
)
3634 const char *attrs
[] = { "userAccountControl", NULL
};
3636 LDAPMessage
*res
= NULL
;
3640 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3642 if (filter
== NULL
) {
3643 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3647 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3649 filter
, attrs
, &res
);
3651 if (!ADS_ERR_OK(status
)) {
3655 if (ads_count_replies(ads
, res
) != 1) {
3656 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3660 dn
= ads_get_dn(ads
, res
);
3662 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3666 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3667 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3676 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3678 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3683 ads_memfree(ads
, dn
);
3684 ads_msgfree(ads
, res
);
3690 * find our configuration path
3691 * @param ads connection to ads server
3692 * @param mem_ctx Pointer to talloc context
3693 * @param config_path Pointer to the config path
3694 * @return status of search
3696 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3697 TALLOC_CTX
*mem_ctx
,
3701 LDAPMessage
*res
= NULL
;
3702 const char *config_context
= NULL
;
3703 const char *attrs
[] = { "configurationNamingContext", NULL
};
3705 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3706 "(objectclass=*)", attrs
, &res
);
3707 if (!ADS_ERR_OK(status
)) {
3711 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3712 "configurationNamingContext");
3713 ads_msgfree(ads
, res
);
3714 if (!config_context
) {
3715 return ADS_ERROR(LDAP_NO_MEMORY
);
3719 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3720 if (!*config_path
) {
3721 return ADS_ERROR(LDAP_NO_MEMORY
);
3725 return ADS_ERROR(LDAP_SUCCESS
);
3729 * find the displayName of an extended right
3730 * @param ads connection to ads server
3731 * @param config_path The config path
3732 * @param mem_ctx Pointer to talloc context
3733 * @param GUID struct of the rightsGUID
3734 * @return status of search
3736 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3737 const char *config_path
,
3738 TALLOC_CTX
*mem_ctx
,
3739 const struct GUID
*rights_guid
)
3742 LDAPMessage
*res
= NULL
;
3744 const char *attrs
[] = { "displayName", NULL
};
3745 const char *result
= NULL
;
3748 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3752 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3753 smb_uuid_string(mem_ctx
, *rights_guid
));
3758 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3763 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3765 if (!ADS_ERR_OK(rc
)) {
3769 if (ads_count_replies(ads
, res
) != 1) {
3773 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3776 ads_msgfree(ads
, res
);
3782 * verify or build and verify an account ou
3783 * @param mem_ctx Pointer to talloc context
3784 * @param ads connection to ads server
3786 * @return status of search
3789 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3791 const char **account_ou
)
3793 struct ldb_dn
*name_dn
= NULL
;
3794 const char *name
= NULL
;
3795 char *ou_string
= NULL
;
3797 name_dn
= ldb_dn_explode(mem_ctx
, *account_ou
);
3802 ou_string
= ads_ou_string(ads
, *account_ou
);
3804 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3807 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3808 ads
->config
.bind_path
);
3809 SAFE_FREE(ou_string
);
3811 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3814 name_dn
= ldb_dn_explode(mem_ctx
, name
);
3816 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3819 *account_ou
= talloc_strdup(mem_ctx
, name
);
3821 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);