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 a canonical dn from search results
983 * @param ads connection to ads server
984 * @param msg Search result
987 char *ads_get_dn_canonical(ADS_STRUCT *ads, LDAPMessage *msg)
989 #ifdef HAVE_LDAP_DN2AD_CANONICAL
990 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
997 * Get the parent from a dn
998 * @param dn the dn to return the parent from
999 * @return parent dn string
1001 char *ads_parent_dn(const char *dn)
1009 p = strchr(dn, ',');
1019 * Find a machine account given a hostname
1020 * @param ads connection to ads server
1021 * @param res ** which will contain results - free res* with ads_msgfree()
1022 * @param host Hostname to search for
1023 * @return status of search
1025 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1026 const char *machine)
1030 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1034 /* the easiest way to find a machine account anywhere in the tree
1035 is to look for hostname$ */
1036 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1037 DEBUG(1, ("asprintf failed!\n"));
1038 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1041 status = ads_search(ads, res, expr, attrs);
1047 * Initialize a list of mods to be used in a modify request
1048 * @param ctx An initialized TALLOC_CTX
1049 * @return allocated ADS_MODLIST
1051 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1053 #define ADS_MODLIST_ALLOC_SIZE 10
1056 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1057 /* -1 is safety to make sure we don't go over the end.
1058 need to reset it to NULL before doing ldap modify */
1059 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1061 return (ADS_MODLIST)mods;
1066 add an attribute to the list, with values list already constructed
1068 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1069 int mod_op, const char *name,
1070 const void *_invals)
1072 const void **invals = (const void **)_invals;
1074 LDAPMod **modlist = (LDAPMod **) *mods;
1075 struct berval **ber_values = NULL;
1076 char **char_values = NULL;
1079 mod_op = LDAP_MOD_DELETE;
1081 if (mod_op & LDAP_MOD_BVALUES)
1082 ber_values = ads_dup_values(ctx,
1083 (const struct berval **)invals);
1085 char_values = ads_push_strvals(ctx,
1086 (const char **) invals);
1089 /* find the first empty slot */
1090 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1092 if (modlist[curmod] == (LDAPMod *) -1) {
1093 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1094 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1095 return ADS_ERROR(LDAP_NO_MEMORY);
1096 memset(&modlist[curmod], 0,
1097 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1098 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1099 *mods = (ADS_MODLIST)modlist;
1102 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1103 return ADS_ERROR(LDAP_NO_MEMORY);
1104 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1105 if (mod_op & LDAP_MOD_BVALUES) {
1106 modlist[curmod]->mod_bvalues = ber_values;
1107 } else if (mod_op & LDAP_MOD_DELETE) {
1108 modlist[curmod]->mod_values = NULL;
1110 modlist[curmod]->mod_values = char_values;
1113 modlist[curmod]->mod_op = mod_op;
1114 return ADS_ERROR(LDAP_SUCCESS);
1118 * Add a single string value to a mod list
1119 * @param ctx An initialized TALLOC_CTX
1120 * @param mods An initialized ADS_MODLIST
1121 * @param name The attribute name to add
1122 * @param val The value to add - NULL means DELETE
1123 * @return ADS STATUS indicating success of add
1125 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1126 const char *name, const char *val)
1128 const char *values[2];
1134 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1135 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1139 * Add an array of string values to a mod list
1140 * @param ctx An initialized TALLOC_CTX
1141 * @param mods An initialized ADS_MODLIST
1142 * @param name The attribute name to add
1143 * @param vals The array of string values to add - NULL means DELETE
1144 * @return ADS STATUS indicating success of add
1146 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1147 const char *name, const char **vals)
1150 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1151 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1152 name, (const void **) vals);
1157 * Add a single ber-encoded value to a mod list
1158 * @param ctx An initialized TALLOC_CTX
1159 * @param mods An initialized ADS_MODLIST
1160 * @param name The attribute name to add
1161 * @param val The value to add - NULL means DELETE
1162 * @return ADS STATUS indicating success of add
1164 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1165 const char *name, const struct berval *val)
1167 const struct berval *values[2];
1172 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1173 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1174 name, (const void **) values);
1179 * Perform an ldap modify
1180 * @param ads connection to ads server
1181 * @param mod_dn DistinguishedName to modify
1182 * @param mods list of modifications to perform
1183 * @return status of modify
1185 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1188 char *utf8_dn = NULL;
1190 this control is needed to modify that contains a currently
1191 non-existent attribute (but allowable for the object) to run
1193 LDAPControl PermitModify = {
1194 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1197 LDAPControl *controls[2];
1199 controls[0] = &PermitModify;
1202 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1203 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1206 /* find the end of the list, marked by NULL or -1 */
1207 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1208 /* make sure the end of the list is NULL */
1210 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1211 (LDAPMod **) mods, controls, NULL);
1213 return ADS_ERROR(ret);
1217 * Perform an ldap add
1218 * @param ads connection to ads server
1219 * @param new_dn DistinguishedName to add
1220 * @param mods list of attributes and values for DN
1221 * @return status of add
1223 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1226 char *utf8_dn = NULL;
1228 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1229 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1230 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1233 /* find the end of the list, marked by NULL or -1 */
1234 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1235 /* make sure the end of the list is NULL */
1238 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1240 return ADS_ERROR(ret);
1244 * Delete a DistinguishedName
1245 * @param ads connection to ads server
1246 * @param new_dn DistinguishedName to delete
1247 * @return status of delete
1249 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1252 char *utf8_dn = NULL;
1253 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1254 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1255 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1258 ret = ldap_delete_s(ads->ld, utf8_dn);
1260 return ADS_ERROR(ret);
1264 * Build an org unit string
1265 * if org unit is Computers or blank then assume a container, otherwise
1266 * assume a / separated list of organisational units.
1267 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1268 * @param ads connection to ads server
1269 * @param org_unit Organizational unit
1270 * @return org unit string - caller must free
1272 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1276 if (!org_unit || !*org_unit) {
1278 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1280 /* samba4 might not yet respond to a wellknownobject-query */
1281 return ret ? ret : SMB_STRDUP("cn=Computers");
1284 if (strequal(org_unit, "Computers")) {
1285 return SMB_STRDUP("cn=Computers");
1288 /* jmcd: removed "\\" from the separation chars, because it is
1289 needed as an escape for chars like '#' which are valid in an
1291 return ads_build_path(org_unit, "/", "ou=", 1);
1295 * Get a org unit string for a well-known GUID
1296 * @param ads connection to ads server
1297 * @param wknguid Well known GUID
1298 * @return org unit string - caller must free
1300 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1303 LDAPMessage *res = NULL;
1304 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1305 **bind_dn_exp = NULL;
1306 const char *attrs[] = {"distinguishedName", NULL};
1307 int new_ln, wkn_ln, bind_ln, i;
1309 if (wknguid == NULL) {
1313 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1314 DEBUG(1, ("asprintf failed!\n"));
1318 status = ads_search_dn(ads, &res, base, attrs);
1319 if (!ADS_ERR_OK(status)) {
1320 DEBUG(1,("Failed while searching for: %s\n", base));
1324 if (ads_count_replies(ads, res) != 1) {
1328 /* substitute the bind-path from the well-known-guid-search result */
1329 wkn_dn = ads_get_dn(ads, res);
1334 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1339 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1344 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1346 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1349 new_ln = wkn_ln - bind_ln;
1351 ret = SMB_STRDUP(wkn_dn_exp[0]);
1356 for (i=1; i < new_ln; i++) {
1359 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1365 ret = SMB_STRDUP(s);
1374 ads_msgfree(ads, res);
1375 ads_memfree(ads, wkn_dn);
1377 ldap_value_free(wkn_dn_exp);
1380 ldap_value_free(bind_dn_exp);
1387 * Adds (appends) an item to an attribute array, rather then
1388 * replacing the whole list
1389 * @param ctx An initialized TALLOC_CTX
1390 * @param mods An initialized ADS_MODLIST
1391 * @param name name of the ldap attribute to append to
1392 * @param vals an array of values to add
1393 * @return status of addition
1396 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1397 const char *name, const char **vals)
1399 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1400 (const void *) vals);
1404 * Determines the computer account's current KVNO via an LDAP lookup
1405 * @param ads An initialized ADS_STRUCT
1406 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1407 * @return the kvno for the computer account, or -1 in case of a failure.
1410 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1412 LDAPMessage *res = NULL;
1413 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1415 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1416 char *dn_string = NULL;
1417 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1419 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1420 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1423 ret = ads_search(ads, &res, filter, attrs);
1425 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1426 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1427 ads_msgfree(ads, res);
1431 dn_string = ads_get_dn(ads, res);
1433 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1434 ads_msgfree(ads, res);
1437 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1438 ads_memfree(ads, dn_string);
1440 /* ---------------------------------------------------------
1441 * 0 is returned as a default KVNO from this point on...
1442 * This is done because Windows 2000 does not support key
1443 * version numbers. Chances are that a failure in the next
1444 * step is simply due to Windows 2000 being used for a
1445 * domain controller. */
1448 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1449 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1450 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1451 ads_msgfree(ads, res);
1456 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1457 ads_msgfree(ads, res);
1462 * This clears out all registered spn's for a given hostname
1463 * @param ads An initilaized ADS_STRUCT
1464 * @param machine_name the NetBIOS name of the computer.
1465 * @return 0 upon success, non-zero otherwise.
1468 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1471 LDAPMessage *res = NULL;
1473 const char *servicePrincipalName[1] = {NULL};
1474 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1475 char *dn_string = NULL;
1477 ret = ads_find_machine_acct(ads, &res, machine_name);
1478 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1479 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1480 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1481 ads_msgfree(ads, res);
1482 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1485 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1486 ctx = talloc_init("ads_clear_service_principal_names");
1488 ads_msgfree(ads, res);
1489 return ADS_ERROR(LDAP_NO_MEMORY);
1492 if (!(mods = ads_init_mods(ctx))) {
1493 talloc_destroy(ctx);
1494 ads_msgfree(ads, res);
1495 return ADS_ERROR(LDAP_NO_MEMORY);
1497 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1498 if (!ADS_ERR_OK(ret)) {
1499 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1500 ads_msgfree(ads, res);
1501 talloc_destroy(ctx);
1504 dn_string = ads_get_dn(ads, res);
1506 talloc_destroy(ctx);
1507 ads_msgfree(ads, res);
1508 return ADS_ERROR(LDAP_NO_MEMORY);
1510 ret = ads_gen_mod(ads, dn_string, mods);
1511 ads_memfree(ads,dn_string);
1512 if (!ADS_ERR_OK(ret)) {
1513 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1515 ads_msgfree(ads, res);
1516 talloc_destroy(ctx);
1520 ads_msgfree(ads, res);
1521 talloc_destroy(ctx);
1526 * This adds a service principal name to an existing computer account
1527 * (found by hostname) in AD.
1528 * @param ads An initialized ADS_STRUCT
1529 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1530 * @param my_fqdn The fully qualified DNS name of the machine
1531 * @param spn A string of the service principal to add, i.e. 'host'
1532 * @return 0 upon sucess, or non-zero if a failure occurs
1535 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1536 const char *my_fqdn, const char *spn)
1540 LDAPMessage *res = NULL;
1543 char *dn_string = NULL;
1544 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1546 ret = ads_find_machine_acct(ads, &res, machine_name);
1547 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1548 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1550 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1551 spn, machine_name, ads->config.realm));
1552 ads_msgfree(ads, res);
1553 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1556 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1557 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1558 ads_msgfree(ads, res);
1559 return ADS_ERROR(LDAP_NO_MEMORY);
1562 /* add short name spn */
1564 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1565 talloc_destroy(ctx);
1566 ads_msgfree(ads, res);
1567 return ADS_ERROR(LDAP_NO_MEMORY);
1570 strlower_m(&psp1[strlen(spn)]);
1571 servicePrincipalName[0] = psp1;
1573 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1574 psp1, machine_name));
1577 /* add fully qualified spn */
1579 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1580 ret = ADS_ERROR(LDAP_NO_MEMORY);
1584 strlower_m(&psp2[strlen(spn)]);
1585 servicePrincipalName[1] = psp2;
1587 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1588 psp2, machine_name));
1590 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1591 ret = ADS_ERROR(LDAP_NO_MEMORY);
1595 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1596 if (!ADS_ERR_OK(ret)) {
1597 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1601 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1602 ret = ADS_ERROR(LDAP_NO_MEMORY);
1606 ret = ads_gen_mod(ads, dn_string, mods);
1607 ads_memfree(ads,dn_string);
1608 if (!ADS_ERR_OK(ret)) {
1609 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1615 ads_msgfree(ads, res);
1620 * adds a machine account to the ADS server
1621 * @param ads An intialized ADS_STRUCT
1622 * @param machine_name - the NetBIOS machine name of this account.
1623 * @param account_type A number indicating the type of account to create
1624 * @param org_unit The LDAP path in which to place this account
1625 * @return 0 upon success, or non-zero otherwise
1628 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1629 const char *org_unit)
1632 char *samAccountName, *controlstr;
1636 const char *objectClass[] = {"top", "person", "organizationalPerson",
1637 "user", "computer", NULL};
1638 LDAPMessage *res = NULL;
1639 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1640 UF_DONT_EXPIRE_PASSWD |\
1641 UF_ACCOUNTDISABLE );
1643 if (!(ctx = talloc_init("ads_add_machine_acct")))
1644 return ADS_ERROR(LDAP_NO_MEMORY);
1646 ret = ADS_ERROR(LDAP_NO_MEMORY);
1648 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1649 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1651 if ( !new_dn || !samAccountName ) {
1655 #ifndef ENCTYPE_ARCFOUR_HMAC
1656 acct_control |= UF_USE_DES_KEY_ONLY;
1659 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1663 if (!(mods = ads_init_mods(ctx))) {
1667 ads_mod_str(ctx, &mods, "cn", machine_name);
1668 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1669 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1670 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1672 ret = ads_gen_add(ads, new_dn, mods);
1675 ads_msgfree(ads, res);
1676 talloc_destroy(ctx);
1682 dump a binary result from ldap
1684 static void dump_binary(const char *field, struct berval **values)
1687 for (i=0; values[i]; i++) {
1688 printf("%s: ", field);
1689 for (j=0; j<values[i]->bv_len; j++) {
1690 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1696 static void dump_guid(const char *field, struct berval **values)
1700 for (i=0; values[i]; i++) {
1701 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1702 printf("%s: %s\n", field,
1703 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1708 dump a sid result from ldap
1710 static void dump_sid(const char *field, struct berval **values)
1713 for (i=0; values[i]; i++) {
1715 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1716 printf("%s: %s\n", field, sid_string_static(&sid));
1721 dump ntSecurityDescriptor
1723 static void dump_sd(const char *filed, struct berval **values)
1728 TALLOC_CTX *ctx = 0;
1730 if (!(ctx = talloc_init("sec_io_desc")))
1734 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1735 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1736 prs_set_offset(&ps,0);
1739 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1741 talloc_destroy(ctx);
1744 if (psd) ads_disp_sd(psd);
1747 talloc_destroy(ctx);
1751 dump a string result from ldap
1753 static void dump_string(const char *field, char **values)
1756 for (i=0; values[i]; i++) {
1757 printf("%s: %s\n", field, values[i]);
1762 dump a field from LDAP on stdout
1766 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1771 void (*handler)(const char *, struct berval **);
1773 {"objectGUID", False, dump_guid},
1774 {"netbootGUID", False, dump_guid},
1775 {"nTSecurityDescriptor", False, dump_sd},
1776 {"dnsRecord", False, dump_binary},
1777 {"objectSid", False, dump_sid},
1778 {"tokenGroups", False, dump_sid},
1779 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1780 {"tokengroupsGlobalandUniversal", False, dump_sid},
1781 {"mS-DS-CreatorSID", False, dump_sid},
1786 if (!field) { /* must be end of an entry */
1791 for (i=0; handlers[i].name; i++) {
1792 if (StrCaseCmp(handlers[i].name, field) == 0) {
1793 if (!values) /* first time, indicate string or not */
1794 return handlers[i].string;
1795 handlers[i].handler(field, (struct berval **) values);
1799 if (!handlers[i].name) {
1800 if (!values) /* first time, indicate string conversion */
1802 dump_string(field, (char **)values);
1808 * Dump a result from LDAP on stdout
1809 * used for debugging
1810 * @param ads connection to ads server
1811 * @param res Results to dump
1814 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1816 ads_process_results(ads, res, ads_dump_field, NULL);
1820 * Walk through results, calling a function for each entry found.
1821 * The function receives a field name, a berval * array of values,
1822 * and a data area passed through from the start. The function is
1823 * called once with null for field and values at the end of each
1825 * @param ads connection to ads server
1826 * @param res Results to process
1827 * @param fn Function for processing each result
1828 * @param data_area user-defined area to pass to function
1830 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1831 BOOL(*fn)(char *, void **, void *),
1837 if (!(ctx = talloc_init("ads_process_results")))
1840 for (msg = ads_first_entry(ads, res); msg;
1841 msg = ads_next_entry(ads, msg)) {
1845 for (utf8_field=ldap_first_attribute(ads->ld,
1846 (LDAPMessage *)msg,&b);
1848 utf8_field=ldap_next_attribute(ads->ld,
1849 (LDAPMessage *)msg,b)) {
1850 struct berval **ber_vals;
1851 char **str_vals, **utf8_vals;
1855 pull_utf8_talloc(ctx, &field, utf8_field);
1856 string = fn(field, NULL, data_area);
1859 utf8_vals = ldap_get_values(ads->ld,
1860 (LDAPMessage *)msg, field);
1861 str_vals = ads_pull_strvals(ctx,
1862 (const char **) utf8_vals);
1863 fn(field, (void **) str_vals, data_area);
1864 ldap_value_free(utf8_vals);
1866 ber_vals = ldap_get_values_len(ads->ld,
1867 (LDAPMessage *)msg, field);
1868 fn(field, (void **) ber_vals, data_area);
1870 ldap_value_free_len(ber_vals);
1872 ldap_memfree(utf8_field);
1875 talloc_free_children(ctx);
1876 fn(NULL, NULL, data_area); /* completed an entry */
1879 talloc_destroy(ctx);
1883 * count how many replies are in a LDAPMessage
1884 * @param ads connection to ads server
1885 * @param res Results to count
1886 * @return number of replies
1888 int ads_count_replies(ADS_STRUCT *ads, void *res)
1890 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1894 * pull the first entry from a ADS result
1895 * @param ads connection to ads server
1896 * @param res Results of search
1897 * @return first entry from result
1899 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1901 return ldap_first_entry(ads->ld, res);
1905 * pull the next entry from a ADS result
1906 * @param ads connection to ads server
1907 * @param res Results of search
1908 * @return next entry from result
1910 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1912 return ldap_next_entry(ads->ld, res);
1916 * pull a single string from a ADS result
1917 * @param ads connection to ads server
1918 * @param mem_ctx TALLOC_CTX to use for allocating result string
1919 * @param msg Results of search
1920 * @param field Attribute to retrieve
1921 * @return Result string in talloc context
1923 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
1931 values = ldap_get_values(ads->ld, msg, field);
1936 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1938 if (rc != (size_t)-1)
1942 ldap_value_free(values);
1947 * pull an array of strings from a ADS result
1948 * @param ads connection to ads server
1949 * @param mem_ctx TALLOC_CTX to use for allocating result string
1950 * @param msg Results of search
1951 * @param field Attribute to retrieve
1952 * @return Result strings in talloc context
1954 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1955 LDAPMessage *msg, const char *field,
1962 values = ldap_get_values(ads->ld, msg, field);
1966 *num_values = ldap_count_values(values);
1968 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1970 ldap_value_free(values);
1974 for (i=0;i<*num_values;i++) {
1975 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1976 ldap_value_free(values);
1982 ldap_value_free(values);
1987 * pull an array of strings from a ADS result
1988 * (handle large multivalue attributes with range retrieval)
1989 * @param ads connection to ads server
1990 * @param mem_ctx TALLOC_CTX to use for allocating result string
1991 * @param msg Results of search
1992 * @param field Attribute to retrieve
1993 * @param current_strings strings returned by a previous call to this function
1994 * @param next_attribute The next query should ask for this attribute
1995 * @param num_values How many values did we get this time?
1996 * @param more_values Are there more values to get?
1997 * @return Result strings in talloc context
1999 char **ads_pull_strings_range(ADS_STRUCT *ads,
2000 TALLOC_CTX *mem_ctx,
2001 LDAPMessage *msg, const char *field,
2002 char **current_strings,
2003 const char **next_attribute,
2004 size_t *num_strings,
2008 char *expected_range_attrib, *range_attr;
2009 BerElement *ptr = NULL;
2012 size_t num_new_strings;
2013 unsigned long int range_start;
2014 unsigned long int range_end;
2016 /* we might have been given the whole lot anyway */
2017 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2018 *more_strings = False;
2022 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2024 /* look for Range result */
2025 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2027 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2028 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2029 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2037 /* nothing here - this field is just empty */
2038 *more_strings = False;
2042 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2043 &range_start, &range_end) == 2) {
2044 *more_strings = True;
2046 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2047 &range_start) == 1) {
2048 *more_strings = False;
2050 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2052 ldap_memfree(range_attr);
2053 *more_strings = False;
2058 if ((*num_strings) != range_start) {
2059 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2060 " - aborting range retreival\n",
2061 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2062 ldap_memfree(range_attr);
2063 *more_strings = False;
2067 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2069 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2070 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2071 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2072 range_attr, (unsigned long int)range_end - range_start + 1,
2073 (unsigned long int)num_new_strings));
2074 ldap_memfree(range_attr);
2075 *more_strings = False;
2079 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2080 *num_strings + num_new_strings);
2082 if (strings == NULL) {
2083 ldap_memfree(range_attr);
2084 *more_strings = False;
2088 if (new_strings && num_new_strings) {
2089 memcpy(&strings[*num_strings], new_strings,
2090 sizeof(*new_strings) * num_new_strings);
2093 (*num_strings) += num_new_strings;
2095 if (*more_strings) {
2096 *next_attribute = talloc_asprintf(mem_ctx,
2101 if (!*next_attribute) {
2102 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2103 ldap_memfree(range_attr);
2104 *more_strings = False;
2109 ldap_memfree(range_attr);
2115 * pull a single uint32 from a ADS result
2116 * @param ads connection to ads server
2117 * @param msg Results of search
2118 * @param field Attribute to retrieve
2119 * @param v Pointer to int to store result
2120 * @return boolean inidicating success
2122 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2127 values = ldap_get_values(ads->ld, msg, field);
2131 ldap_value_free(values);
2135 *v = atoi(values[0]);
2136 ldap_value_free(values);
2141 * pull a single objectGUID from an ADS result
2142 * @param ads connection to ADS server
2143 * @param msg results of search
2144 * @param guid 37-byte area to receive text guid
2145 * @return boolean indicating success
2147 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2150 UUID_FLAT flat_guid;
2152 values = ldap_get_values(ads->ld, msg, "objectGUID");
2157 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2158 smb_uuid_unpack(flat_guid, guid);
2159 ldap_value_free(values);
2162 ldap_value_free(values);
2169 * pull a single DOM_SID from a ADS result
2170 * @param ads connection to ads server
2171 * @param msg Results of search
2172 * @param field Attribute to retrieve
2173 * @param sid Pointer to sid to store result
2174 * @return boolean inidicating success
2176 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2179 struct berval **values;
2182 values = ldap_get_values_len(ads->ld, msg, field);
2188 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2190 ldap_value_free_len(values);
2195 * pull an array of DOM_SIDs from a ADS result
2196 * @param ads connection to ads server
2197 * @param mem_ctx TALLOC_CTX for allocating sid array
2198 * @param msg Results of search
2199 * @param field Attribute to retrieve
2200 * @param sids pointer to sid array to allocate
2201 * @return the count of SIDs pulled
2203 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2204 LDAPMessage *msg, const char *field, DOM_SID **sids)
2206 struct berval **values;
2210 values = ldap_get_values_len(ads->ld, msg, field);
2215 for (i=0; values[i]; i++)
2218 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2220 ldap_value_free_len(values);
2225 for (i=0; values[i]; i++) {
2226 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2229 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2234 ldap_value_free_len(values);
2239 * pull a SEC_DESC from a ADS result
2240 * @param ads connection to ads server
2241 * @param mem_ctx TALLOC_CTX for allocating sid array
2242 * @param msg Results of search
2243 * @param field Attribute to retrieve
2244 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2245 * @return boolean inidicating success
2247 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2248 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2250 struct berval **values;
2254 values = ldap_get_values_len(ads->ld, msg, field);
2256 if (!values) return False;
2259 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2260 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2261 prs_set_offset(&ps,0);
2263 ret = sec_io_desc("sd", sd, &ps, 1);
2266 ldap_value_free_len(values);
2271 * in order to support usernames longer than 21 characters we need to
2272 * use both the sAMAccountName and the userPrincipalName attributes
2273 * It seems that not all users have the userPrincipalName attribute set
2275 * @param ads connection to ads server
2276 * @param mem_ctx TALLOC_CTX for allocating sid array
2277 * @param msg Results of search
2278 * @return the username
2280 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2286 /* lookup_name() only works on the sAMAccountName to
2287 returning the username portion of userPrincipalName
2288 breaks winbindd_getpwnam() */
2290 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2291 if (ret && (p = strchr_m(ret, '@'))) {
2296 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2301 * find the update serial number - this is the core of the ldap cache
2302 * @param ads connection to ads server
2303 * @param ads connection to ADS server
2304 * @param usn Pointer to retrieved update serial number
2305 * @return status of search
2307 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2309 const char *attrs[] = {"highestCommittedUSN", NULL};
2313 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2314 if (!ADS_ERR_OK(status))
2317 if (ads_count_replies(ads, res) != 1) {
2318 ads_msgfree(ads, res);
2319 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2322 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2323 ads_msgfree(ads, res);
2324 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2327 ads_msgfree(ads, res);
2331 /* parse a ADS timestring - typical string is
2332 '20020917091222.0Z0' which means 09:12.22 17th September
2334 static time_t ads_parse_time(const char *str)
2340 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2341 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2342 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2351 /********************************************************************
2352 ********************************************************************/
2354 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2356 const char *attrs[] = {"currentTime", NULL};
2361 ADS_STRUCT *ads_s = ads;
2363 if (!(ctx = talloc_init("ads_current_time"))) {
2364 return ADS_ERROR(LDAP_NO_MEMORY);
2367 /* establish a new ldap tcp session if necessary */
2370 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2371 ads->server.ldap_server )) == NULL )
2375 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2376 status = ads_connect( ads_s );
2377 if ( !ADS_ERR_OK(status))
2381 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2382 if (!ADS_ERR_OK(status)) {
2386 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2388 ads_msgfree(ads_s, res);
2389 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2393 /* but save the time and offset in the original ADS_STRUCT */
2395 ads->config.current_time = ads_parse_time(timestr);
2397 if (ads->config.current_time != 0) {
2398 ads->auth.time_offset = ads->config.current_time - time(NULL);
2399 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2402 ads_msgfree(ads, res);
2404 status = ADS_SUCCESS;
2407 /* free any temporary ads connections */
2408 if ( ads_s != ads ) {
2409 ads_destroy( &ads_s );
2411 talloc_destroy(ctx);
2416 /********************************************************************
2417 ********************************************************************/
2419 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2421 const char *attrs[] = {"domainFunctionality", NULL};
2424 ADS_STRUCT *ads_s = ads;
2426 *val = DS_DOMAIN_FUNCTION_2000;
2428 /* establish a new ldap tcp session if necessary */
2431 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2432 ads->server.ldap_server )) == NULL )
2436 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2437 status = ads_connect( ads_s );
2438 if ( !ADS_ERR_OK(status))
2442 /* If the attribute does not exist assume it is a Windows 2000
2443 functional domain */
2445 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2446 if (!ADS_ERR_OK(status)) {
2447 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2448 status = ADS_SUCCESS;
2453 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2454 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2456 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2459 ads_msgfree(ads, res);
2462 /* free any temporary ads connections */
2463 if ( ads_s != ads ) {
2464 ads_destroy( &ads_s );
2471 * find the domain sid for our domain
2472 * @param ads connection to ads server
2473 * @param sid Pointer to domain sid
2474 * @return status of search
2476 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2478 const char *attrs[] = {"objectSid", NULL};
2482 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2484 if (!ADS_ERR_OK(rc)) return rc;
2485 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2486 ads_msgfree(ads, res);
2487 return ADS_ERROR_SYSTEM(ENOENT);
2489 ads_msgfree(ads, res);
2495 * find our site name
2496 * @param ads connection to ads server
2497 * @param mem_ctx Pointer to talloc context
2498 * @param site_name Pointer to the sitename
2499 * @return status of search
2501 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2505 const char *dn, *service_name;
2506 const char *attrs[] = { "dsServiceName", NULL };
2508 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2509 if (!ADS_ERR_OK(status)) {
2513 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2514 if (service_name == NULL) {
2515 ads_msgfree(ads, res);
2516 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2519 ads_msgfree(ads, res);
2521 /* go up three levels */
2522 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2524 return ADS_ERROR(LDAP_NO_MEMORY);
2527 *site_name = talloc_strdup(mem_ctx, dn);
2528 if (*site_name == NULL) {
2529 return ADS_ERROR(LDAP_NO_MEMORY);
2534 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2539 * find the site dn where a machine resides
2540 * @param ads connection to ads server
2541 * @param mem_ctx Pointer to talloc context
2542 * @param computer_name name of the machine
2543 * @param site_name Pointer to the sitename
2544 * @return status of search
2546 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2550 const char *parent, *config_context, *filter;
2551 const char *attrs[] = { "configurationNamingContext", NULL };
2554 /* shortcut a query */
2555 if (strequal(computer_name, ads->config.ldap_server_name)) {
2556 return ads_site_dn(ads, mem_ctx, site_dn);
2559 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2560 if (!ADS_ERR_OK(status)) {
2564 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2565 if (config_context == NULL) {
2566 ads_msgfree(ads, res);
2567 return ADS_ERROR(LDAP_NO_MEMORY);
2570 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2571 if (filter == NULL) {
2572 ads_msgfree(ads, res);
2573 return ADS_ERROR(LDAP_NO_MEMORY);
2576 ads_msgfree(ads, res);
2578 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2579 if (!ADS_ERR_OK(status)) {
2583 if (ads_count_replies(ads, res) != 1) {
2584 ads_msgfree(ads, res);
2585 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2588 dn = ads_get_dn(ads, res);
2590 ads_msgfree(ads, res);
2591 return ADS_ERROR(LDAP_NO_MEMORY);
2594 /* go up three levels */
2595 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2596 if (parent == NULL) {
2597 ads_msgfree(ads, res);
2598 ads_memfree(ads, dn);
2599 return ADS_ERROR(LDAP_NO_MEMORY);
2602 *site_dn = talloc_strdup(mem_ctx, parent);
2603 if (*site_dn == NULL) {
2604 ads_msgfree(ads, res);
2605 ads_memfree(ads, dn);
2606 ADS_ERROR(LDAP_NO_MEMORY);
2609 ads_memfree(ads, dn);
2610 ads_msgfree(ads, res);
2616 * get the upn suffixes for a domain
2617 * @param ads connection to ads server
2618 * @param mem_ctx Pointer to talloc context
2619 * @param suffixes Pointer to an array of suffixes
2620 * @param site_name Pointer to the number of suffixes
2621 * @return status of search
2623 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2627 const char *config_context, *base;
2628 const char *attrs[] = { "configurationNamingContext", NULL };
2629 const char *attrs2[] = { "uPNSuffixes", NULL };
2631 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2632 if (!ADS_ERR_OK(status)) {
2636 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2637 if (config_context == NULL) {
2638 return ADS_ERROR(LDAP_NO_MEMORY);
2641 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2643 return ADS_ERROR(LDAP_NO_MEMORY);
2646 status = ads_search_dn(ads, &res, base, attrs2);
2647 if (!ADS_ERR_OK(status)) {
2651 if (ads_count_replies(ads, res) != 1) {
2652 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2655 suffixes = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2656 if (suffixes == NULL) {
2657 ads_msgfree(ads, res);
2658 return ADS_ERROR(LDAP_NO_MEMORY);
2661 ads_msgfree(ads, res);
2667 * pull a DOM_SID from an extended dn string
2668 * @param mem_ctx TALLOC_CTX
2669 * @param flags string type of extended_dn
2670 * @param sid pointer to a DOM_SID
2671 * @return boolean inidicating success
2673 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2675 enum ads_extended_dn_flags flags,
2685 * ADS_EXTENDED_DN_HEX_STRING:
2686 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2688 * ADS_EXTENDED_DN_STRING (only with w2k3):
2689 <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
2692 p = strchr(dn, ';');
2697 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2701 p += strlen(";<SID=");
2710 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2714 case ADS_EXTENDED_DN_STRING:
2715 if (!string_to_sid(sid, p)) {
2719 case ADS_EXTENDED_DN_HEX_STRING: {
2723 buf_len = strhex_to_str(buf, strlen(p), p);
2728 if (!sid_parse(buf, buf_len, sid)) {
2729 DEBUG(10,("failed to parse sid\n"));
2735 DEBUG(10,("unknown extended dn format\n"));
2743 * pull an array of DOM_SIDs from a ADS result
2744 * @param ads connection to ads server
2745 * @param mem_ctx TALLOC_CTX for allocating sid array
2746 * @param msg Results of search
2747 * @param field Attribute to retrieve
2748 * @param flags string type of extended_dn
2749 * @param sids pointer to sid array to allocate
2750 * @return the count of SIDs pulled
2752 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2753 TALLOC_CTX *mem_ctx,
2756 enum ads_extended_dn_flags flags,
2763 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2764 &dn_count)) == NULL) {
2768 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2770 TALLOC_FREE(dn_strings);
2774 for (i=0; i<dn_count; i++) {
2776 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2777 flags, &(*sids)[i])) {
2779 TALLOC_FREE(dn_strings);
2784 TALLOC_FREE(dn_strings);
2789 /********************************************************************
2790 ********************************************************************/
2792 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2794 LDAPMessage *res = NULL;
2799 status = ads_find_machine_acct(ads, &res, global_myname());
2800 if (!ADS_ERR_OK(status)) {
2801 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2806 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2807 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2811 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2812 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2816 ads_msgfree(ads, res);
2821 /********************************************************************
2822 ********************************************************************/
2824 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2826 LDAPMessage *res = NULL;
2831 status = ads_find_machine_acct(ads, &res, global_myname());
2832 if (!ADS_ERR_OK(status)) {
2833 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2838 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2839 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2843 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2844 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2848 ads_msgfree(ads, res);
2853 /********************************************************************
2854 ********************************************************************/
2856 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2858 LDAPMessage *res = NULL;
2863 status = ads_find_machine_acct(ads, &res, global_myname());
2864 if (!ADS_ERR_OK(status)) {
2865 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2870 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2871 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2875 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2876 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2880 ads_msgfree(ads, res);
2887 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2890 * Join a machine to a realm
2891 * Creates the machine account and sets the machine password
2892 * @param ads connection to ads server
2893 * @param machine name of host to add
2894 * @param org_unit Organizational unit to place machine in
2895 * @return status of join
2897 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2898 uint32 account_type, const char *org_unit)
2901 LDAPMessage *res = NULL;
2904 /* machine name must be lowercase */
2905 machine = SMB_STRDUP(machine_name);
2906 strlower_m(machine);
2909 status = ads_find_machine_acct(ads, (void **)&res, machine);
2910 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2911 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2912 status = ads_leave_realm(ads, machine);
2913 if (!ADS_ERR_OK(status)) {
2914 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2915 machine, ads->config.realm));
2920 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2921 if (!ADS_ERR_OK(status)) {
2922 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2927 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2928 if (!ADS_ERR_OK(status)) {
2929 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2935 ads_msgfree(ads, res);
2944 * Delete a machine from the realm
2945 * @param ads connection to ads server
2946 * @param hostname Machine to remove
2947 * @return status of delete
2949 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2954 char *hostnameDN, *host;
2956 LDAPControl ldap_control;
2957 LDAPControl * pldap_control[2] = {NULL, NULL};
2959 pldap_control[0] = &ldap_control;
2960 memset(&ldap_control, 0, sizeof(LDAPControl));
2961 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
2963 /* hostname must be lowercase */
2964 host = SMB_STRDUP(hostname);
2967 status = ads_find_machine_acct(ads, &res, host);
2968 if (!ADS_ERR_OK(status)) {
2969 DEBUG(0, ("Host account for %s does not exist.\n", host));
2974 msg = ads_first_entry(ads, res);
2977 return ADS_ERROR_SYSTEM(ENOENT);
2980 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
2982 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
2984 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
2986 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
2989 if (rc != LDAP_SUCCESS) {
2990 const char *attrs[] = { "cn", NULL };
2991 LDAPMessage *msg_sub;
2993 /* we only search with scope ONE, we do not expect any further
2994 * objects to be created deeper */
2996 status = ads_do_search_retry(ads, hostnameDN,
2997 LDAP_SCOPE_ONELEVEL,
2998 "(objectclass=*)", attrs, &res);
3000 if (!ADS_ERR_OK(status)) {
3002 ads_memfree(ads, hostnameDN);
3006 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3007 msg_sub = ads_next_entry(ads, msg_sub)) {
3011 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3013 ads_memfree(ads, hostnameDN);
3014 return ADS_ERROR(LDAP_NO_MEMORY);
3017 status = ads_del_dn(ads, dn);
3018 if (!ADS_ERR_OK(status)) {
3019 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3021 ads_memfree(ads, dn);
3022 ads_memfree(ads, hostnameDN);
3026 ads_memfree(ads, dn);
3029 /* there should be no subordinate objects anymore */
3030 status = ads_do_search_retry(ads, hostnameDN,
3031 LDAP_SCOPE_ONELEVEL,
3032 "(objectclass=*)", attrs, &res);
3034 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3036 ads_memfree(ads, hostnameDN);
3040 /* delete hostnameDN now */
3041 status = ads_del_dn(ads, hostnameDN);
3042 if (!ADS_ERR_OK(status)) {
3044 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3045 ads_memfree(ads, hostnameDN);
3050 ads_memfree(ads, hostnameDN);
3052 status = ads_find_machine_acct(ads, &res, host);
3053 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3054 DEBUG(3, ("Failed to remove host account.\n"));