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 bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
194 struct nbt_cldap_netlogon_5 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
= 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
)
314 /* we need to try once with the realm name and fallback to the
315 netbios domain name if we fail (if netbios has not been disabled */
317 if ( !got_realm
&& !lp_disable_netbios() ) {
318 c_realm
= ads
->server
.workgroup
;
319 if (!c_realm
|| !*c_realm
) {
320 if ( use_own_domain
)
321 c_realm
= lp_workgroup();
325 if ( !c_realm
|| !*c_realm
) {
326 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
327 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
330 if ( use_own_domain
) {
331 c_domain
= lp_workgroup();
333 c_domain
= ads
->server
.workgroup
;
340 * In case of LDAP we use get_dc_name() as that
341 * creates the custom krb5.conf file
343 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
345 struct sockaddr_storage ip_out
;
347 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
348 (got_realm
? "realm" : "domain"), realm
));
350 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
352 * we call ads_try_connect() to fill in the
353 * ads->config details
355 if (ads_try_connect(ads
, srv_name
)) {
360 return NT_STATUS_NO_LOGON_SERVERS
;
363 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
) ) {
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
;
438 * Connect to the LDAP server
439 * @param ads Pointer to an existing ADS_STRUCT
440 * @return status of connection
442 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
444 int version
= LDAP_VERSION3
;
447 char addr
[INET6_ADDRSTRLEN
];
449 ZERO_STRUCT(ads
->ldap
);
450 ads
->ldap
.last_attempt
= time(NULL
);
451 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
453 /* try with a user specified server */
455 if (DEBUGLEVEL
>= 11) {
456 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
457 DEBUG(11,("ads_connect: entering\n"));
458 DEBUGADD(11,("%s\n", s
));
462 if (ads
->server
.ldap_server
&&
463 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
467 ntstatus
= ads_find_dc(ads
);
468 if (NT_STATUS_IS_OK(ntstatus
)) {
472 status
= ADS_ERROR_NT(ntstatus
);
477 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
478 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
480 if (!ads
->auth
.user_name
) {
481 /* Must use the userPrincipalName value here or sAMAccountName
482 and not servicePrincipalName; found by Guenther Deschner */
484 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
487 if (!ads
->auth
.realm
) {
488 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
491 if (!ads
->auth
.kdc_server
) {
492 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
493 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
497 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
498 to MIT kerberos to work (tridge) */
501 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
502 setenv(env
, ads
->auth
.kdc_server
, 1);
507 /* If the caller() requested no LDAP bind, then we are done */
509 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
510 status
= ADS_SUCCESS
;
514 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
515 if (!ads
->ldap
.mem_ctx
) {
516 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
520 /* Otherwise setup the TCP LDAP session */
522 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
523 LDAP_PORT
, lp_ldap_timeout());
524 if (ads
->ldap
.ld
== NULL
) {
525 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
528 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
530 /* cache the successful connection for workgroup and realm */
531 if (ads_closest_dc(ads
)) {
532 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
533 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
536 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
538 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
539 if (!ADS_ERR_OK(status
)) {
543 /* fill in the current time and offsets */
545 status
= ads_current_time( ads
);
546 if ( !ADS_ERR_OK(status
) ) {
550 /* Now do the bind */
552 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
553 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
557 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
558 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
562 status
= ads_sasl_bind(ads
);
565 if (DEBUGLEVEL
>= 11) {
566 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
567 DEBUG(11,("ads_connect: leaving with: %s\n",
568 ads_errstr(status
)));
569 DEBUGADD(11,("%s\n", s
));
577 * Disconnect the LDAP server
578 * @param ads Pointer to an existing ADS_STRUCT
580 void ads_disconnect(ADS_STRUCT
*ads
)
583 ldap_unbind(ads
->ldap
.ld
);
586 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
587 ads
->ldap
.wrap_ops
->disconnect(ads
);
589 if (ads
->ldap
.mem_ctx
) {
590 talloc_free(ads
->ldap
.mem_ctx
);
592 ZERO_STRUCT(ads
->ldap
);
596 Duplicate a struct berval into talloc'ed memory
598 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
600 struct berval
*value
;
602 if (!in_val
) return NULL
;
604 value
= TALLOC_ZERO_P(ctx
, struct berval
);
607 if (in_val
->bv_len
== 0) return value
;
609 value
->bv_len
= in_val
->bv_len
;
610 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
616 Make a values list out of an array of (struct berval *)
618 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
619 const struct berval
**in_vals
)
621 struct berval
**values
;
624 if (!in_vals
) return NULL
;
625 for (i
=0; in_vals
[i
]; i
++)
627 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
628 if (!values
) return NULL
;
630 for (i
=0; in_vals
[i
]; i
++) {
631 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
637 UTF8-encode a values list out of an array of (char *)
639 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
644 if (!in_vals
) return NULL
;
645 for (i
=0; in_vals
[i
]; i
++)
647 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
648 if (!values
) return NULL
;
650 for (i
=0; in_vals
[i
]; i
++) {
651 if (push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]) == (size_t) -1) {
660 Pull a (char *) array out of a UTF8-encoded values list
662 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
667 if (!in_vals
) return NULL
;
668 for (i
=0; in_vals
[i
]; i
++)
670 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
671 if (!values
) return NULL
;
673 for (i
=0; in_vals
[i
]; i
++) {
674 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
680 * Do a search with paged results. cookie must be null on the first
681 * call, and then returned on each subsequent call. It will be null
682 * again when the entire search is complete
683 * @param ads connection to ads server
684 * @param bind_path Base dn for the search
685 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
686 * @param expr Search expression - specified in local charset
687 * @param attrs Attributes to retrieve - specified in utf8 or ascii
688 * @param res ** which will contain results - free res* with ads_msgfree()
689 * @param count Number of entries retrieved on this page
690 * @param cookie The paged results cookie to be returned on subsequent calls
691 * @return status of search
693 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
694 const char *bind_path
,
695 int scope
, const char *expr
,
696 const char **attrs
, void *args
,
698 int *count
, struct berval
**cookie
)
701 char *utf8_expr
, *utf8_path
, **search_attrs
;
702 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
703 BerElement
*cookie_be
= NULL
;
704 struct berval
*cookie_bv
= NULL
;
705 BerElement
*ext_be
= NULL
;
706 struct berval
*ext_bv
= NULL
;
709 ads_control
*external_control
= (ads_control
*) args
;
713 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
714 return ADS_ERROR(LDAP_NO_MEMORY
);
716 /* 0 means the conversion worked but the result was empty
717 so we only fail if it's -1. In any case, it always
718 at least nulls out the dest */
719 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
720 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
725 if (!attrs
|| !(*attrs
))
728 /* This would be the utf8-encoded version...*/
729 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
730 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
))) {
736 /* Paged results only available on ldap v3 or later */
737 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
738 if (version
< LDAP_VERSION3
) {
739 rc
= LDAP_NOT_SUPPORTED
;
743 cookie_be
= ber_alloc_t(LBER_USE_DER
);
745 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
746 ber_bvfree(*cookie
); /* don't need it from last time */
749 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
751 ber_flatten(cookie_be
, &cookie_bv
);
752 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
753 PagedResults
.ldctl_iscritical
= (char) 1;
754 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
755 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
757 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
758 NoReferrals
.ldctl_iscritical
= (char) 0;
759 NoReferrals
.ldctl_value
.bv_len
= 0;
760 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
762 if (external_control
&&
763 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
764 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
766 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
767 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
769 /* win2k does not accept a ldctl_value beeing passed in */
771 if (external_control
->val
!= 0) {
773 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
778 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
782 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
787 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
788 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
791 ExternalCtrl
.ldctl_value
.bv_len
= 0;
792 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
795 controls
[0] = &NoReferrals
;
796 controls
[1] = &PagedResults
;
797 controls
[2] = &ExternalCtrl
;
801 controls
[0] = &NoReferrals
;
802 controls
[1] = &PagedResults
;
806 /* we need to disable referrals as the openldap libs don't
807 handle them and paged results at the same time. Using them
808 together results in the result record containing the server
809 page control being removed from the result list (tridge/jmcd)
811 leaving this in despite the control that says don't generate
812 referrals, in case the server doesn't support it (jmcd)
814 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
816 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
817 search_attrs
, 0, controls
,
819 (LDAPMessage
**)res
);
821 ber_free(cookie_be
, 1);
822 ber_bvfree(cookie_bv
);
825 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
826 ldap_err2string(rc
)));
830 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
831 NULL
, &rcontrols
, 0);
837 for (i
=0; rcontrols
[i
]; i
++) {
838 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
839 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
840 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
842 /* the berval is the cookie, but must be freed when
844 if (cookie_bv
->bv_len
) /* still more to do */
845 *cookie
=ber_bvdup(cookie_bv
);
848 ber_bvfree(cookie_bv
);
849 ber_free(cookie_be
, 1);
853 ldap_controls_free(rcontrols
);
866 /* if/when we decide to utf8-encode attrs, take out this next line */
867 TALLOC_FREE(search_attrs
);
869 return ADS_ERROR(rc
);
872 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
873 int scope
, const char *expr
,
874 const char **attrs
, LDAPMessage
**res
,
875 int *count
, struct berval
**cookie
)
877 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
882 * Get all results for a search. This uses ads_do_paged_search() to return
883 * all entries in a large search.
884 * @param ads connection to ads server
885 * @param bind_path Base dn for the search
886 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
887 * @param expr Search expression
888 * @param attrs Attributes to retrieve
889 * @param res ** which will contain results - free res* with ads_msgfree()
890 * @return status of search
892 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
893 int scope
, const char *expr
,
894 const char **attrs
, void *args
,
897 struct berval
*cookie
= NULL
;
902 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
905 if (!ADS_ERR_OK(status
))
908 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
910 LDAPMessage
*res2
= NULL
;
912 LDAPMessage
*msg
, *next
;
914 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
915 attrs
, args
, &res2
, &count
, &cookie
);
917 if (!ADS_ERR_OK(status2
)) break;
919 /* this relies on the way that ldap_add_result_entry() works internally. I hope
920 that this works on all ldap libs, but I have only tested with openldap */
921 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
922 next
= ads_next_message(ads
, msg
);
923 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
925 /* note that we do not free res2, as the memory is now
926 part of the main returned list */
929 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
930 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
936 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
937 int scope
, const char *expr
,
938 const char **attrs
, LDAPMessage
**res
)
940 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
943 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
944 int scope
, const char *expr
,
945 const char **attrs
, uint32 sd_flags
,
950 args
.control
= ADS_SD_FLAGS_OID
;
952 args
.critical
= True
;
954 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
959 * Run a function on all results for a search. Uses ads_do_paged_search() and
960 * runs the function as each page is returned, using ads_process_results()
961 * @param ads connection to ads server
962 * @param bind_path Base dn for the search
963 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
964 * @param expr Search expression - specified in local charset
965 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
966 * @param fn Function which takes attr name, values list, and data_area
967 * @param data_area Pointer which is passed to function on each call
968 * @return status of search
970 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
971 int scope
, const char *expr
, const char **attrs
,
972 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
975 struct berval
*cookie
= NULL
;
980 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
983 if (!ADS_ERR_OK(status
)) return status
;
985 ads_process_results(ads
, res
, fn
, data_area
);
986 ads_msgfree(ads
, res
);
989 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
990 &res
, &count
, &cookie
);
992 if (!ADS_ERR_OK(status
)) break;
994 ads_process_results(ads
, res
, fn
, data_area
);
995 ads_msgfree(ads
, res
);
1002 * Do a search with a timeout.
1003 * @param ads connection to ads server
1004 * @param bind_path Base dn for the search
1005 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1006 * @param expr Search expression
1007 * @param attrs Attributes to retrieve
1008 * @param res ** which will contain results - free res* with ads_msgfree()
1009 * @return status of search
1011 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1013 const char **attrs
, LDAPMessage
**res
)
1016 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1020 if (!(ctx
= talloc_init("ads_do_search"))) {
1021 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1022 return ADS_ERROR(LDAP_NO_MEMORY
);
1025 /* 0 means the conversion worked but the result was empty
1026 so we only fail if it's negative. In any case, it always
1027 at least nulls out the dest */
1028 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
1029 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
1030 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1031 rc
= LDAP_NO_MEMORY
;
1035 if (!attrs
|| !(*attrs
))
1036 search_attrs
= NULL
;
1038 /* This would be the utf8-encoded version...*/
1039 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1040 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
)))
1042 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1043 rc
= LDAP_NO_MEMORY
;
1048 /* see the note in ads_do_paged_search - we *must* disable referrals */
1049 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1051 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1052 search_attrs
, 0, NULL
, NULL
,
1054 (LDAPMessage
**)res
);
1056 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1057 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1062 talloc_destroy(ctx
);
1063 /* if/when we decide to utf8-encode attrs, take out this next line */
1064 TALLOC_FREE(search_attrs
);
1065 return ADS_ERROR(rc
);
1068 * Do a general ADS search
1069 * @param ads connection to ads server
1070 * @param res ** which will contain results - free res* with ads_msgfree()
1071 * @param expr Search expression
1072 * @param attrs Attributes to retrieve
1073 * @return status of search
1075 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1076 const char *expr
, const char **attrs
)
1078 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1083 * Do a search on a specific DistinguishedName
1084 * @param ads connection to ads server
1085 * @param res ** which will contain results - free res* with ads_msgfree()
1086 * @param dn DistinguishName to search
1087 * @param attrs Attributes to retrieve
1088 * @return status of search
1090 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1091 const char *dn
, const char **attrs
)
1093 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1098 * Free up memory from a ads_search
1099 * @param ads connection to ads server
1100 * @param msg Search results to free
1102 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1109 * Free up memory from various ads requests
1110 * @param ads connection to ads server
1111 * @param mem Area to free
1113 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1119 * Get a dn from search results
1120 * @param ads connection to ads server
1121 * @param msg Search result
1124 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1126 char *utf8_dn
, *unix_dn
;
1128 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1131 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1135 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
1136 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1140 ldap_memfree(utf8_dn
);
1145 * Get the parent from a dn
1146 * @param dn the dn to return the parent from
1147 * @return parent dn string
1149 char *ads_parent_dn(const char *dn
)
1157 p
= strchr(dn
, ',');
1167 * Find a machine account given a hostname
1168 * @param ads connection to ads server
1169 * @param res ** which will contain results - free res* with ads_msgfree()
1170 * @param host Hostname to search for
1171 * @return status of search
1173 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1174 const char *machine
)
1178 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1182 /* the easiest way to find a machine account anywhere in the tree
1183 is to look for hostname$ */
1184 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1185 DEBUG(1, ("asprintf failed!\n"));
1186 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1189 status
= ads_search(ads
, res
, expr
, attrs
);
1195 * Initialize a list of mods to be used in a modify request
1196 * @param ctx An initialized TALLOC_CTX
1197 * @return allocated ADS_MODLIST
1199 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1201 #define ADS_MODLIST_ALLOC_SIZE 10
1204 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1205 /* -1 is safety to make sure we don't go over the end.
1206 need to reset it to NULL before doing ldap modify */
1207 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1209 return (ADS_MODLIST
)mods
;
1214 add an attribute to the list, with values list already constructed
1216 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1217 int mod_op
, const char *name
,
1218 const void *_invals
)
1220 const void **invals
= (const void **)_invals
;
1222 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1223 struct berval
**ber_values
= NULL
;
1224 char **char_values
= NULL
;
1227 mod_op
= LDAP_MOD_DELETE
;
1229 if (mod_op
& LDAP_MOD_BVALUES
)
1230 ber_values
= ads_dup_values(ctx
,
1231 (const struct berval
**)invals
);
1233 char_values
= ads_push_strvals(ctx
,
1234 (const char **) invals
);
1237 /* find the first empty slot */
1238 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1240 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1241 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1242 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1243 return ADS_ERROR(LDAP_NO_MEMORY
);
1244 memset(&modlist
[curmod
], 0,
1245 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1246 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1247 *mods
= (ADS_MODLIST
)modlist
;
1250 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1251 return ADS_ERROR(LDAP_NO_MEMORY
);
1252 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1253 if (mod_op
& LDAP_MOD_BVALUES
) {
1254 modlist
[curmod
]->mod_bvalues
= ber_values
;
1255 } else if (mod_op
& LDAP_MOD_DELETE
) {
1256 modlist
[curmod
]->mod_values
= NULL
;
1258 modlist
[curmod
]->mod_values
= char_values
;
1261 modlist
[curmod
]->mod_op
= mod_op
;
1262 return ADS_ERROR(LDAP_SUCCESS
);
1266 * Add a single string value to a mod list
1267 * @param ctx An initialized TALLOC_CTX
1268 * @param mods An initialized ADS_MODLIST
1269 * @param name The attribute name to add
1270 * @param val The value to add - NULL means DELETE
1271 * @return ADS STATUS indicating success of add
1273 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1274 const char *name
, const char *val
)
1276 const char *values
[2];
1282 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1283 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1287 * Add an array of string values to a mod list
1288 * @param ctx An initialized TALLOC_CTX
1289 * @param mods An initialized ADS_MODLIST
1290 * @param name The attribute name to add
1291 * @param vals The array of string values to add - NULL means DELETE
1292 * @return ADS STATUS indicating success of add
1294 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1295 const char *name
, const char **vals
)
1298 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1299 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1300 name
, (const void **) vals
);
1305 * Add a single ber-encoded value to a mod list
1306 * @param ctx An initialized TALLOC_CTX
1307 * @param mods An initialized ADS_MODLIST
1308 * @param name The attribute name to add
1309 * @param val The value to add - NULL means DELETE
1310 * @return ADS STATUS indicating success of add
1312 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1313 const char *name
, const struct berval
*val
)
1315 const struct berval
*values
[2];
1320 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1321 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1322 name
, (const void **) values
);
1327 * Perform an ldap modify
1328 * @param ads connection to ads server
1329 * @param mod_dn DistinguishedName to modify
1330 * @param mods list of modifications to perform
1331 * @return status of modify
1333 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1336 char *utf8_dn
= NULL
;
1338 this control is needed to modify that contains a currently
1339 non-existent attribute (but allowable for the object) to run
1341 LDAPControl PermitModify
= {
1342 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1345 LDAPControl
*controls
[2];
1347 controls
[0] = &PermitModify
;
1350 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1351 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1354 /* find the end of the list, marked by NULL or -1 */
1355 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1356 /* make sure the end of the list is NULL */
1358 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1359 (LDAPMod
**) mods
, controls
, NULL
);
1361 return ADS_ERROR(ret
);
1365 * Perform an ldap add
1366 * @param ads connection to ads server
1367 * @param new_dn DistinguishedName to add
1368 * @param mods list of attributes and values for DN
1369 * @return status of add
1371 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1374 char *utf8_dn
= NULL
;
1376 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1377 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1378 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1381 /* find the end of the list, marked by NULL or -1 */
1382 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1383 /* make sure the end of the list is NULL */
1386 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1388 return ADS_ERROR(ret
);
1392 * Delete a DistinguishedName
1393 * @param ads connection to ads server
1394 * @param new_dn DistinguishedName to delete
1395 * @return status of delete
1397 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1400 char *utf8_dn
= NULL
;
1401 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1402 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1403 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1406 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1408 return ADS_ERROR(ret
);
1412 * Build an org unit string
1413 * if org unit is Computers or blank then assume a container, otherwise
1414 * assume a / separated list of organisational units.
1415 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1416 * @param ads connection to ads server
1417 * @param org_unit Organizational unit
1418 * @return org unit string - caller must free
1420 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1424 if (!org_unit
|| !*org_unit
) {
1426 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1428 /* samba4 might not yet respond to a wellknownobject-query */
1429 return ret
? ret
: SMB_STRDUP("cn=Computers");
1432 if (strequal(org_unit
, "Computers")) {
1433 return SMB_STRDUP("cn=Computers");
1436 /* jmcd: removed "\\" from the separation chars, because it is
1437 needed as an escape for chars like '#' which are valid in an
1439 return ads_build_path(org_unit
, "/", "ou=", 1);
1443 * Get a org unit string for a well-known GUID
1444 * @param ads connection to ads server
1445 * @param wknguid Well known GUID
1446 * @return org unit string - caller must free
1448 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1451 LDAPMessage
*res
= NULL
;
1452 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1453 **bind_dn_exp
= NULL
;
1454 const char *attrs
[] = {"distinguishedName", NULL
};
1455 int new_ln
, wkn_ln
, bind_ln
, i
;
1457 if (wknguid
== NULL
) {
1461 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1462 DEBUG(1, ("asprintf failed!\n"));
1466 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1467 if (!ADS_ERR_OK(status
)) {
1468 DEBUG(1,("Failed while searching for: %s\n", base
));
1472 if (ads_count_replies(ads
, res
) != 1) {
1476 /* substitute the bind-path from the well-known-guid-search result */
1477 wkn_dn
= ads_get_dn(ads
, res
);
1482 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1487 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1492 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1494 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1497 new_ln
= wkn_ln
- bind_ln
;
1499 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1504 for (i
=1; i
< new_ln
; i
++) {
1507 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1513 ret
= SMB_STRDUP(s
);
1522 ads_msgfree(ads
, res
);
1523 ads_memfree(ads
, wkn_dn
);
1525 ldap_value_free(wkn_dn_exp
);
1528 ldap_value_free(bind_dn_exp
);
1535 * Adds (appends) an item to an attribute array, rather then
1536 * replacing the whole list
1537 * @param ctx An initialized TALLOC_CTX
1538 * @param mods An initialized ADS_MODLIST
1539 * @param name name of the ldap attribute to append to
1540 * @param vals an array of values to add
1541 * @return status of addition
1544 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1545 const char *name
, const char **vals
)
1547 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1548 (const void *) vals
);
1552 * Determines the computer account's current KVNO via an LDAP lookup
1553 * @param ads An initialized ADS_STRUCT
1554 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1555 * @return the kvno for the computer account, or -1 in case of a failure.
1558 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1560 LDAPMessage
*res
= NULL
;
1561 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1563 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1564 char *dn_string
= NULL
;
1565 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1567 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1568 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1571 ret
= ads_search(ads
, &res
, filter
, attrs
);
1573 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1574 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1575 ads_msgfree(ads
, res
);
1579 dn_string
= ads_get_dn(ads
, res
);
1581 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1582 ads_msgfree(ads
, res
);
1585 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1586 ads_memfree(ads
, dn_string
);
1588 /* ---------------------------------------------------------
1589 * 0 is returned as a default KVNO from this point on...
1590 * This is done because Windows 2000 does not support key
1591 * version numbers. Chances are that a failure in the next
1592 * step is simply due to Windows 2000 being used for a
1593 * domain controller. */
1596 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1597 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1598 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1599 ads_msgfree(ads
, res
);
1604 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1605 ads_msgfree(ads
, res
);
1610 * This clears out all registered spn's for a given hostname
1611 * @param ads An initilaized ADS_STRUCT
1612 * @param machine_name the NetBIOS name of the computer.
1613 * @return 0 upon success, non-zero otherwise.
1616 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1619 LDAPMessage
*res
= NULL
;
1621 const char *servicePrincipalName
[1] = {NULL
};
1622 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1623 char *dn_string
= NULL
;
1625 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1626 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1627 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1628 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1629 ads_msgfree(ads
, res
);
1630 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1633 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1634 ctx
= talloc_init("ads_clear_service_principal_names");
1636 ads_msgfree(ads
, res
);
1637 return ADS_ERROR(LDAP_NO_MEMORY
);
1640 if (!(mods
= ads_init_mods(ctx
))) {
1641 talloc_destroy(ctx
);
1642 ads_msgfree(ads
, res
);
1643 return ADS_ERROR(LDAP_NO_MEMORY
);
1645 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1646 if (!ADS_ERR_OK(ret
)) {
1647 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1648 ads_msgfree(ads
, res
);
1649 talloc_destroy(ctx
);
1652 dn_string
= ads_get_dn(ads
, res
);
1654 talloc_destroy(ctx
);
1655 ads_msgfree(ads
, res
);
1656 return ADS_ERROR(LDAP_NO_MEMORY
);
1658 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1659 ads_memfree(ads
,dn_string
);
1660 if (!ADS_ERR_OK(ret
)) {
1661 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1663 ads_msgfree(ads
, res
);
1664 talloc_destroy(ctx
);
1668 ads_msgfree(ads
, res
);
1669 talloc_destroy(ctx
);
1674 * This adds a service principal name to an existing computer account
1675 * (found by hostname) in AD.
1676 * @param ads An initialized ADS_STRUCT
1677 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1678 * @param my_fqdn The fully qualified DNS name of the machine
1679 * @param spn A string of the service principal to add, i.e. 'host'
1680 * @return 0 upon sucess, or non-zero if a failure occurs
1683 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1684 const char *my_fqdn
, const char *spn
)
1688 LDAPMessage
*res
= NULL
;
1691 char *dn_string
= NULL
;
1692 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1694 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1695 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1696 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1698 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1699 spn
, machine_name
, ads
->config
.realm
));
1700 ads_msgfree(ads
, res
);
1701 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1704 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1705 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1706 ads_msgfree(ads
, res
);
1707 return ADS_ERROR(LDAP_NO_MEMORY
);
1710 /* add short name spn */
1712 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1713 talloc_destroy(ctx
);
1714 ads_msgfree(ads
, res
);
1715 return ADS_ERROR(LDAP_NO_MEMORY
);
1718 strlower_m(&psp1
[strlen(spn
)]);
1719 servicePrincipalName
[0] = psp1
;
1721 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1722 psp1
, machine_name
));
1725 /* add fully qualified spn */
1727 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1728 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1732 strlower_m(&psp2
[strlen(spn
)]);
1733 servicePrincipalName
[1] = psp2
;
1735 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1736 psp2
, machine_name
));
1738 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1739 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1743 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1744 if (!ADS_ERR_OK(ret
)) {
1745 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1749 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1750 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1754 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1755 ads_memfree(ads
,dn_string
);
1756 if (!ADS_ERR_OK(ret
)) {
1757 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1763 ads_msgfree(ads
, res
);
1768 * adds a machine account to the ADS server
1769 * @param ads An intialized ADS_STRUCT
1770 * @param machine_name - the NetBIOS machine name of this account.
1771 * @param account_type A number indicating the type of account to create
1772 * @param org_unit The LDAP path in which to place this account
1773 * @return 0 upon success, or non-zero otherwise
1776 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1777 const char *org_unit
)
1780 char *samAccountName
, *controlstr
;
1783 char *machine_escaped
= NULL
;
1785 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1786 "user", "computer", NULL
};
1787 LDAPMessage
*res
= NULL
;
1788 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1789 UF_DONT_EXPIRE_PASSWD
|\
1790 UF_ACCOUNTDISABLE
);
1792 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1793 return ADS_ERROR(LDAP_NO_MEMORY
);
1795 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1797 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1798 if (!machine_escaped
) {
1802 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1803 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1805 if ( !new_dn
|| !samAccountName
) {
1809 #ifndef ENCTYPE_ARCFOUR_HMAC
1810 acct_control
|= UF_USE_DES_KEY_ONLY
;
1813 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1817 if (!(mods
= ads_init_mods(ctx
))) {
1821 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1822 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1823 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1824 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1826 ret
= ads_gen_add(ads
, new_dn
, mods
);
1829 SAFE_FREE(machine_escaped
);
1830 ads_msgfree(ads
, res
);
1831 talloc_destroy(ctx
);
1837 * move a machine account to another OU on the ADS server
1838 * @param ads - An intialized ADS_STRUCT
1839 * @param machine_name - the NetBIOS machine name of this account.
1840 * @param org_unit - The LDAP path in which to place this account
1841 * @param moved - whether we moved the machine account (optional)
1842 * @return 0 upon success, or non-zero otherwise
1845 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1846 const char *org_unit
, bool *moved
)
1850 LDAPMessage
*res
= NULL
;
1851 char *filter
= NULL
;
1852 char *computer_dn
= NULL
;
1854 char *computer_rdn
= NULL
;
1855 bool need_move
= False
;
1857 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1858 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1862 /* Find pre-existing machine */
1863 rc
= ads_search(ads
, &res
, filter
, NULL
);
1864 if (!ADS_ERR_OK(rc
)) {
1868 computer_dn
= ads_get_dn(ads
, res
);
1870 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1874 parent_dn
= ads_parent_dn(computer_dn
);
1875 if (strequal(parent_dn
, org_unit
)) {
1881 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
1882 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1886 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
1887 org_unit
, 1, NULL
, NULL
);
1888 rc
= ADS_ERROR(ldap_status
);
1891 ads_msgfree(ads
, res
);
1893 SAFE_FREE(computer_dn
);
1894 SAFE_FREE(computer_rdn
);
1896 if (!ADS_ERR_OK(rc
)) {
1908 dump a binary result from ldap
1910 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1913 for (i
=0; values
[i
]; i
++) {
1914 printf("%s: ", field
);
1915 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1916 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1922 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1925 for (i
=0; values
[i
]; i
++) {
1930 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1931 smb_uuid_unpack(guid
, &tmp
);
1932 printf("%s: %s\n", field
, smb_uuid_string(talloc_tos(), tmp
));
1937 dump a sid result from ldap
1939 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1942 for (i
=0; values
[i
]; i
++) {
1945 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1946 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
1951 dump ntSecurityDescriptor
1953 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
1955 TALLOC_CTX
*frame
= talloc_stackframe();
1956 struct security_descriptor
*psd
;
1959 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
1960 values
[0]->bv_len
, &psd
);
1961 if (!NT_STATUS_IS_OK(status
)) {
1962 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1963 nt_errstr(status
)));
1969 ads_disp_sd(ads
, talloc_tos(), psd
);
1976 dump a string result from ldap
1978 static void dump_string(const char *field
, char **values
)
1981 for (i
=0; values
[i
]; i
++) {
1982 printf("%s: %s\n", field
, values
[i
]);
1987 dump a field from LDAP on stdout
1991 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
1996 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
1998 {"objectGUID", False
, dump_guid
},
1999 {"netbootGUID", False
, dump_guid
},
2000 {"nTSecurityDescriptor", False
, dump_sd
},
2001 {"dnsRecord", False
, dump_binary
},
2002 {"objectSid", False
, dump_sid
},
2003 {"tokenGroups", False
, dump_sid
},
2004 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2005 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2006 {"mS-DS-CreatorSID", False
, dump_sid
},
2007 {"msExchMailboxGuid", False
, dump_guid
},
2012 if (!field
) { /* must be end of an entry */
2017 for (i
=0; handlers
[i
].name
; i
++) {
2018 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
2019 if (!values
) /* first time, indicate string or not */
2020 return handlers
[i
].string
;
2021 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2025 if (!handlers
[i
].name
) {
2026 if (!values
) /* first time, indicate string conversion */
2028 dump_string(field
, (char **)values
);
2034 * Dump a result from LDAP on stdout
2035 * used for debugging
2036 * @param ads connection to ads server
2037 * @param res Results to dump
2040 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2042 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2046 * Walk through results, calling a function for each entry found.
2047 * The function receives a field name, a berval * array of values,
2048 * and a data area passed through from the start. The function is
2049 * called once with null for field and values at the end of each
2051 * @param ads connection to ads server
2052 * @param res Results to process
2053 * @param fn Function for processing each result
2054 * @param data_area user-defined area to pass to function
2056 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2057 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2063 if (!(ctx
= talloc_init("ads_process_results")))
2066 for (msg
= ads_first_entry(ads
, res
); msg
;
2067 msg
= ads_next_entry(ads
, msg
)) {
2071 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2072 (LDAPMessage
*)msg
,&b
);
2074 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2075 (LDAPMessage
*)msg
,b
)) {
2076 struct berval
**ber_vals
;
2077 char **str_vals
, **utf8_vals
;
2081 pull_utf8_talloc(ctx
, &field
, utf8_field
);
2082 string
= fn(ads
, field
, NULL
, data_area
);
2085 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2086 (LDAPMessage
*)msg
, field
);
2087 str_vals
= ads_pull_strvals(ctx
,
2088 (const char **) utf8_vals
);
2089 fn(ads
, field
, (void **) str_vals
, data_area
);
2090 ldap_value_free(utf8_vals
);
2092 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2093 (LDAPMessage
*)msg
, field
);
2094 fn(ads
, field
, (void **) ber_vals
, data_area
);
2096 ldap_value_free_len(ber_vals
);
2098 ldap_memfree(utf8_field
);
2101 talloc_free_children(ctx
);
2102 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2105 talloc_destroy(ctx
);
2109 * count how many replies are in a LDAPMessage
2110 * @param ads connection to ads server
2111 * @param res Results to count
2112 * @return number of replies
2114 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2116 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2120 * pull the first entry from a ADS result
2121 * @param ads connection to ads server
2122 * @param res Results of search
2123 * @return first entry from result
2125 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2127 return ldap_first_entry(ads
->ldap
.ld
, res
);
2131 * pull the next entry from a ADS result
2132 * @param ads connection to ads server
2133 * @param res Results of search
2134 * @return next entry from result
2136 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2138 return ldap_next_entry(ads
->ldap
.ld
, res
);
2142 * pull the first message from a ADS result
2143 * @param ads connection to ads server
2144 * @param res Results of search
2145 * @return first message from result
2147 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2149 return ldap_first_message(ads
->ldap
.ld
, res
);
2153 * pull the next message from a ADS result
2154 * @param ads connection to ads server
2155 * @param res Results of search
2156 * @return next message from result
2158 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2160 return ldap_next_message(ads
->ldap
.ld
, res
);
2164 * pull a single string from a ADS result
2165 * @param ads connection to ads server
2166 * @param mem_ctx TALLOC_CTX to use for allocating result string
2167 * @param msg Results of search
2168 * @param field Attribute to retrieve
2169 * @return Result string in talloc context
2171 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2179 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2184 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
2186 if (rc
!= (size_t)-1)
2190 ldap_value_free(values
);
2195 * pull an array of strings from a ADS result
2196 * @param ads connection to ads server
2197 * @param mem_ctx TALLOC_CTX to use for allocating result string
2198 * @param msg Results of search
2199 * @param field Attribute to retrieve
2200 * @return Result strings in talloc context
2202 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2203 LDAPMessage
*msg
, const char *field
,
2210 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2214 *num_values
= ldap_count_values(values
);
2216 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2218 ldap_value_free(values
);
2222 for (i
=0;i
<*num_values
;i
++) {
2223 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
2224 ldap_value_free(values
);
2230 ldap_value_free(values
);
2235 * pull an array of strings from a ADS result
2236 * (handle large multivalue attributes with range retrieval)
2237 * @param ads connection to ads server
2238 * @param mem_ctx TALLOC_CTX to use for allocating result string
2239 * @param msg Results of search
2240 * @param field Attribute to retrieve
2241 * @param current_strings strings returned by a previous call to this function
2242 * @param next_attribute The next query should ask for this attribute
2243 * @param num_values How many values did we get this time?
2244 * @param more_values Are there more values to get?
2245 * @return Result strings in talloc context
2247 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2248 TALLOC_CTX
*mem_ctx
,
2249 LDAPMessage
*msg
, const char *field
,
2250 char **current_strings
,
2251 const char **next_attribute
,
2252 size_t *num_strings
,
2256 char *expected_range_attrib
, *range_attr
;
2257 BerElement
*ptr
= NULL
;
2260 size_t num_new_strings
;
2261 unsigned long int range_start
;
2262 unsigned long int range_end
;
2264 /* we might have been given the whole lot anyway */
2265 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2266 *more_strings
= False
;
2270 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2272 /* look for Range result */
2273 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2275 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2276 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2277 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2285 /* nothing here - this field is just empty */
2286 *more_strings
= False
;
2290 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2291 &range_start
, &range_end
) == 2) {
2292 *more_strings
= True
;
2294 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2295 &range_start
) == 1) {
2296 *more_strings
= False
;
2298 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2300 ldap_memfree(range_attr
);
2301 *more_strings
= False
;
2306 if ((*num_strings
) != range_start
) {
2307 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2308 " - aborting range retreival\n",
2309 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2310 ldap_memfree(range_attr
);
2311 *more_strings
= False
;
2315 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2317 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2318 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2319 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2320 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2321 (unsigned long int)num_new_strings
));
2322 ldap_memfree(range_attr
);
2323 *more_strings
= False
;
2327 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2328 *num_strings
+ num_new_strings
);
2330 if (strings
== NULL
) {
2331 ldap_memfree(range_attr
);
2332 *more_strings
= False
;
2336 if (new_strings
&& num_new_strings
) {
2337 memcpy(&strings
[*num_strings
], new_strings
,
2338 sizeof(*new_strings
) * num_new_strings
);
2341 (*num_strings
) += num_new_strings
;
2343 if (*more_strings
) {
2344 *next_attribute
= talloc_asprintf(mem_ctx
,
2349 if (!*next_attribute
) {
2350 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2351 ldap_memfree(range_attr
);
2352 *more_strings
= False
;
2357 ldap_memfree(range_attr
);
2363 * pull a single uint32 from a ADS result
2364 * @param ads connection to ads server
2365 * @param msg Results of search
2366 * @param field Attribute to retrieve
2367 * @param v Pointer to int to store result
2368 * @return boolean inidicating success
2370 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2375 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2379 ldap_value_free(values
);
2383 *v
= atoi(values
[0]);
2384 ldap_value_free(values
);
2389 * pull a single objectGUID from an ADS result
2390 * @param ads connection to ADS server
2391 * @param msg results of search
2392 * @param guid 37-byte area to receive text guid
2393 * @return boolean indicating success
2395 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2398 UUID_FLAT flat_guid
;
2400 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2405 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2406 smb_uuid_unpack(flat_guid
, guid
);
2407 ldap_value_free(values
);
2410 ldap_value_free(values
);
2417 * pull a single DOM_SID from a ADS result
2418 * @param ads connection to ads server
2419 * @param msg Results of search
2420 * @param field Attribute to retrieve
2421 * @param sid Pointer to sid to store result
2422 * @return boolean inidicating success
2424 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2427 struct berval
**values
;
2430 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2436 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2438 ldap_value_free_len(values
);
2443 * pull an array of DOM_SIDs from a ADS result
2444 * @param ads connection to ads server
2445 * @param mem_ctx TALLOC_CTX for allocating sid array
2446 * @param msg Results of search
2447 * @param field Attribute to retrieve
2448 * @param sids pointer to sid array to allocate
2449 * @return the count of SIDs pulled
2451 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2452 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2454 struct berval
**values
;
2458 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2463 for (i
=0; values
[i
]; i
++)
2467 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2469 ldap_value_free_len(values
);
2477 for (i
=0; values
[i
]; i
++) {
2478 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2480 DEBUG(10, ("pulling SID: %s\n",
2481 sid_string_dbg(&(*sids
)[count
])));
2486 ldap_value_free_len(values
);
2491 * pull a SEC_DESC from a ADS result
2492 * @param ads connection to ads server
2493 * @param mem_ctx TALLOC_CTX for allocating sid array
2494 * @param msg Results of search
2495 * @param field Attribute to retrieve
2496 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2497 * @return boolean inidicating success
2499 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2500 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2502 struct berval
**values
;
2505 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2507 if (!values
) return false;
2511 status
= unmarshall_sec_desc(mem_ctx
,
2512 (uint8
*)values
[0]->bv_val
,
2513 values
[0]->bv_len
, sd
);
2514 if (!NT_STATUS_IS_OK(status
)) {
2515 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2516 nt_errstr(status
)));
2521 ldap_value_free_len(values
);
2526 * in order to support usernames longer than 21 characters we need to
2527 * use both the sAMAccountName and the userPrincipalName attributes
2528 * It seems that not all users have the userPrincipalName attribute set
2530 * @param ads connection to ads server
2531 * @param mem_ctx TALLOC_CTX for allocating sid array
2532 * @param msg Results of search
2533 * @return the username
2535 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2541 /* lookup_name() only works on the sAMAccountName to
2542 returning the username portion of userPrincipalName
2543 breaks winbindd_getpwnam() */
2545 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2546 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2551 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2556 * find the update serial number - this is the core of the ldap cache
2557 * @param ads connection to ads server
2558 * @param ads connection to ADS server
2559 * @param usn Pointer to retrieved update serial number
2560 * @return status of search
2562 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2564 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2568 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2569 if (!ADS_ERR_OK(status
))
2572 if (ads_count_replies(ads
, res
) != 1) {
2573 ads_msgfree(ads
, res
);
2574 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2577 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2578 ads_msgfree(ads
, res
);
2579 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2582 ads_msgfree(ads
, res
);
2586 /* parse a ADS timestring - typical string is
2587 '20020917091222.0Z0' which means 09:12.22 17th September
2589 static time_t ads_parse_time(const char *str
)
2595 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2596 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2597 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2606 /********************************************************************
2607 ********************************************************************/
2609 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2611 const char *attrs
[] = {"currentTime", NULL
};
2616 ADS_STRUCT
*ads_s
= ads
;
2618 if (!(ctx
= talloc_init("ads_current_time"))) {
2619 return ADS_ERROR(LDAP_NO_MEMORY
);
2622 /* establish a new ldap tcp session if necessary */
2624 if ( !ads
->ldap
.ld
) {
2625 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2626 ads
->server
.ldap_server
)) == NULL
)
2630 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2631 status
= ads_connect( ads_s
);
2632 if ( !ADS_ERR_OK(status
))
2636 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2637 if (!ADS_ERR_OK(status
)) {
2641 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2643 ads_msgfree(ads_s
, res
);
2644 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2648 /* but save the time and offset in the original ADS_STRUCT */
2650 ads
->config
.current_time
= ads_parse_time(timestr
);
2652 if (ads
->config
.current_time
!= 0) {
2653 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2654 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2657 ads_msgfree(ads
, res
);
2659 status
= ADS_SUCCESS
;
2662 /* free any temporary ads connections */
2663 if ( ads_s
!= ads
) {
2664 ads_destroy( &ads_s
);
2666 talloc_destroy(ctx
);
2671 /********************************************************************
2672 ********************************************************************/
2674 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2676 const char *attrs
[] = {"domainFunctionality", NULL
};
2679 ADS_STRUCT
*ads_s
= ads
;
2681 *val
= DS_DOMAIN_FUNCTION_2000
;
2683 /* establish a new ldap tcp session if necessary */
2685 if ( !ads
->ldap
.ld
) {
2686 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2687 ads
->server
.ldap_server
)) == NULL
)
2689 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2692 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2693 status
= ads_connect( ads_s
);
2694 if ( !ADS_ERR_OK(status
))
2698 /* If the attribute does not exist assume it is a Windows 2000
2699 functional domain */
2701 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2702 if (!ADS_ERR_OK(status
)) {
2703 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2704 status
= ADS_SUCCESS
;
2709 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2710 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2712 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2715 ads_msgfree(ads
, res
);
2718 /* free any temporary ads connections */
2719 if ( ads_s
!= ads
) {
2720 ads_destroy( &ads_s
);
2727 * find the domain sid for our domain
2728 * @param ads connection to ads server
2729 * @param sid Pointer to domain sid
2730 * @return status of search
2732 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2734 const char *attrs
[] = {"objectSid", NULL
};
2738 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2740 if (!ADS_ERR_OK(rc
)) return rc
;
2741 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2742 ads_msgfree(ads
, res
);
2743 return ADS_ERROR_SYSTEM(ENOENT
);
2745 ads_msgfree(ads
, res
);
2751 * find our site name
2752 * @param ads connection to ads server
2753 * @param mem_ctx Pointer to talloc context
2754 * @param site_name Pointer to the sitename
2755 * @return status of search
2757 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2761 const char *dn
, *service_name
;
2762 const char *attrs
[] = { "dsServiceName", NULL
};
2764 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2765 if (!ADS_ERR_OK(status
)) {
2769 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2770 if (service_name
== NULL
) {
2771 ads_msgfree(ads
, res
);
2772 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2775 ads_msgfree(ads
, res
);
2777 /* go up three levels */
2778 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2780 return ADS_ERROR(LDAP_NO_MEMORY
);
2783 *site_name
= talloc_strdup(mem_ctx
, dn
);
2784 if (*site_name
== NULL
) {
2785 return ADS_ERROR(LDAP_NO_MEMORY
);
2790 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2795 * find the site dn where a machine resides
2796 * @param ads connection to ads server
2797 * @param mem_ctx Pointer to talloc context
2798 * @param computer_name name of the machine
2799 * @param site_name Pointer to the sitename
2800 * @return status of search
2802 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2806 const char *parent
, *filter
;
2807 char *config_context
= NULL
;
2810 /* shortcut a query */
2811 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2812 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2815 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2816 if (!ADS_ERR_OK(status
)) {
2820 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2821 if (filter
== NULL
) {
2822 return ADS_ERROR(LDAP_NO_MEMORY
);
2825 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2826 filter
, NULL
, &res
);
2827 if (!ADS_ERR_OK(status
)) {
2831 if (ads_count_replies(ads
, res
) != 1) {
2832 ads_msgfree(ads
, res
);
2833 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2836 dn
= ads_get_dn(ads
, res
);
2838 ads_msgfree(ads
, res
);
2839 return ADS_ERROR(LDAP_NO_MEMORY
);
2842 /* go up three levels */
2843 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2844 if (parent
== NULL
) {
2845 ads_msgfree(ads
, res
);
2846 ads_memfree(ads
, dn
);
2847 return ADS_ERROR(LDAP_NO_MEMORY
);
2850 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2851 if (*site_dn
== NULL
) {
2852 ads_msgfree(ads
, res
);
2853 ads_memfree(ads
, dn
);
2854 return ADS_ERROR(LDAP_NO_MEMORY
);
2857 ads_memfree(ads
, dn
);
2858 ads_msgfree(ads
, res
);
2864 * get the upn suffixes for a domain
2865 * @param ads connection to ads server
2866 * @param mem_ctx Pointer to talloc context
2867 * @param suffixes Pointer to an array of suffixes
2868 * @param num_suffixes Pointer to the number of suffixes
2869 * @return status of search
2871 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
2876 char *config_context
= NULL
;
2877 const char *attrs
[] = { "uPNSuffixes", NULL
};
2879 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2880 if (!ADS_ERR_OK(status
)) {
2884 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2886 return ADS_ERROR(LDAP_NO_MEMORY
);
2889 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2890 if (!ADS_ERR_OK(status
)) {
2894 if (ads_count_replies(ads
, res
) != 1) {
2895 ads_msgfree(ads
, res
);
2896 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2899 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2900 if ((*suffixes
) == NULL
) {
2901 ads_msgfree(ads
, res
);
2902 return ADS_ERROR(LDAP_NO_MEMORY
);
2905 ads_msgfree(ads
, res
);
2911 * get the joinable ous for a domain
2912 * @param ads connection to ads server
2913 * @param mem_ctx Pointer to talloc context
2914 * @param ous Pointer to an array of ous
2915 * @param num_ous Pointer to the number of ous
2916 * @return status of search
2918 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
2919 TALLOC_CTX
*mem_ctx
,
2924 LDAPMessage
*res
= NULL
;
2925 LDAPMessage
*msg
= NULL
;
2926 const char *attrs
[] = { "dn", NULL
};
2929 status
= ads_search(ads
, &res
,
2930 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2932 if (!ADS_ERR_OK(status
)) {
2936 count
= ads_count_replies(ads
, res
);
2938 ads_msgfree(ads
, res
);
2939 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2942 for (msg
= ads_first_entry(ads
, res
); msg
;
2943 msg
= ads_next_entry(ads
, msg
)) {
2947 dn
= ads_get_dn(ads
, msg
);
2949 ads_msgfree(ads
, res
);
2950 return ADS_ERROR(LDAP_NO_MEMORY
);
2953 if (!add_string_to_array(mem_ctx
, dn
,
2954 (const char ***)ous
,
2956 ads_memfree(ads
, dn
);
2957 ads_msgfree(ads
, res
);
2958 return ADS_ERROR(LDAP_NO_MEMORY
);
2961 ads_memfree(ads
, dn
);
2964 ads_msgfree(ads
, res
);
2971 * pull a DOM_SID from an extended dn string
2972 * @param mem_ctx TALLOC_CTX
2973 * @param extended_dn string
2974 * @param flags string type of extended_dn
2975 * @param sid pointer to a DOM_SID
2976 * @return NT_STATUS_OK on success,
2977 * NT_INVALID_PARAMETER on error,
2978 * NT_STATUS_NOT_FOUND if no SID present
2980 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2981 const char *extended_dn
,
2982 enum ads_extended_dn_flags flags
,
2988 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
2991 /* otherwise extended_dn gets stripped off */
2992 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
2993 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
2996 * ADS_EXTENDED_DN_HEX_STRING:
2997 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2999 * ADS_EXTENDED_DN_STRING (only with w2k3):
3000 * <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
3002 * Object with no SID, such as an Exchange Public Folder
3003 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3006 p
= strchr(dn
, ';');
3008 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3011 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3012 DEBUG(5,("No SID present in extended dn\n"));
3013 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3016 p
+= strlen(";<SID=");
3020 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3025 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3029 case ADS_EXTENDED_DN_STRING
:
3030 if (!string_to_sid(sid
, p
)) {
3031 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3034 case ADS_EXTENDED_DN_HEX_STRING
: {
3038 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3040 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3043 if (!sid_parse(buf
, buf_len
, sid
)) {
3044 DEBUG(10,("failed to parse sid\n"));
3045 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3050 DEBUG(10,("unknown extended dn format\n"));
3051 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3054 return ADS_ERROR_NT(NT_STATUS_OK
);
3058 * pull an array of DOM_SIDs from a ADS result
3059 * @param ads connection to ads server
3060 * @param mem_ctx TALLOC_CTX for allocating sid array
3061 * @param msg Results of search
3062 * @param field Attribute to retrieve
3063 * @param flags string type of extended_dn
3064 * @param sids pointer to sid array to allocate
3065 * @return the count of SIDs pulled
3067 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
3068 TALLOC_CTX
*mem_ctx
,
3071 enum ads_extended_dn_flags flags
,
3076 size_t dn_count
, ret_count
= 0;
3079 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
3080 &dn_count
)) == NULL
) {
3084 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
3086 TALLOC_FREE(dn_strings
);
3090 for (i
=0; i
<dn_count
; i
++) {
3091 rc
= ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3092 flags
, &(*sids
)[i
]);
3093 if (!ADS_ERR_OK(rc
)) {
3094 if (NT_STATUS_EQUAL(ads_ntstatus(rc
),
3095 NT_STATUS_NOT_FOUND
)) {
3100 TALLOC_FREE(dn_strings
);
3107 TALLOC_FREE(dn_strings
);
3112 /********************************************************************
3113 ********************************************************************/
3115 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3117 LDAPMessage
*res
= NULL
;
3122 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3123 if (!ADS_ERR_OK(status
)) {
3124 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3129 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3130 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3134 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3135 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3139 ads_msgfree(ads
, res
);
3144 /********************************************************************
3145 ********************************************************************/
3147 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3149 LDAPMessage
*res
= NULL
;
3154 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3155 if (!ADS_ERR_OK(status
)) {
3156 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3161 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3162 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3166 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3167 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3171 ads_msgfree(ads
, res
);
3176 /********************************************************************
3177 ********************************************************************/
3179 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3181 LDAPMessage
*res
= NULL
;
3186 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3187 if (!ADS_ERR_OK(status
)) {
3188 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3193 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3194 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3198 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3199 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3203 ads_msgfree(ads
, res
);
3210 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3213 * Join a machine to a realm
3214 * Creates the machine account and sets the machine password
3215 * @param ads connection to ads server
3216 * @param machine name of host to add
3217 * @param org_unit Organizational unit to place machine in
3218 * @return status of join
3220 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3221 uint32 account_type
, const char *org_unit
)
3224 LDAPMessage
*res
= NULL
;
3227 /* machine name must be lowercase */
3228 machine
= SMB_STRDUP(machine_name
);
3229 strlower_m(machine
);
3232 status = ads_find_machine_acct(ads, (void **)&res, machine);
3233 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3234 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3235 status = ads_leave_realm(ads, machine);
3236 if (!ADS_ERR_OK(status)) {
3237 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3238 machine, ads->config.realm));
3243 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3244 if (!ADS_ERR_OK(status
)) {
3245 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3250 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3251 if (!ADS_ERR_OK(status
)) {
3252 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3258 ads_msgfree(ads
, res
);
3265 * Delete a machine from the realm
3266 * @param ads connection to ads server
3267 * @param hostname Machine to remove
3268 * @return status of delete
3270 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3275 char *hostnameDN
, *host
;
3277 LDAPControl ldap_control
;
3278 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3280 pldap_control
[0] = &ldap_control
;
3281 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3282 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3284 /* hostname must be lowercase */
3285 host
= SMB_STRDUP(hostname
);
3288 status
= ads_find_machine_acct(ads
, &res
, host
);
3289 if (!ADS_ERR_OK(status
)) {
3290 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3295 msg
= ads_first_entry(ads
, res
);
3298 return ADS_ERROR_SYSTEM(ENOENT
);
3301 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3303 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3305 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3307 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3310 if (rc
!= LDAP_SUCCESS
) {
3311 const char *attrs
[] = { "cn", NULL
};
3312 LDAPMessage
*msg_sub
;
3314 /* we only search with scope ONE, we do not expect any further
3315 * objects to be created deeper */
3317 status
= ads_do_search_retry(ads
, hostnameDN
,
3318 LDAP_SCOPE_ONELEVEL
,
3319 "(objectclass=*)", attrs
, &res
);
3321 if (!ADS_ERR_OK(status
)) {
3323 ads_memfree(ads
, hostnameDN
);
3327 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3328 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3332 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3334 ads_memfree(ads
, hostnameDN
);
3335 return ADS_ERROR(LDAP_NO_MEMORY
);
3338 status
= ads_del_dn(ads
, dn
);
3339 if (!ADS_ERR_OK(status
)) {
3340 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3342 ads_memfree(ads
, dn
);
3343 ads_memfree(ads
, hostnameDN
);
3347 ads_memfree(ads
, dn
);
3350 /* there should be no subordinate objects anymore */
3351 status
= ads_do_search_retry(ads
, hostnameDN
,
3352 LDAP_SCOPE_ONELEVEL
,
3353 "(objectclass=*)", attrs
, &res
);
3355 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3357 ads_memfree(ads
, hostnameDN
);
3361 /* delete hostnameDN now */
3362 status
= ads_del_dn(ads
, hostnameDN
);
3363 if (!ADS_ERR_OK(status
)) {
3365 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3366 ads_memfree(ads
, hostnameDN
);
3371 ads_memfree(ads
, hostnameDN
);
3373 status
= ads_find_machine_acct(ads
, &res
, host
);
3374 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3375 DEBUG(3, ("Failed to remove host account.\n"));
3385 * pull all token-sids from an LDAP dn
3386 * @param ads connection to ads server
3387 * @param mem_ctx TALLOC_CTX for allocating sid array
3388 * @param dn of LDAP object
3389 * @param user_sid pointer to DOM_SID (objectSid)
3390 * @param primary_group_sid pointer to DOM_SID (self composed)
3391 * @param sids pointer to sid array to allocate
3392 * @param num_sids counter of SIDs pulled
3393 * @return status of token query
3395 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3396 TALLOC_CTX
*mem_ctx
,
3399 DOM_SID
*primary_group_sid
,
3404 LDAPMessage
*res
= NULL
;
3406 size_t tmp_num_sids
;
3408 DOM_SID tmp_user_sid
;
3409 DOM_SID tmp_primary_group_sid
;
3411 const char *attrs
[] = {
3418 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3419 if (!ADS_ERR_OK(status
)) {
3423 count
= ads_count_replies(ads
, res
);
3425 ads_msgfree(ads
, res
);
3426 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3429 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3430 ads_msgfree(ads
, res
);
3431 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3434 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3435 ads_msgfree(ads
, res
);
3436 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3440 /* hack to compose the primary group sid without knowing the
3446 sid_copy(&domsid
, &tmp_user_sid
);
3448 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3449 ads_msgfree(ads
, res
);
3450 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3453 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3454 ads_msgfree(ads
, res
);
3455 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3459 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3461 if (tmp_num_sids
== 0 || !tmp_sids
) {
3462 ads_msgfree(ads
, res
);
3463 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3467 *num_sids
= tmp_num_sids
;
3475 *user_sid
= tmp_user_sid
;
3478 if (primary_group_sid
) {
3479 *primary_group_sid
= tmp_primary_group_sid
;
3482 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3484 ads_msgfree(ads
, res
);
3485 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3489 * Find a sAMAccoutName in LDAP
3490 * @param ads connection to ads server
3491 * @param mem_ctx TALLOC_CTX for allocating sid array
3492 * @param samaccountname to search
3493 * @param uac_ret uint32 pointer userAccountControl attribute value
3494 * @param dn_ret pointer to dn
3495 * @return status of token query
3497 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3498 TALLOC_CTX
*mem_ctx
,
3499 const char *samaccountname
,
3501 const char **dn_ret
)
3504 const char *attrs
[] = { "userAccountControl", NULL
};
3506 LDAPMessage
*res
= NULL
;
3510 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3512 if (filter
== NULL
) {
3513 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3517 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3519 filter
, attrs
, &res
);
3521 if (!ADS_ERR_OK(status
)) {
3525 if (ads_count_replies(ads
, res
) != 1) {
3526 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3530 dn
= ads_get_dn(ads
, res
);
3532 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3536 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3537 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3546 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3548 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3553 ads_memfree(ads
, dn
);
3554 ads_msgfree(ads
, res
);
3560 * find our configuration path
3561 * @param ads connection to ads server
3562 * @param mem_ctx Pointer to talloc context
3563 * @param config_path Pointer to the config path
3564 * @return status of search
3566 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3567 TALLOC_CTX
*mem_ctx
,
3571 LDAPMessage
*res
= NULL
;
3572 const char *config_context
= NULL
;
3573 const char *attrs
[] = { "configurationNamingContext", NULL
};
3575 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3576 "(objectclass=*)", attrs
, &res
);
3577 if (!ADS_ERR_OK(status
)) {
3581 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3582 "configurationNamingContext");
3583 ads_msgfree(ads
, res
);
3584 if (!config_context
) {
3585 return ADS_ERROR(LDAP_NO_MEMORY
);
3589 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3590 if (!*config_path
) {
3591 return ADS_ERROR(LDAP_NO_MEMORY
);
3595 return ADS_ERROR(LDAP_SUCCESS
);
3599 * find the displayName of an extended right
3600 * @param ads connection to ads server
3601 * @param config_path The config path
3602 * @param mem_ctx Pointer to talloc context
3603 * @param GUID struct of the rightsGUID
3604 * @return status of search
3606 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3607 const char *config_path
,
3608 TALLOC_CTX
*mem_ctx
,
3609 const struct GUID
*rights_guid
)
3612 LDAPMessage
*res
= NULL
;
3614 const char *attrs
[] = { "displayName", NULL
};
3615 const char *result
= NULL
;
3618 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3622 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3623 smb_uuid_string(mem_ctx
, *rights_guid
));
3628 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3633 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3635 if (!ADS_ERR_OK(rc
)) {
3639 if (ads_count_replies(ads
, res
) != 1) {
3643 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3646 ads_msgfree(ads
, res
);
3652 * verify or build and verify an account ou
3653 * @param mem_ctx Pointer to talloc context
3654 * @param ads connection to ads server
3656 * @return status of search
3659 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3661 const char **account_ou
)
3663 struct ldb_dn
*name_dn
= NULL
;
3664 const char *name
= NULL
;
3665 char *ou_string
= NULL
;
3667 name_dn
= ldb_dn_explode(mem_ctx
, *account_ou
);
3672 ou_string
= ads_ou_string(ads
, *account_ou
);
3674 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3677 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3678 ads
->config
.bind_path
);
3679 SAFE_FREE(ou_string
);
3681 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3684 name_dn
= ldb_dn_explode(mem_ctx
, name
);
3686 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3689 *account_ou
= talloc_strdup(mem_ctx
, name
);
3691 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);