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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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
)
62 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
64 /* End setup timeout. */
66 ldp
= ldap_open(server
, port
);
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server
, port
, strerror(errno
)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
80 static int ldap_search_with_timeout(LDAP
*ld
,
81 LDAP_CONST
char *base
,
83 LDAP_CONST
char *filter
,
91 struct timeval timeout
;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout
.tv_sec
= lp_ldap_timeout();
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
100 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
105 attrsonly
, sctrls
, cctrls
, &timeout
,
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
113 return LDAP_TIMELIMIT_EXCEEDED
;
118 /**********************************************
119 Do client and server sitename match ?
120 **********************************************/
122 BOOL
ads_sitename_match(ADS_STRUCT
*ads
)
124 if (ads
->config
.server_site_name
== NULL
&&
125 ads
->config
.client_site_name
== NULL
) {
126 DEBUG(10,("ads_sitename_match: both null\n"));
129 if (ads
->config
.server_site_name
&&
130 ads
->config
.client_site_name
&&
131 strequal(ads
->config
.server_site_name
,
132 ads
->config
.client_site_name
)) {
133 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
136 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
137 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
138 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
142 /**********************************************
143 Is this the closest DC ?
144 **********************************************/
146 BOOL
ads_closest_dc(ADS_STRUCT
*ads
)
148 if (ads
->config
.flags
& ADS_CLOSEST
) {
149 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
153 /* not sure if this can ever happen */
154 if (ads_sitename_match(ads
)) {
155 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
159 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
160 ads
->config
.ldap_server_name
));
167 try a connection to a given ldap server, returning True and setting the servers IP
168 in the ads struct if successful
170 BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
173 struct cldap_netlogon_reply cldap_reply
;
175 if (!server
|| !*server
) {
179 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
180 server
, ads
->server
.realm
));
182 /* this copes with inet_ntoa brokenness */
184 srv
= SMB_STRDUP(server
);
186 ZERO_STRUCT( cldap_reply
);
188 if ( !ads_cldap_netlogon( srv
, ads
->server
.realm
, &cldap_reply
) ) {
189 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
194 /* Check the CLDAP reply flags */
196 if ( !(cldap_reply
.flags
& ADS_LDAP
) ) {
197 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
203 /* Fill in the ads->config values */
205 SAFE_FREE(ads
->config
.realm
);
206 SAFE_FREE(ads
->config
.bind_path
);
207 SAFE_FREE(ads
->config
.ldap_server_name
);
208 SAFE_FREE(ads
->config
.server_site_name
);
209 SAFE_FREE(ads
->config
.client_site_name
);
210 SAFE_FREE(ads
->server
.workgroup
);
212 ads
->config
.flags
= cldap_reply
.flags
;
213 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.hostname
);
214 strupper_m(cldap_reply
.domain
);
215 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.domain
);
216 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
217 if (*cldap_reply
.server_site_name
) {
218 ads
->config
.server_site_name
=
219 SMB_STRDUP(cldap_reply
.server_site_name
);
221 if (*cldap_reply
.client_site_name
) {
222 ads
->config
.client_site_name
=
223 SMB_STRDUP(cldap_reply
.client_site_name
);
226 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.netbios_domain
);
228 ads
->ldap_port
= LDAP_PORT
;
229 ads
->ldap_ip
= *interpret_addr2(srv
);
232 /* Store our site name. */
233 sitename_store( cldap_reply
.client_site_name
);
238 /**********************************************************************
239 Try to find an AD dc using our internal name resolution routines
240 Try the realm first and then then workgroup name if netbios is not
242 **********************************************************************/
244 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
248 struct ip_service
*ip_list
;
250 BOOL got_realm
= False
;
251 BOOL use_own_domain
= False
;
252 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
254 /* if the realm and workgroup are both empty, assume they are ours */
257 c_realm
= ads
->server
.realm
;
259 if ( !c_realm
|| !*c_realm
) {
260 /* special case where no realm and no workgroup means our own */
261 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
262 use_own_domain
= True
;
263 c_realm
= lp_realm();
267 if (c_realm
&& *c_realm
)
271 /* we need to try once with the realm name and fallback to the
272 netbios domain name if we fail (if netbios has not been disabled */
274 if ( !got_realm
&& !lp_disable_netbios() ) {
275 c_realm
= ads
->server
.workgroup
;
276 if (!c_realm
|| !*c_realm
) {
277 if ( use_own_domain
)
278 c_realm
= lp_workgroup();
281 if ( !c_realm
|| !*c_realm
) {
282 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
283 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
287 pstrcpy( realm
, c_realm
);
289 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
290 (got_realm
? "realm" : "domain"), realm
));
292 status
= get_sorted_dc_list(realm
, &ip_list
, &count
, got_realm
);
293 if (!NT_STATUS_IS_OK(status
)) {
294 /* fall back to netbios if we can */
295 if ( got_realm
&& !lp_disable_netbios() ) {
303 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
304 for ( i
=0; i
<count
; i
++ ) {
307 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
309 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
313 /* realm in this case is a workgroup name. We need
314 to ignore any IP addresses in the negative connection
315 cache that match ip addresses returned in the ad realm
316 case. It sucks that I have to reproduce the logic above... */
317 c_realm
= ads
->server
.realm
;
318 if ( !c_realm
|| !*c_realm
) {
319 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
320 c_realm
= lp_realm();
323 if (c_realm
&& *c_realm
&&
324 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
325 /* Ensure we add the workgroup name for this
326 IP address as negative too. */
327 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
332 if ( ads_try_connect(ads
, server
) ) {
337 /* keep track of failures */
338 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
343 return NT_STATUS_NO_LOGON_SERVERS
;
348 * Connect to the LDAP server
349 * @param ads Pointer to an existing ADS_STRUCT
350 * @return status of connection
352 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
354 int version
= LDAP_VERSION3
;
358 ads
->last_attempt
= time(NULL
);
361 /* try with a user specified server */
363 if (ads
->server
.ldap_server
&&
364 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
368 ntstatus
= ads_find_dc(ads
);
369 if (NT_STATUS_IS_OK(ntstatus
)) {
373 return ADS_ERROR_NT(ntstatus
);
376 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
378 if (!ads
->auth
.user_name
) {
379 /* Must use the userPrincipalName value here or sAMAccountName
380 and not servicePrincipalName; found by Guenther Deschner */
382 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
385 if (!ads
->auth
.realm
) {
386 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
389 if (!ads
->auth
.kdc_server
) {
390 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap_ip
));
394 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
395 to MIT kerberos to work (tridge) */
398 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
399 setenv(env
, ads
->auth
.kdc_server
, 1);
404 /* If the caller() requested no LDAP bind, then we are done */
406 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
410 /* Otherwise setup the TCP LDAP session */
412 if ( (ads
->ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
413 LDAP_PORT
, lp_ldap_timeout())) == NULL
)
415 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
418 /* cache the successful connection for workgroup and realm */
419 if (ads_closest_dc(ads
)) {
420 saf_store( ads
->server
.workgroup
, inet_ntoa(ads
->ldap_ip
));
421 saf_store( ads
->server
.realm
, inet_ntoa(ads
->ldap_ip
));
424 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
426 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ld
, version
));
427 if (!ADS_ERR_OK(status
)) {
431 /* fill in the current time and offsets */
433 status
= ads_current_time( ads
);
434 if ( !ADS_ERR_OK(status
) ) {
438 /* Now do the bind */
440 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
441 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
444 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
445 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
448 return ads_sasl_bind(ads
);
452 Duplicate a struct berval into talloc'ed memory
454 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
456 struct berval
*value
;
458 if (!in_val
) return NULL
;
460 value
= TALLOC_ZERO_P(ctx
, struct berval
);
463 if (in_val
->bv_len
== 0) return value
;
465 value
->bv_len
= in_val
->bv_len
;
466 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
472 Make a values list out of an array of (struct berval *)
474 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
475 const struct berval
**in_vals
)
477 struct berval
**values
;
480 if (!in_vals
) return NULL
;
481 for (i
=0; in_vals
[i
]; i
++)
483 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
484 if (!values
) return NULL
;
486 for (i
=0; in_vals
[i
]; i
++) {
487 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
493 UTF8-encode a values list out of an array of (char *)
495 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
500 if (!in_vals
) return NULL
;
501 for (i
=0; in_vals
[i
]; i
++)
503 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
504 if (!values
) return NULL
;
506 for (i
=0; in_vals
[i
]; i
++) {
507 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
513 Pull a (char *) array out of a UTF8-encoded values list
515 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
520 if (!in_vals
) return NULL
;
521 for (i
=0; in_vals
[i
]; i
++)
523 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
524 if (!values
) return NULL
;
526 for (i
=0; in_vals
[i
]; i
++) {
527 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
533 * Do a search with paged results. cookie must be null on the first
534 * call, and then returned on each subsequent call. It will be null
535 * again when the entire search is complete
536 * @param ads connection to ads server
537 * @param bind_path Base dn for the search
538 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
539 * @param expr Search expression - specified in local charset
540 * @param attrs Attributes to retrieve - specified in utf8 or ascii
541 * @param res ** which will contain results - free res* with ads_msgfree()
542 * @param count Number of entries retrieved on this page
543 * @param cookie The paged results cookie to be returned on subsequent calls
544 * @return status of search
546 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
547 const char *bind_path
,
548 int scope
, const char *expr
,
549 const char **attrs
, void *args
,
551 int *count
, struct berval
**cookie
)
554 char *utf8_expr
, *utf8_path
, **search_attrs
;
555 LDAPControl PagedResults
, NoReferrals
, ExtendedDn
, *controls
[4], **rcontrols
;
556 BerElement
*cookie_be
= NULL
;
557 struct berval
*cookie_bv
= NULL
;
558 BerElement
*extdn_be
= NULL
;
559 struct berval
*extdn_bv
= NULL
;
562 ads_control
*external_control
= (ads_control
*) args
;
566 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
567 return ADS_ERROR(LDAP_NO_MEMORY
);
569 /* 0 means the conversion worked but the result was empty
570 so we only fail if it's -1. In any case, it always
571 at least nulls out the dest */
572 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
573 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
578 if (!attrs
|| !(*attrs
))
581 /* This would be the utf8-encoded version...*/
582 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
583 if (!(str_list_copy(&search_attrs
, attrs
))) {
590 /* Paged results only available on ldap v3 or later */
591 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
592 if (version
< LDAP_VERSION3
) {
593 rc
= LDAP_NOT_SUPPORTED
;
597 cookie_be
= ber_alloc_t(LBER_USE_DER
);
599 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
600 ber_bvfree(*cookie
); /* don't need it from last time */
603 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
605 ber_flatten(cookie_be
, &cookie_bv
);
606 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
607 PagedResults
.ldctl_iscritical
= (char) 1;
608 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
609 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
611 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
612 NoReferrals
.ldctl_iscritical
= (char) 0;
613 NoReferrals
.ldctl_value
.bv_len
= 0;
614 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
616 if (external_control
&& strequal(external_control
->control
, ADS_EXTENDED_DN_OID
)) {
618 ExtendedDn
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
619 ExtendedDn
.ldctl_iscritical
= (char) external_control
->critical
;
621 /* win2k does not accept a ldctl_value beeing passed in */
623 if (external_control
->val
!= 0) {
625 if ((extdn_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
630 if ((ber_printf(extdn_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
634 if ((ber_flatten(extdn_be
, &extdn_bv
)) == -1) {
639 ExtendedDn
.ldctl_value
.bv_len
= extdn_bv
->bv_len
;
640 ExtendedDn
.ldctl_value
.bv_val
= extdn_bv
->bv_val
;
643 ExtendedDn
.ldctl_value
.bv_len
= 0;
644 ExtendedDn
.ldctl_value
.bv_val
= NULL
;
647 controls
[0] = &NoReferrals
;
648 controls
[1] = &PagedResults
;
649 controls
[2] = &ExtendedDn
;
653 controls
[0] = &NoReferrals
;
654 controls
[1] = &PagedResults
;
658 /* we need to disable referrals as the openldap libs don't
659 handle them and paged results at the same time. Using them
660 together results in the result record containing the server
661 page control being removed from the result list (tridge/jmcd)
663 leaving this in despite the control that says don't generate
664 referrals, in case the server doesn't support it (jmcd)
666 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
668 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
669 search_attrs
, 0, controls
,
671 (LDAPMessage
**)res
);
673 ber_free(cookie_be
, 1);
674 ber_bvfree(cookie_bv
);
677 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
678 ldap_err2string(rc
)));
682 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
683 NULL
, &rcontrols
, 0);
689 for (i
=0; rcontrols
[i
]; i
++) {
690 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
691 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
692 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
694 /* the berval is the cookie, but must be freed when
696 if (cookie_bv
->bv_len
) /* still more to do */
697 *cookie
=ber_bvdup(cookie_bv
);
700 ber_bvfree(cookie_bv
);
701 ber_free(cookie_be
, 1);
705 ldap_controls_free(rcontrols
);
711 ber_free(extdn_be
, 1);
715 ber_bvfree(extdn_bv
);
718 /* if/when we decide to utf8-encode attrs, take out this next line */
719 str_list_free(&search_attrs
);
721 return ADS_ERROR(rc
);
724 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
725 int scope
, const char *expr
,
726 const char **attrs
, LDAPMessage
**res
,
727 int *count
, struct berval
**cookie
)
729 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
734 * Get all results for a search. This uses ads_do_paged_search() to return
735 * all entries in a large search.
736 * @param ads connection to ads server
737 * @param bind_path Base dn for the search
738 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
739 * @param expr Search expression
740 * @param attrs Attributes to retrieve
741 * @param res ** which will contain results - free res* with ads_msgfree()
742 * @return status of search
744 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
745 int scope
, const char *expr
,
746 const char **attrs
, void *args
,
749 struct berval
*cookie
= NULL
;
754 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
757 if (!ADS_ERR_OK(status
))
760 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
762 LDAPMessage
*res2
= NULL
;
764 LDAPMessage
*msg
, *next
;
766 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
767 attrs
, args
, &res2
, &count
, &cookie
);
769 if (!ADS_ERR_OK(status2
)) break;
771 /* this relies on the way that ldap_add_result_entry() works internally. I hope
772 that this works on all ldap libs, but I have only tested with openldap */
773 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
774 next
= ads_next_entry(ads
, msg
);
775 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
777 /* note that we do not free res2, as the memory is now
778 part of the main returned list */
781 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
782 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
788 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
789 int scope
, const char *expr
,
790 const char **attrs
, LDAPMessage
**res
)
792 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
796 * Run a function on all results for a search. Uses ads_do_paged_search() and
797 * runs the function as each page is returned, using ads_process_results()
798 * @param ads connection to ads server
799 * @param bind_path Base dn for the search
800 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
801 * @param expr Search expression - specified in local charset
802 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
803 * @param fn Function which takes attr name, values list, and data_area
804 * @param data_area Pointer which is passed to function on each call
805 * @return status of search
807 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
808 int scope
, const char *expr
, const char **attrs
,
809 BOOL(*fn
)(char *, void **, void *),
812 struct berval
*cookie
= NULL
;
817 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
820 if (!ADS_ERR_OK(status
)) return status
;
822 ads_process_results(ads
, res
, fn
, data_area
);
823 ads_msgfree(ads
, res
);
826 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
827 &res
, &count
, &cookie
);
829 if (!ADS_ERR_OK(status
)) break;
831 ads_process_results(ads
, res
, fn
, data_area
);
832 ads_msgfree(ads
, res
);
839 * Do a search with a timeout.
840 * @param ads connection to ads server
841 * @param bind_path Base dn for the search
842 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
843 * @param expr Search expression
844 * @param attrs Attributes to retrieve
845 * @param res ** which will contain results - free res* with ads_msgfree()
846 * @return status of search
848 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
850 const char **attrs
, LDAPMessage
**res
)
853 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
857 if (!(ctx
= talloc_init("ads_do_search"))) {
858 DEBUG(1,("ads_do_search: talloc_init() failed!"));
859 return ADS_ERROR(LDAP_NO_MEMORY
);
862 /* 0 means the conversion worked but the result was empty
863 so we only fail if it's negative. In any case, it always
864 at least nulls out the dest */
865 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
866 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
867 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
872 if (!attrs
|| !(*attrs
))
875 /* This would be the utf8-encoded version...*/
876 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
877 if (!(str_list_copy(&search_attrs
, attrs
)))
879 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
885 /* see the note in ads_do_paged_search - we *must* disable referrals */
886 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
888 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
889 search_attrs
, 0, NULL
, NULL
,
891 (LDAPMessage
**)res
);
893 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
894 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
900 /* if/when we decide to utf8-encode attrs, take out this next line */
901 str_list_free(&search_attrs
);
902 return ADS_ERROR(rc
);
905 * Do a general ADS search
906 * @param ads connection to ads server
907 * @param res ** which will contain results - free res* with ads_msgfree()
908 * @param expr Search expression
909 * @param attrs Attributes to retrieve
910 * @return status of search
912 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
913 const char *expr
, const char **attrs
)
915 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
920 * Do a search on a specific DistinguishedName
921 * @param ads connection to ads server
922 * @param res ** which will contain results - free res* with ads_msgfree()
923 * @param dn DistinguishName to search
924 * @param attrs Attributes to retrieve
925 * @return status of search
927 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
928 const char *dn
, const char **attrs
)
930 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
935 * Free up memory from a ads_search
936 * @param ads connection to ads server
937 * @param msg Search results to free
939 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
946 * Free up memory from various ads requests
947 * @param ads connection to ads server
948 * @param mem Area to free
950 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
956 * Get a dn from search results
957 * @param ads connection to ads server
958 * @param msg Search result
961 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
963 char *utf8_dn
, *unix_dn
;
965 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
968 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
972 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
973 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
977 ldap_memfree(utf8_dn
);
982 * Get the parent from a dn
983 * @param dn the dn to return the parent from
984 * @return parent dn string
986 char *ads_parent_dn(const char *dn
)
1004 * Find a machine account given a hostname
1005 * @param ads connection to ads server
1006 * @param res ** which will contain results - free res* with ads_msgfree()
1007 * @param host Hostname to search for
1008 * @return status of search
1010 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1011 const char *machine
)
1015 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1019 /* the easiest way to find a machine account anywhere in the tree
1020 is to look for hostname$ */
1021 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1022 DEBUG(1, ("asprintf failed!\n"));
1023 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1026 status
= ads_search(ads
, res
, expr
, attrs
);
1032 * Initialize a list of mods to be used in a modify request
1033 * @param ctx An initialized TALLOC_CTX
1034 * @return allocated ADS_MODLIST
1036 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1038 #define ADS_MODLIST_ALLOC_SIZE 10
1041 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1042 /* -1 is safety to make sure we don't go over the end.
1043 need to reset it to NULL before doing ldap modify */
1044 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1046 return (ADS_MODLIST
)mods
;
1051 add an attribute to the list, with values list already constructed
1053 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1054 int mod_op
, const char *name
,
1055 const void *_invals
)
1057 const void **invals
= (const void **)_invals
;
1059 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1060 struct berval
**ber_values
= NULL
;
1061 char **char_values
= NULL
;
1064 mod_op
= LDAP_MOD_DELETE
;
1066 if (mod_op
& LDAP_MOD_BVALUES
)
1067 ber_values
= ads_dup_values(ctx
,
1068 (const struct berval
**)invals
);
1070 char_values
= ads_push_strvals(ctx
,
1071 (const char **) invals
);
1074 /* find the first empty slot */
1075 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1077 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1078 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1079 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1080 return ADS_ERROR(LDAP_NO_MEMORY
);
1081 memset(&modlist
[curmod
], 0,
1082 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1083 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1084 *mods
= (ADS_MODLIST
)modlist
;
1087 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1088 return ADS_ERROR(LDAP_NO_MEMORY
);
1089 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1090 if (mod_op
& LDAP_MOD_BVALUES
) {
1091 modlist
[curmod
]->mod_bvalues
= ber_values
;
1092 } else if (mod_op
& LDAP_MOD_DELETE
) {
1093 modlist
[curmod
]->mod_values
= NULL
;
1095 modlist
[curmod
]->mod_values
= char_values
;
1098 modlist
[curmod
]->mod_op
= mod_op
;
1099 return ADS_ERROR(LDAP_SUCCESS
);
1103 * Add a single string value to a mod list
1104 * @param ctx An initialized TALLOC_CTX
1105 * @param mods An initialized ADS_MODLIST
1106 * @param name The attribute name to add
1107 * @param val The value to add - NULL means DELETE
1108 * @return ADS STATUS indicating success of add
1110 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1111 const char *name
, const char *val
)
1113 const char *values
[2];
1119 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1120 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1124 * Add an array of string values to a mod list
1125 * @param ctx An initialized TALLOC_CTX
1126 * @param mods An initialized ADS_MODLIST
1127 * @param name The attribute name to add
1128 * @param vals The array of string values to add - NULL means DELETE
1129 * @return ADS STATUS indicating success of add
1131 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1132 const char *name
, const char **vals
)
1135 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1136 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1137 name
, (const void **) vals
);
1142 * Add a single ber-encoded value to a mod list
1143 * @param ctx An initialized TALLOC_CTX
1144 * @param mods An initialized ADS_MODLIST
1145 * @param name The attribute name to add
1146 * @param val The value to add - NULL means DELETE
1147 * @return ADS STATUS indicating success of add
1149 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1150 const char *name
, const struct berval
*val
)
1152 const struct berval
*values
[2];
1157 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1158 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1159 name
, (const void **) values
);
1164 * Perform an ldap modify
1165 * @param ads connection to ads server
1166 * @param mod_dn DistinguishedName to modify
1167 * @param mods list of modifications to perform
1168 * @return status of modify
1170 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1173 char *utf8_dn
= NULL
;
1175 this control is needed to modify that contains a currently
1176 non-existent attribute (but allowable for the object) to run
1178 LDAPControl PermitModify
= {
1179 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1182 LDAPControl
*controls
[2];
1184 controls
[0] = &PermitModify
;
1187 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1188 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1191 /* find the end of the list, marked by NULL or -1 */
1192 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1193 /* make sure the end of the list is NULL */
1195 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
1196 (LDAPMod
**) mods
, controls
, NULL
);
1198 return ADS_ERROR(ret
);
1202 * Perform an ldap add
1203 * @param ads connection to ads server
1204 * @param new_dn DistinguishedName to add
1205 * @param mods list of attributes and values for DN
1206 * @return status of add
1208 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1211 char *utf8_dn
= NULL
;
1213 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1214 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1215 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1218 /* find the end of the list, marked by NULL or -1 */
1219 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1220 /* make sure the end of the list is NULL */
1223 ret
= ldap_add_s(ads
->ld
, utf8_dn
, (LDAPMod
**)mods
);
1225 return ADS_ERROR(ret
);
1229 * Delete a DistinguishedName
1230 * @param ads connection to ads server
1231 * @param new_dn DistinguishedName to delete
1232 * @return status of delete
1234 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1237 char *utf8_dn
= NULL
;
1238 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1239 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1240 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1243 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
1245 return ADS_ERROR(ret
);
1249 * Build an org unit string
1250 * if org unit is Computers or blank then assume a container, otherwise
1251 * assume a / separated list of organisational units.
1252 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1253 * @param ads connection to ads server
1254 * @param org_unit Organizational unit
1255 * @return org unit string - caller must free
1257 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1261 if (!org_unit
|| !*org_unit
) {
1263 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1265 /* samba4 might not yet respond to a wellknownobject-query */
1266 return ret
? ret
: SMB_STRDUP("cn=Computers");
1269 if (strequal(org_unit
, "Computers")) {
1270 return SMB_STRDUP("cn=Computers");
1273 /* jmcd: removed "\\" from the separation chars, because it is
1274 needed as an escape for chars like '#' which are valid in an
1276 return ads_build_path(org_unit
, "/", "ou=", 1);
1280 * Get a org unit string for a well-known GUID
1281 * @param ads connection to ads server
1282 * @param wknguid Well known GUID
1283 * @return org unit string - caller must free
1285 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1288 LDAPMessage
*res
= NULL
;
1289 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1290 **bind_dn_exp
= NULL
;
1291 const char *attrs
[] = {"distinguishedName", NULL
};
1292 int new_ln
, wkn_ln
, bind_ln
, i
;
1294 if (wknguid
== NULL
) {
1298 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1299 DEBUG(1, ("asprintf failed!\n"));
1303 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1304 if (!ADS_ERR_OK(status
)) {
1305 DEBUG(1,("Failed while searching for: %s\n", base
));
1309 if (ads_count_replies(ads
, res
) != 1) {
1313 /* substitute the bind-path from the well-known-guid-search result */
1314 wkn_dn
= ads_get_dn(ads
, res
);
1319 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1324 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1329 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1331 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1334 new_ln
= wkn_ln
- bind_ln
;
1336 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1341 for (i
=1; i
< new_ln
; i
++) {
1344 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1350 ret
= SMB_STRDUP(s
);
1359 ads_msgfree(ads
, res
);
1360 ads_memfree(ads
, wkn_dn
);
1362 ldap_value_free(wkn_dn_exp
);
1365 ldap_value_free(bind_dn_exp
);
1372 * Adds (appends) an item to an attribute array, rather then
1373 * replacing the whole list
1374 * @param ctx An initialized TALLOC_CTX
1375 * @param mods An initialized ADS_MODLIST
1376 * @param name name of the ldap attribute to append to
1377 * @param vals an array of values to add
1378 * @return status of addition
1381 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1382 const char *name
, const char **vals
)
1384 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1385 (const void *) vals
);
1389 * Determines the computer account's current KVNO via an LDAP lookup
1390 * @param ads An initialized ADS_STRUCT
1391 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1392 * @return the kvno for the computer account, or -1 in case of a failure.
1395 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1397 LDAPMessage
*res
= NULL
;
1398 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1400 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1401 char *dn_string
= NULL
;
1402 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1404 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1405 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1408 ret
= ads_search(ads
, &res
, filter
, attrs
);
1410 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1411 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1412 ads_msgfree(ads
, res
);
1416 dn_string
= ads_get_dn(ads
, res
);
1418 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1419 ads_msgfree(ads
, res
);
1422 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1423 ads_memfree(ads
, dn_string
);
1425 /* ---------------------------------------------------------
1426 * 0 is returned as a default KVNO from this point on...
1427 * This is done because Windows 2000 does not support key
1428 * version numbers. Chances are that a failure in the next
1429 * step is simply due to Windows 2000 being used for a
1430 * domain controller. */
1433 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1434 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1435 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1436 ads_msgfree(ads
, res
);
1441 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1442 ads_msgfree(ads
, res
);
1447 * This clears out all registered spn's for a given hostname
1448 * @param ads An initilaized ADS_STRUCT
1449 * @param machine_name the NetBIOS name of the computer.
1450 * @return 0 upon success, non-zero otherwise.
1453 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1456 LDAPMessage
*res
= NULL
;
1458 const char *servicePrincipalName
[1] = {NULL
};
1459 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1460 char *dn_string
= NULL
;
1462 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1463 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1464 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1465 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1466 ads_msgfree(ads
, res
);
1467 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1470 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1471 ctx
= talloc_init("ads_clear_service_principal_names");
1473 ads_msgfree(ads
, res
);
1474 return ADS_ERROR(LDAP_NO_MEMORY
);
1477 if (!(mods
= ads_init_mods(ctx
))) {
1478 talloc_destroy(ctx
);
1479 ads_msgfree(ads
, res
);
1480 return ADS_ERROR(LDAP_NO_MEMORY
);
1482 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1483 if (!ADS_ERR_OK(ret
)) {
1484 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1485 ads_msgfree(ads
, res
);
1486 talloc_destroy(ctx
);
1489 dn_string
= ads_get_dn(ads
, res
);
1491 talloc_destroy(ctx
);
1492 ads_msgfree(ads
, res
);
1493 return ADS_ERROR(LDAP_NO_MEMORY
);
1495 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1496 ads_memfree(ads
,dn_string
);
1497 if (!ADS_ERR_OK(ret
)) {
1498 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1500 ads_msgfree(ads
, res
);
1501 talloc_destroy(ctx
);
1505 ads_msgfree(ads
, res
);
1506 talloc_destroy(ctx
);
1511 * This adds a service principal name to an existing computer account
1512 * (found by hostname) in AD.
1513 * @param ads An initialized ADS_STRUCT
1514 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1515 * @param my_fqdn The fully qualified DNS name of the machine
1516 * @param spn A string of the service principal to add, i.e. 'host'
1517 * @return 0 upon sucess, or non-zero if a failure occurs
1520 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1521 const char *my_fqdn
, const char *spn
)
1525 LDAPMessage
*res
= NULL
;
1528 char *dn_string
= NULL
;
1529 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1531 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1532 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1533 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1535 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1536 spn
, machine_name
, ads
->config
.realm
));
1537 ads_msgfree(ads
, res
);
1538 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1541 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1542 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1543 ads_msgfree(ads
, res
);
1544 return ADS_ERROR(LDAP_NO_MEMORY
);
1547 /* add short name spn */
1549 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1550 talloc_destroy(ctx
);
1551 ads_msgfree(ads
, res
);
1552 return ADS_ERROR(LDAP_NO_MEMORY
);
1555 strlower_m(&psp1
[strlen(spn
)]);
1556 servicePrincipalName
[0] = psp1
;
1558 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1559 psp1
, machine_name
));
1562 /* add fully qualified spn */
1564 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1565 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1569 strlower_m(&psp2
[strlen(spn
)]);
1570 servicePrincipalName
[1] = psp2
;
1572 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1573 psp2
, machine_name
));
1575 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1576 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1580 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1581 if (!ADS_ERR_OK(ret
)) {
1582 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1586 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1587 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1591 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1592 ads_memfree(ads
,dn_string
);
1593 if (!ADS_ERR_OK(ret
)) {
1594 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1600 ads_msgfree(ads
, res
);
1605 * adds a machine account to the ADS server
1606 * @param ads An intialized ADS_STRUCT
1607 * @param machine_name - the NetBIOS machine name of this account.
1608 * @param account_type A number indicating the type of account to create
1609 * @param org_unit The LDAP path in which to place this account
1610 * @return 0 upon success, or non-zero otherwise
1613 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1614 const char *org_unit
)
1617 char *samAccountName
, *controlstr
;
1621 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1622 "user", "computer", NULL
};
1623 LDAPMessage
*res
= NULL
;
1624 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1625 UF_DONT_EXPIRE_PASSWD
|\
1626 UF_ACCOUNTDISABLE
);
1628 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1629 return ADS_ERROR(LDAP_NO_MEMORY
);
1631 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1633 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_name
, org_unit
);
1634 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1636 if ( !new_dn
|| !samAccountName
) {
1640 #ifndef ENCTYPE_ARCFOUR_HMAC
1641 acct_control
|= UF_USE_DES_KEY_ONLY
;
1644 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1648 if (!(mods
= ads_init_mods(ctx
))) {
1652 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1653 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1654 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1655 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1657 ret
= ads_gen_add(ads
, new_dn
, mods
);
1660 ads_msgfree(ads
, res
);
1661 talloc_destroy(ctx
);
1667 dump a binary result from ldap
1669 static void dump_binary(const char *field
, struct berval
**values
)
1672 for (i
=0; values
[i
]; i
++) {
1673 printf("%s: ", field
);
1674 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1675 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1681 static void dump_guid(const char *field
, struct berval
**values
)
1685 for (i
=0; values
[i
]; i
++) {
1686 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1687 printf("%s: %s\n", field
,
1688 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1693 dump a sid result from ldap
1695 static void dump_sid(const char *field
, struct berval
**values
)
1698 for (i
=0; values
[i
]; i
++) {
1700 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1701 printf("%s: %s\n", field
, sid_string_static(&sid
));
1706 dump ntSecurityDescriptor
1708 static void dump_sd(const char *filed
, struct berval
**values
)
1713 TALLOC_CTX
*ctx
= 0;
1715 if (!(ctx
= talloc_init("sec_io_desc")))
1719 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1720 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1721 prs_set_offset(&ps
,0);
1724 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1726 talloc_destroy(ctx
);
1729 if (psd
) ads_disp_sd(psd
);
1732 talloc_destroy(ctx
);
1736 dump a string result from ldap
1738 static void dump_string(const char *field
, char **values
)
1741 for (i
=0; values
[i
]; i
++) {
1742 printf("%s: %s\n", field
, values
[i
]);
1747 dump a field from LDAP on stdout
1751 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1756 void (*handler
)(const char *, struct berval
**);
1758 {"objectGUID", False
, dump_guid
},
1759 {"netbootGUID", False
, dump_guid
},
1760 {"nTSecurityDescriptor", False
, dump_sd
},
1761 {"dnsRecord", False
, dump_binary
},
1762 {"objectSid", False
, dump_sid
},
1763 {"tokenGroups", False
, dump_sid
},
1764 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1765 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1766 {"mS-DS-CreatorSID", False
, dump_sid
},
1771 if (!field
) { /* must be end of an entry */
1776 for (i
=0; handlers
[i
].name
; i
++) {
1777 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1778 if (!values
) /* first time, indicate string or not */
1779 return handlers
[i
].string
;
1780 handlers
[i
].handler(field
, (struct berval
**) values
);
1784 if (!handlers
[i
].name
) {
1785 if (!values
) /* first time, indicate string conversion */
1787 dump_string(field
, (char **)values
);
1793 * Dump a result from LDAP on stdout
1794 * used for debugging
1795 * @param ads connection to ads server
1796 * @param res Results to dump
1799 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1801 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1805 * Walk through results, calling a function for each entry found.
1806 * The function receives a field name, a berval * array of values,
1807 * and a data area passed through from the start. The function is
1808 * called once with null for field and values at the end of each
1810 * @param ads connection to ads server
1811 * @param res Results to process
1812 * @param fn Function for processing each result
1813 * @param data_area user-defined area to pass to function
1815 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
1816 BOOL(*fn
)(char *, void **, void *),
1822 if (!(ctx
= talloc_init("ads_process_results")))
1825 for (msg
= ads_first_entry(ads
, res
); msg
;
1826 msg
= ads_next_entry(ads
, msg
)) {
1830 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1831 (LDAPMessage
*)msg
,&b
);
1833 utf8_field
=ldap_next_attribute(ads
->ld
,
1834 (LDAPMessage
*)msg
,b
)) {
1835 struct berval
**ber_vals
;
1836 char **str_vals
, **utf8_vals
;
1840 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1841 string
= fn(field
, NULL
, data_area
);
1844 utf8_vals
= ldap_get_values(ads
->ld
,
1845 (LDAPMessage
*)msg
, field
);
1846 str_vals
= ads_pull_strvals(ctx
,
1847 (const char **) utf8_vals
);
1848 fn(field
, (void **) str_vals
, data_area
);
1849 ldap_value_free(utf8_vals
);
1851 ber_vals
= ldap_get_values_len(ads
->ld
,
1852 (LDAPMessage
*)msg
, field
);
1853 fn(field
, (void **) ber_vals
, data_area
);
1855 ldap_value_free_len(ber_vals
);
1857 ldap_memfree(utf8_field
);
1860 talloc_free_children(ctx
);
1861 fn(NULL
, NULL
, data_area
); /* completed an entry */
1864 talloc_destroy(ctx
);
1868 * count how many replies are in a LDAPMessage
1869 * @param ads connection to ads server
1870 * @param res Results to count
1871 * @return number of replies
1873 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1875 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1879 * pull the first entry from a ADS result
1880 * @param ads connection to ads server
1881 * @param res Results of search
1882 * @return first entry from result
1884 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1886 return ldap_first_entry(ads
->ld
, res
);
1890 * pull the next entry from a ADS result
1891 * @param ads connection to ads server
1892 * @param res Results of search
1893 * @return next entry from result
1895 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1897 return ldap_next_entry(ads
->ld
, res
);
1901 * pull a single string from a ADS result
1902 * @param ads connection to ads server
1903 * @param mem_ctx TALLOC_CTX to use for allocating result string
1904 * @param msg Results of search
1905 * @param field Attribute to retrieve
1906 * @return Result string in talloc context
1908 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
1916 values
= ldap_get_values(ads
->ld
, msg
, field
);
1921 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1923 if (rc
!= (size_t)-1)
1927 ldap_value_free(values
);
1932 * pull an array of strings from a ADS result
1933 * @param ads connection to ads server
1934 * @param mem_ctx TALLOC_CTX to use for allocating result string
1935 * @param msg Results of search
1936 * @param field Attribute to retrieve
1937 * @return Result strings in talloc context
1939 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
1940 LDAPMessage
*msg
, const char *field
,
1947 values
= ldap_get_values(ads
->ld
, msg
, field
);
1951 *num_values
= ldap_count_values(values
);
1953 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
1955 ldap_value_free(values
);
1959 for (i
=0;i
<*num_values
;i
++) {
1960 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1961 ldap_value_free(values
);
1967 ldap_value_free(values
);
1972 * pull an array of strings from a ADS result
1973 * (handle large multivalue attributes with range retrieval)
1974 * @param ads connection to ads server
1975 * @param mem_ctx TALLOC_CTX to use for allocating result string
1976 * @param msg Results of search
1977 * @param field Attribute to retrieve
1978 * @param current_strings strings returned by a previous call to this function
1979 * @param next_attribute The next query should ask for this attribute
1980 * @param num_values How many values did we get this time?
1981 * @param more_values Are there more values to get?
1982 * @return Result strings in talloc context
1984 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
1985 TALLOC_CTX
*mem_ctx
,
1986 LDAPMessage
*msg
, const char *field
,
1987 char **current_strings
,
1988 const char **next_attribute
,
1989 size_t *num_strings
,
1993 char *expected_range_attrib
, *range_attr
;
1994 BerElement
*ptr
= NULL
;
1997 size_t num_new_strings
;
1998 unsigned long int range_start
;
1999 unsigned long int range_end
;
2001 /* we might have been given the whole lot anyway */
2002 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2003 *more_strings
= False
;
2007 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2009 /* look for Range result */
2010 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
2012 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
2013 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2014 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2022 /* nothing here - this field is just empty */
2023 *more_strings
= False
;
2027 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2028 &range_start
, &range_end
) == 2) {
2029 *more_strings
= True
;
2031 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2032 &range_start
) == 1) {
2033 *more_strings
= False
;
2035 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2037 ldap_memfree(range_attr
);
2038 *more_strings
= False
;
2043 if ((*num_strings
) != range_start
) {
2044 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2045 " - aborting range retreival\n",
2046 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2047 ldap_memfree(range_attr
);
2048 *more_strings
= False
;
2052 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2054 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2055 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2056 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2057 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2058 (unsigned long int)num_new_strings
));
2059 ldap_memfree(range_attr
);
2060 *more_strings
= False
;
2064 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2065 *num_strings
+ num_new_strings
);
2067 if (strings
== NULL
) {
2068 ldap_memfree(range_attr
);
2069 *more_strings
= False
;
2073 if (new_strings
&& num_new_strings
) {
2074 memcpy(&strings
[*num_strings
], new_strings
,
2075 sizeof(*new_strings
) * num_new_strings
);
2078 (*num_strings
) += num_new_strings
;
2080 if (*more_strings
) {
2081 *next_attribute
= talloc_asprintf(mem_ctx
,
2086 if (!*next_attribute
) {
2087 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2088 ldap_memfree(range_attr
);
2089 *more_strings
= False
;
2094 ldap_memfree(range_attr
);
2100 * pull a single uint32 from a ADS result
2101 * @param ads connection to ads server
2102 * @param msg Results of search
2103 * @param field Attribute to retrieve
2104 * @param v Pointer to int to store result
2105 * @return boolean inidicating success
2107 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2112 values
= ldap_get_values(ads
->ld
, msg
, field
);
2116 ldap_value_free(values
);
2120 *v
= atoi(values
[0]);
2121 ldap_value_free(values
);
2126 * pull a single objectGUID from an ADS result
2127 * @param ads connection to ADS server
2128 * @param msg results of search
2129 * @param guid 37-byte area to receive text guid
2130 * @return boolean indicating success
2132 BOOL
ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2135 UUID_FLAT flat_guid
;
2137 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2142 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2143 smb_uuid_unpack(flat_guid
, guid
);
2144 ldap_value_free(values
);
2147 ldap_value_free(values
);
2154 * pull a single DOM_SID from a ADS result
2155 * @param ads connection to ads server
2156 * @param msg Results of search
2157 * @param field Attribute to retrieve
2158 * @param sid Pointer to sid to store result
2159 * @return boolean inidicating success
2161 BOOL
ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2164 struct berval
**values
;
2167 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2173 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2175 ldap_value_free_len(values
);
2180 * pull an array of DOM_SIDs from a ADS result
2181 * @param ads connection to ads server
2182 * @param mem_ctx TALLOC_CTX for allocating sid array
2183 * @param msg Results of search
2184 * @param field Attribute to retrieve
2185 * @param sids pointer to sid array to allocate
2186 * @return the count of SIDs pulled
2188 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2189 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2191 struct berval
**values
;
2195 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2200 for (i
=0; values
[i
]; i
++)
2203 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2205 ldap_value_free_len(values
);
2210 for (i
=0; values
[i
]; i
++) {
2211 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2214 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2219 ldap_value_free_len(values
);
2224 * pull a SEC_DESC from a ADS result
2225 * @param ads connection to ads server
2226 * @param mem_ctx TALLOC_CTX for allocating sid array
2227 * @param msg Results of search
2228 * @param field Attribute to retrieve
2229 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2230 * @return boolean inidicating success
2232 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2233 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2235 struct berval
**values
;
2239 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2241 if (!values
) return False
;
2244 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2245 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2246 prs_set_offset(&ps
,0);
2248 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2251 ldap_value_free_len(values
);
2256 * in order to support usernames longer than 21 characters we need to
2257 * use both the sAMAccountName and the userPrincipalName attributes
2258 * It seems that not all users have the userPrincipalName attribute set
2260 * @param ads connection to ads server
2261 * @param mem_ctx TALLOC_CTX for allocating sid array
2262 * @param msg Results of search
2263 * @return the username
2265 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2271 /* lookup_name() only works on the sAMAccountName to
2272 returning the username portion of userPrincipalName
2273 breaks winbindd_getpwnam() */
2275 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2276 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2281 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2286 * find the update serial number - this is the core of the ldap cache
2287 * @param ads connection to ads server
2288 * @param ads connection to ADS server
2289 * @param usn Pointer to retrieved update serial number
2290 * @return status of search
2292 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2294 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2298 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2299 if (!ADS_ERR_OK(status
))
2302 if (ads_count_replies(ads
, res
) != 1) {
2303 ads_msgfree(ads
, res
);
2304 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2307 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2308 ads_msgfree(ads
, res
);
2309 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2312 ads_msgfree(ads
, res
);
2316 /* parse a ADS timestring - typical string is
2317 '20020917091222.0Z0' which means 09:12.22 17th September
2319 static time_t ads_parse_time(const char *str
)
2325 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2326 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2327 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2336 /********************************************************************
2337 ********************************************************************/
2339 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2341 const char *attrs
[] = {"currentTime", NULL
};
2346 ADS_STRUCT
*ads_s
= ads
;
2348 if (!(ctx
= talloc_init("ads_current_time"))) {
2349 return ADS_ERROR(LDAP_NO_MEMORY
);
2352 /* establish a new ldap tcp session if necessary */
2355 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2356 ads
->server
.ldap_server
)) == NULL
)
2360 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2361 status
= ads_connect( ads_s
);
2362 if ( !ADS_ERR_OK(status
))
2366 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2367 if (!ADS_ERR_OK(status
)) {
2371 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2373 ads_msgfree(ads_s
, res
);
2374 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2378 /* but save the time and offset in the original ADS_STRUCT */
2380 ads
->config
.current_time
= ads_parse_time(timestr
);
2382 if (ads
->config
.current_time
!= 0) {
2383 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2384 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2387 ads_msgfree(ads
, res
);
2389 status
= ADS_SUCCESS
;
2392 /* free any temporary ads connections */
2393 if ( ads_s
!= ads
) {
2394 ads_destroy( &ads_s
);
2396 talloc_destroy(ctx
);
2401 /********************************************************************
2402 ********************************************************************/
2404 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2406 const char *attrs
[] = {"domainFunctionality", NULL
};
2409 ADS_STRUCT
*ads_s
= ads
;
2411 *val
= DS_DOMAIN_FUNCTION_2000
;
2413 /* establish a new ldap tcp session if necessary */
2416 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2417 ads
->server
.ldap_server
)) == NULL
)
2421 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2422 status
= ads_connect( ads_s
);
2423 if ( !ADS_ERR_OK(status
))
2427 /* If the attribute does not exist assume it is a Windows 2000
2428 functional domain */
2430 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2431 if (!ADS_ERR_OK(status
)) {
2432 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2433 status
= ADS_SUCCESS
;
2438 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2439 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2441 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2444 ads_msgfree(ads
, res
);
2447 /* free any temporary ads connections */
2448 if ( ads_s
!= ads
) {
2449 ads_destroy( &ads_s
);
2456 * find the domain sid for our domain
2457 * @param ads connection to ads server
2458 * @param sid Pointer to domain sid
2459 * @return status of search
2461 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2463 const char *attrs
[] = {"objectSid", NULL
};
2467 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2469 if (!ADS_ERR_OK(rc
)) return rc
;
2470 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2471 ads_msgfree(ads
, res
);
2472 return ADS_ERROR_SYSTEM(ENOENT
);
2474 ads_msgfree(ads
, res
);
2480 * find our site name
2481 * @param ads connection to ads server
2482 * @param mem_ctx Pointer to talloc context
2483 * @param site_name Pointer to the sitename
2484 * @return status of search
2486 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2490 const char *dn
, *service_name
;
2491 const char *attrs
[] = { "dsServiceName", NULL
};
2493 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2494 if (!ADS_ERR_OK(status
)) {
2498 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2499 if (service_name
== NULL
) {
2500 ads_msgfree(ads
, res
);
2501 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2504 ads_msgfree(ads
, res
);
2506 /* go up three levels */
2507 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2509 return ADS_ERROR(LDAP_NO_MEMORY
);
2512 *site_name
= talloc_strdup(mem_ctx
, dn
);
2513 if (*site_name
== NULL
) {
2514 return ADS_ERROR(LDAP_NO_MEMORY
);
2519 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2524 * find the site dn where a machine resides
2525 * @param ads connection to ads server
2526 * @param mem_ctx Pointer to talloc context
2527 * @param computer_name name of the machine
2528 * @param site_name Pointer to the sitename
2529 * @return status of search
2531 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2535 const char *parent
, *config_context
, *filter
;
2536 const char *attrs
[] = { "configurationNamingContext", NULL
};
2539 /* shortcut a query */
2540 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2541 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2544 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2545 if (!ADS_ERR_OK(status
)) {
2549 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2550 if (config_context
== NULL
) {
2551 ads_msgfree(ads
, res
);
2552 return ADS_ERROR(LDAP_NO_MEMORY
);
2555 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2556 if (filter
== NULL
) {
2557 ads_msgfree(ads
, res
);
2558 return ADS_ERROR(LDAP_NO_MEMORY
);
2561 ads_msgfree(ads
, res
);
2563 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
, filter
, NULL
, &res
);
2564 if (!ADS_ERR_OK(status
)) {
2568 if (ads_count_replies(ads
, res
) != 1) {
2569 ads_msgfree(ads
, res
);
2570 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2573 dn
= ads_get_dn(ads
, res
);
2575 ads_msgfree(ads
, res
);
2576 return ADS_ERROR(LDAP_NO_MEMORY
);
2579 /* go up three levels */
2580 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2581 if (parent
== NULL
) {
2582 ads_msgfree(ads
, res
);
2583 ads_memfree(ads
, dn
);
2584 return ADS_ERROR(LDAP_NO_MEMORY
);
2587 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2588 if (*site_dn
== NULL
) {
2589 ads_msgfree(ads
, res
);
2590 ads_memfree(ads
, dn
);
2591 ADS_ERROR(LDAP_NO_MEMORY
);
2594 ads_memfree(ads
, dn
);
2595 ads_msgfree(ads
, res
);
2601 * get the upn suffixes for a domain
2602 * @param ads connection to ads server
2603 * @param mem_ctx Pointer to talloc context
2604 * @param suffixes Pointer to an array of suffixes
2605 * @param site_name Pointer to the number of suffixes
2606 * @return status of search
2608 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char **suffixes
, size_t *num_suffixes
)
2612 const char *config_context
, *base
;
2613 const char *attrs
[] = { "configurationNamingContext", NULL
};
2614 const char *attrs2
[] = { "uPNSuffixes", NULL
};
2616 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2617 if (!ADS_ERR_OK(status
)) {
2621 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2622 if (config_context
== NULL
) {
2623 return ADS_ERROR(LDAP_NO_MEMORY
);
2626 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2628 return ADS_ERROR(LDAP_NO_MEMORY
);
2631 status
= ads_search_dn(ads
, &res
, base
, attrs2
);
2632 if (!ADS_ERR_OK(status
)) {
2636 if (ads_count_replies(ads
, res
) != 1) {
2637 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2640 suffixes
= ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2641 if (suffixes
== NULL
) {
2642 ads_msgfree(ads
, res
);
2643 return ADS_ERROR(LDAP_NO_MEMORY
);
2646 ads_msgfree(ads
, res
);
2652 * pull a DOM_SID from an extended dn string
2653 * @param mem_ctx TALLOC_CTX
2654 * @param flags string type of extended_dn
2655 * @param sid pointer to a DOM_SID
2656 * @return boolean inidicating success
2658 BOOL
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2660 enum ads_extended_dn_flags flags
,
2670 * ADS_EXTENDED_DN_HEX_STRING:
2671 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2673 * ADS_EXTENDED_DN_STRING (only with w2k3):
2674 <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
2677 p
= strchr(dn
, ';');
2682 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
2686 p
+= strlen(";<SID=");
2695 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
2699 case ADS_EXTENDED_DN_STRING
:
2700 if (!string_to_sid(sid
, p
)) {
2704 case ADS_EXTENDED_DN_HEX_STRING
: {
2708 buf_len
= strhex_to_str(buf
, strlen(p
), p
);
2713 if (!sid_parse(buf
, buf_len
, sid
)) {
2714 DEBUG(10,("failed to parse sid\n"));
2720 DEBUG(10,("unknown extended dn format\n"));
2728 * pull an array of DOM_SIDs from a ADS result
2729 * @param ads connection to ads server
2730 * @param mem_ctx TALLOC_CTX for allocating sid array
2731 * @param msg Results of search
2732 * @param field Attribute to retrieve
2733 * @param flags string type of extended_dn
2734 * @param sids pointer to sid array to allocate
2735 * @return the count of SIDs pulled
2737 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
2738 TALLOC_CTX
*mem_ctx
,
2741 enum ads_extended_dn_flags flags
,
2748 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
2749 &dn_count
)) == NULL
) {
2753 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
2755 TALLOC_FREE(dn_strings
);
2759 for (i
=0; i
<dn_count
; i
++) {
2761 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
2762 flags
, &(*sids
)[i
])) {
2764 TALLOC_FREE(dn_strings
);
2769 TALLOC_FREE(dn_strings
);
2774 /********************************************************************
2775 ********************************************************************/
2777 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2779 LDAPMessage
*res
= NULL
;
2784 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2785 if (!ADS_ERR_OK(status
)) {
2786 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2791 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2792 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2796 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
2797 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2801 ads_msgfree(ads
, res
);
2806 /********************************************************************
2807 ********************************************************************/
2809 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2811 LDAPMessage
*res
= NULL
;
2816 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2817 if (!ADS_ERR_OK(status
)) {
2818 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2823 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2824 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
2828 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
2829 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2833 ads_msgfree(ads
, res
);
2838 /********************************************************************
2839 ********************************************************************/
2841 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2843 LDAPMessage
*res
= NULL
;
2848 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2849 if (!ADS_ERR_OK(status
)) {
2850 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2855 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2856 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2860 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
2861 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2865 ads_msgfree(ads
, res
);
2872 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
2875 * Join a machine to a realm
2876 * Creates the machine account and sets the machine password
2877 * @param ads connection to ads server
2878 * @param machine name of host to add
2879 * @param org_unit Organizational unit to place machine in
2880 * @return status of join
2882 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
2883 uint32 account_type
, const char *org_unit
)
2886 LDAPMessage
*res
= NULL
;
2889 /* machine name must be lowercase */
2890 machine
= SMB_STRDUP(machine_name
);
2891 strlower_m(machine
);
2894 status = ads_find_machine_acct(ads, (void **)&res, machine);
2895 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2896 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2897 status = ads_leave_realm(ads, machine);
2898 if (!ADS_ERR_OK(status)) {
2899 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2900 machine, ads->config.realm));
2905 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
2906 if (!ADS_ERR_OK(status
)) {
2907 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
2912 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
2913 if (!ADS_ERR_OK(status
)) {
2914 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
2920 ads_msgfree(ads
, res
);
2927 * Delete a machine from the realm
2928 * @param ads connection to ads server
2929 * @param hostname Machine to remove
2930 * @return status of delete
2932 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
2937 char *hostnameDN
, *host
;
2939 LDAPControl ldap_control
;
2940 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
2942 pldap_control
[0] = &ldap_control
;
2943 memset(&ldap_control
, 0, sizeof(LDAPControl
));
2944 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
2946 /* hostname must be lowercase */
2947 host
= SMB_STRDUP(hostname
);
2950 status
= ads_find_machine_acct(ads
, &res
, host
);
2951 if (!ADS_ERR_OK(status
)) {
2952 DEBUG(0, ("Host account for %s does not exist.\n", host
));
2957 msg
= ads_first_entry(ads
, res
);
2960 return ADS_ERROR_SYSTEM(ENOENT
);
2963 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
2965 rc
= ldap_delete_ext_s(ads
->ld
, hostnameDN
, pldap_control
, NULL
);
2967 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
2969 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
2972 if (rc
!= LDAP_SUCCESS
) {
2973 const char *attrs
[] = { "cn", NULL
};
2974 LDAPMessage
*msg_sub
;
2976 /* we only search with scope ONE, we do not expect any further
2977 * objects to be created deeper */
2979 status
= ads_do_search_retry(ads
, hostnameDN
,
2980 LDAP_SCOPE_ONELEVEL
,
2981 "(objectclass=*)", attrs
, &res
);
2983 if (!ADS_ERR_OK(status
)) {
2985 ads_memfree(ads
, hostnameDN
);
2989 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
2990 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
2994 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
2996 ads_memfree(ads
, hostnameDN
);
2997 return ADS_ERROR(LDAP_NO_MEMORY
);
3000 status
= ads_del_dn(ads
, dn
);
3001 if (!ADS_ERR_OK(status
)) {
3002 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3004 ads_memfree(ads
, dn
);
3005 ads_memfree(ads
, hostnameDN
);
3009 ads_memfree(ads
, dn
);
3012 /* there should be no subordinate objects anymore */
3013 status
= ads_do_search_retry(ads
, hostnameDN
,
3014 LDAP_SCOPE_ONELEVEL
,
3015 "(objectclass=*)", attrs
, &res
);
3017 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3019 ads_memfree(ads
, hostnameDN
);
3023 /* delete hostnameDN now */
3024 status
= ads_del_dn(ads
, hostnameDN
);
3025 if (!ADS_ERR_OK(status
)) {
3027 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3028 ads_memfree(ads
, hostnameDN
);
3033 ads_memfree(ads
, hostnameDN
);
3035 status
= ads_find_machine_acct(ads
, &res
, host
);
3036 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3037 DEBUG(3, ("Failed to remove host account.\n"));