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.domain, 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;
253 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
255 /* if the realm and workgroup are both empty, assume they are ours */
258 c_realm = ads->server.realm;
260 if ( !c_realm || !*c_realm ) {
261 /* special case where no realm and no workgroup means our own */
262 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
263 use_own_domain = True;
264 c_realm = lp_realm();
268 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 sitename = sitename_fetch(realm);
293 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
294 (got_realm ? "realm" : "domain"), realm));
296 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
297 if (!NT_STATUS_IS_OK(status)) {
298 /* fall back to netbios if we can */
299 if ( got_realm && !lp_disable_netbios() ) {
308 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
309 for ( i=0; i<count; i++ ) {
312 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
314 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
318 /* realm in this case is a workgroup name. We need
319 to ignore any IP addresses in the negative connection
320 cache that match ip addresses returned in the ad realm
321 case. It sucks that I have to reproduce the logic above... */
322 c_realm = ads->server.realm;
323 if ( !c_realm || !*c_realm ) {
324 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
325 c_realm = lp_realm();
328 if (c_realm && *c_realm &&
329 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
330 /* Ensure we add the workgroup name for this
331 IP address as negative too. */
332 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
337 if ( ads_try_connect(ads, server) ) {
343 /* keep track of failures */
344 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
349 /* In case we failed to contact one of our closest DC on our site we
350 * need to try to find another DC, retry with a site-less SRV DNS query
354 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
355 "trying to find another DC\n", sitename));
357 namecache_delete(realm, 0x1C);
361 return NT_STATUS_NO_LOGON_SERVERS;
366 * Connect to the LDAP server
367 * @param ads Pointer to an existing ADS_STRUCT
368 * @return status of connection
370 ADS_STATUS ads_connect(ADS_STRUCT *ads)
372 int version = LDAP_VERSION3;
376 ads->last_attempt = time(NULL);
379 /* try with a user specified server */
381 if (ads->server.ldap_server &&
382 ads_try_connect(ads, ads->server.ldap_server)) {
386 ntstatus = ads_find_dc(ads);
387 if (NT_STATUS_IS_OK(ntstatus)) {
391 return ADS_ERROR_NT(ntstatus);
394 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
396 if (!ads->auth.user_name) {
397 /* Must use the userPrincipalName value here or sAMAccountName
398 and not servicePrincipalName; found by Guenther Deschner */
400 asprintf(&ads->auth.user_name, "%s$", global_myname() );
403 if (!ads->auth.realm) {
404 ads->auth.realm = SMB_STRDUP(ads->config.realm);
407 if (!ads->auth.kdc_server) {
408 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
412 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
413 to MIT kerberos to work (tridge) */
416 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
417 setenv(env, ads->auth.kdc_server, 1);
422 /* If the caller() requested no LDAP bind, then we are done */
424 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
428 /* Otherwise setup the TCP LDAP session */
430 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
431 LDAP_PORT, lp_ldap_timeout())) == NULL )
433 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
436 /* cache the successful connection for workgroup and realm */
437 if (ads_closest_dc(ads)) {
438 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
439 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
442 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
444 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
445 if (!ADS_ERR_OK(status)) {
449 /* fill in the current time and offsets */
451 status = ads_current_time( ads );
452 if ( !ADS_ERR_OK(status) ) {
456 /* Now do the bind */
458 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
459 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
462 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
463 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
466 return ads_sasl_bind(ads);
470 Duplicate a struct berval into talloc'ed memory
472 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
474 struct berval *value;
476 if (!in_val) return NULL;
478 value = TALLOC_ZERO_P(ctx, struct berval);
481 if (in_val->bv_len == 0) return value;
483 value->bv_len = in_val->bv_len;
484 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
490 Make a values list out of an array of (struct berval *)
492 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
493 const struct berval **in_vals)
495 struct berval **values;
498 if (!in_vals) return NULL;
499 for (i=0; in_vals[i]; i++)
501 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
502 if (!values) return NULL;
504 for (i=0; in_vals[i]; i++) {
505 values[i] = dup_berval(ctx, in_vals[i]);
511 UTF8-encode a values list out of an array of (char *)
513 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
518 if (!in_vals) return NULL;
519 for (i=0; in_vals[i]; i++)
521 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
522 if (!values) return NULL;
524 for (i=0; in_vals[i]; i++) {
525 push_utf8_talloc(ctx, &values[i], in_vals[i]);
531 Pull a (char *) array out of a UTF8-encoded values list
533 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
538 if (!in_vals) return NULL;
539 for (i=0; in_vals[i]; i++)
541 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
542 if (!values) return NULL;
544 for (i=0; in_vals[i]; i++) {
545 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
551 * Do a search with paged results. cookie must be null on the first
552 * call, and then returned on each subsequent call. It will be null
553 * again when the entire search is complete
554 * @param ads connection to ads server
555 * @param bind_path Base dn for the search
556 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
557 * @param expr Search expression - specified in local charset
558 * @param attrs Attributes to retrieve - specified in utf8 or ascii
559 * @param res ** which will contain results - free res* with ads_msgfree()
560 * @param count Number of entries retrieved on this page
561 * @param cookie The paged results cookie to be returned on subsequent calls
562 * @return status of search
564 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
565 const char *bind_path,
566 int scope, const char *expr,
567 const char **attrs, void *args,
569 int *count, struct berval **cookie)
572 char *utf8_expr, *utf8_path, **search_attrs;
573 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
574 BerElement *cookie_be = NULL;
575 struct berval *cookie_bv= NULL;
576 BerElement *extdn_be = NULL;
577 struct berval *extdn_bv= NULL;
580 ads_control *external_control = (ads_control *) args;
584 if (!(ctx = talloc_init("ads_do_paged_search_args")))
585 return ADS_ERROR(LDAP_NO_MEMORY);
587 /* 0 means the conversion worked but the result was empty
588 so we only fail if it's -1. In any case, it always
589 at least nulls out the dest */
590 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
591 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
596 if (!attrs || !(*attrs))
599 /* This would be the utf8-encoded version...*/
600 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
601 if (!(str_list_copy(&search_attrs, attrs))) {
608 /* Paged results only available on ldap v3 or later */
609 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
610 if (version < LDAP_VERSION3) {
611 rc = LDAP_NOT_SUPPORTED;
615 cookie_be = ber_alloc_t(LBER_USE_DER);
617 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
618 ber_bvfree(*cookie); /* don't need it from last time */
621 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
623 ber_flatten(cookie_be, &cookie_bv);
624 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
625 PagedResults.ldctl_iscritical = (char) 1;
626 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
627 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
629 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
630 NoReferrals.ldctl_iscritical = (char) 0;
631 NoReferrals.ldctl_value.bv_len = 0;
632 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
634 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
636 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
637 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
639 /* win2k does not accept a ldctl_value beeing passed in */
641 if (external_control->val != 0) {
643 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
648 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
652 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
657 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
658 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
661 ExtendedDn.ldctl_value.bv_len = 0;
662 ExtendedDn.ldctl_value.bv_val = NULL;
665 controls[0] = &NoReferrals;
666 controls[1] = &PagedResults;
667 controls[2] = &ExtendedDn;
671 controls[0] = &NoReferrals;
672 controls[1] = &PagedResults;
676 /* we need to disable referrals as the openldap libs don't
677 handle them and paged results at the same time. Using them
678 together results in the result record containing the server
679 page control being removed from the result list (tridge/jmcd)
681 leaving this in despite the control that says don't generate
682 referrals, in case the server doesn't support it (jmcd)
684 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
686 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
687 search_attrs, 0, controls,
689 (LDAPMessage **)res);
691 ber_free(cookie_be, 1);
692 ber_bvfree(cookie_bv);
695 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
696 ldap_err2string(rc)));
700 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
701 NULL, &rcontrols, 0);
707 for (i=0; rcontrols[i]; i++) {
708 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
709 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
710 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
712 /* the berval is the cookie, but must be freed when
714 if (cookie_bv->bv_len) /* still more to do */
715 *cookie=ber_bvdup(cookie_bv);
718 ber_bvfree(cookie_bv);
719 ber_free(cookie_be, 1);
723 ldap_controls_free(rcontrols);
729 ber_free(extdn_be, 1);
733 ber_bvfree(extdn_bv);
736 /* if/when we decide to utf8-encode attrs, take out this next line */
737 str_list_free(&search_attrs);
739 return ADS_ERROR(rc);
742 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
743 int scope, const char *expr,
744 const char **attrs, LDAPMessage **res,
745 int *count, struct berval **cookie)
747 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
752 * Get all results for a search. This uses ads_do_paged_search() to return
753 * all entries in a large search.
754 * @param ads connection to ads server
755 * @param bind_path Base dn for the search
756 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
757 * @param expr Search expression
758 * @param attrs Attributes to retrieve
759 * @param res ** which will contain results - free res* with ads_msgfree()
760 * @return status of search
762 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
763 int scope, const char *expr,
764 const char **attrs, void *args,
767 struct berval *cookie = NULL;
772 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
775 if (!ADS_ERR_OK(status))
778 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
780 LDAPMessage *res2 = NULL;
782 LDAPMessage *msg, *next;
784 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
785 attrs, args, &res2, &count, &cookie);
787 if (!ADS_ERR_OK(status2)) break;
789 /* this relies on the way that ldap_add_result_entry() works internally. I hope
790 that this works on all ldap libs, but I have only tested with openldap */
791 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
792 next = ads_next_entry(ads, msg);
793 ldap_add_result_entry((LDAPMessage **)res, msg);
795 /* note that we do not free res2, as the memory is now
796 part of the main returned list */
799 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
800 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
806 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
807 int scope, const char *expr,
808 const char **attrs, LDAPMessage **res)
810 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
814 * Run a function on all results for a search. Uses ads_do_paged_search() and
815 * runs the function as each page is returned, using ads_process_results()
816 * @param ads connection to ads server
817 * @param bind_path Base dn for the search
818 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
819 * @param expr Search expression - specified in local charset
820 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
821 * @param fn Function which takes attr name, values list, and data_area
822 * @param data_area Pointer which is passed to function on each call
823 * @return status of search
825 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
826 int scope, const char *expr, const char **attrs,
827 BOOL(*fn)(char *, void **, void *),
830 struct berval *cookie = NULL;
835 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
838 if (!ADS_ERR_OK(status)) return status;
840 ads_process_results(ads, res, fn, data_area);
841 ads_msgfree(ads, res);
844 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
845 &res, &count, &cookie);
847 if (!ADS_ERR_OK(status)) break;
849 ads_process_results(ads, res, fn, data_area);
850 ads_msgfree(ads, res);
857 * Do a search with a timeout.
858 * @param ads connection to ads server
859 * @param bind_path Base dn for the search
860 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
861 * @param expr Search expression
862 * @param attrs Attributes to retrieve
863 * @param res ** which will contain results - free res* with ads_msgfree()
864 * @return status of search
866 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
868 const char **attrs, LDAPMessage **res)
871 char *utf8_expr, *utf8_path, **search_attrs = NULL;
875 if (!(ctx = talloc_init("ads_do_search"))) {
876 DEBUG(1,("ads_do_search: talloc_init() failed!"));
877 return ADS_ERROR(LDAP_NO_MEMORY);
880 /* 0 means the conversion worked but the result was empty
881 so we only fail if it's negative. In any case, it always
882 at least nulls out the dest */
883 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
884 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
885 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
890 if (!attrs || !(*attrs))
893 /* This would be the utf8-encoded version...*/
894 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
895 if (!(str_list_copy(&search_attrs, attrs)))
897 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
903 /* see the note in ads_do_paged_search - we *must* disable referrals */
904 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
906 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
907 search_attrs, 0, NULL, NULL,
909 (LDAPMessage **)res);
911 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
912 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
918 /* if/when we decide to utf8-encode attrs, take out this next line */
919 str_list_free(&search_attrs);
920 return ADS_ERROR(rc);
923 * Do a general ADS search
924 * @param ads connection to ads server
925 * @param res ** which will contain results - free res* with ads_msgfree()
926 * @param expr Search expression
927 * @param attrs Attributes to retrieve
928 * @return status of search
930 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
931 const char *expr, const char **attrs)
933 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
938 * Do a search on a specific DistinguishedName
939 * @param ads connection to ads server
940 * @param res ** which will contain results - free res* with ads_msgfree()
941 * @param dn DistinguishName to search
942 * @param attrs Attributes to retrieve
943 * @return status of search
945 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
946 const char *dn, const char **attrs)
948 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
953 * Free up memory from a ads_search
954 * @param ads connection to ads server
955 * @param msg Search results to free
957 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
964 * Free up memory from various ads requests
965 * @param ads connection to ads server
966 * @param mem Area to free
968 void ads_memfree(ADS_STRUCT *ads, void *mem)
974 * Get a dn from search results
975 * @param ads connection to ads server
976 * @param msg Search result
979 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
981 char *utf8_dn, *unix_dn;
983 utf8_dn = ldap_get_dn(ads->ld, msg);
986 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
990 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
991 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
995 ldap_memfree(utf8_dn);
1000 * Get the parent from a dn
1001 * @param dn the dn to return the parent from
1002 * @return parent dn string
1004 char *ads_parent_dn(const char *dn)
1012 p = strchr(dn, ',');
1022 * Find a machine account given a hostname
1023 * @param ads connection to ads server
1024 * @param res ** which will contain results - free res* with ads_msgfree()
1025 * @param host Hostname to search for
1026 * @return status of search
1028 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1029 const char *machine)
1033 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1037 /* the easiest way to find a machine account anywhere in the tree
1038 is to look for hostname$ */
1039 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1040 DEBUG(1, ("asprintf failed!\n"));
1041 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1044 status = ads_search(ads, res, expr, attrs);
1050 * Initialize a list of mods to be used in a modify request
1051 * @param ctx An initialized TALLOC_CTX
1052 * @return allocated ADS_MODLIST
1054 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1056 #define ADS_MODLIST_ALLOC_SIZE 10
1059 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1060 /* -1 is safety to make sure we don't go over the end.
1061 need to reset it to NULL before doing ldap modify */
1062 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1064 return (ADS_MODLIST)mods;
1069 add an attribute to the list, with values list already constructed
1071 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1072 int mod_op, const char *name,
1073 const void *_invals)
1075 const void **invals = (const void **)_invals;
1077 LDAPMod **modlist = (LDAPMod **) *mods;
1078 struct berval **ber_values = NULL;
1079 char **char_values = NULL;
1082 mod_op = LDAP_MOD_DELETE;
1084 if (mod_op & LDAP_MOD_BVALUES)
1085 ber_values = ads_dup_values(ctx,
1086 (const struct berval **)invals);
1088 char_values = ads_push_strvals(ctx,
1089 (const char **) invals);
1092 /* find the first empty slot */
1093 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1095 if (modlist[curmod] == (LDAPMod *) -1) {
1096 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1097 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1098 return ADS_ERROR(LDAP_NO_MEMORY);
1099 memset(&modlist[curmod], 0,
1100 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1101 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1102 *mods = (ADS_MODLIST)modlist;
1105 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1106 return ADS_ERROR(LDAP_NO_MEMORY);
1107 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1108 if (mod_op & LDAP_MOD_BVALUES) {
1109 modlist[curmod]->mod_bvalues = ber_values;
1110 } else if (mod_op & LDAP_MOD_DELETE) {
1111 modlist[curmod]->mod_values = NULL;
1113 modlist[curmod]->mod_values = char_values;
1116 modlist[curmod]->mod_op = mod_op;
1117 return ADS_ERROR(LDAP_SUCCESS);
1121 * Add a single string value to a mod list
1122 * @param ctx An initialized TALLOC_CTX
1123 * @param mods An initialized ADS_MODLIST
1124 * @param name The attribute name to add
1125 * @param val The value to add - NULL means DELETE
1126 * @return ADS STATUS indicating success of add
1128 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1129 const char *name, const char *val)
1131 const char *values[2];
1137 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1138 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1142 * Add an array of string values 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 vals The array of string values to add - NULL means DELETE
1147 * @return ADS STATUS indicating success of add
1149 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1150 const char *name, const char **vals)
1153 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1154 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1155 name, (const void **) vals);
1160 * Add a single ber-encoded value to a mod list
1161 * @param ctx An initialized TALLOC_CTX
1162 * @param mods An initialized ADS_MODLIST
1163 * @param name The attribute name to add
1164 * @param val The value to add - NULL means DELETE
1165 * @return ADS STATUS indicating success of add
1167 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1168 const char *name, const struct berval *val)
1170 const struct berval *values[2];
1175 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1176 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1177 name, (const void **) values);
1182 * Perform an ldap modify
1183 * @param ads connection to ads server
1184 * @param mod_dn DistinguishedName to modify
1185 * @param mods list of modifications to perform
1186 * @return status of modify
1188 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1191 char *utf8_dn = NULL;
1193 this control is needed to modify that contains a currently
1194 non-existent attribute (but allowable for the object) to run
1196 LDAPControl PermitModify = {
1197 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1200 LDAPControl *controls[2];
1202 controls[0] = &PermitModify;
1205 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1206 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1209 /* find the end of the list, marked by NULL or -1 */
1210 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1211 /* make sure the end of the list is NULL */
1213 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1214 (LDAPMod **) mods, controls, NULL);
1216 return ADS_ERROR(ret);
1220 * Perform an ldap add
1221 * @param ads connection to ads server
1222 * @param new_dn DistinguishedName to add
1223 * @param mods list of attributes and values for DN
1224 * @return status of add
1226 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1229 char *utf8_dn = NULL;
1231 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1232 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1233 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1236 /* find the end of the list, marked by NULL or -1 */
1237 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1238 /* make sure the end of the list is NULL */
1241 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1243 return ADS_ERROR(ret);
1247 * Delete a DistinguishedName
1248 * @param ads connection to ads server
1249 * @param new_dn DistinguishedName to delete
1250 * @return status of delete
1252 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1255 char *utf8_dn = NULL;
1256 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1257 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1258 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1261 ret = ldap_delete_s(ads->ld, utf8_dn);
1263 return ADS_ERROR(ret);
1267 * Build an org unit string
1268 * if org unit is Computers or blank then assume a container, otherwise
1269 * assume a / separated list of organisational units.
1270 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1271 * @param ads connection to ads server
1272 * @param org_unit Organizational unit
1273 * @return org unit string - caller must free
1275 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1279 if (!org_unit || !*org_unit) {
1281 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1283 /* samba4 might not yet respond to a wellknownobject-query */
1284 return ret ? ret : SMB_STRDUP("cn=Computers");
1287 if (strequal(org_unit, "Computers")) {
1288 return SMB_STRDUP("cn=Computers");
1291 /* jmcd: removed "\\" from the separation chars, because it is
1292 needed as an escape for chars like '#' which are valid in an
1294 return ads_build_path(org_unit, "/", "ou=", 1);
1298 * Get a org unit string for a well-known GUID
1299 * @param ads connection to ads server
1300 * @param wknguid Well known GUID
1301 * @return org unit string - caller must free
1303 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1306 LDAPMessage *res = NULL;
1307 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1308 **bind_dn_exp = NULL;
1309 const char *attrs[] = {"distinguishedName", NULL};
1310 int new_ln, wkn_ln, bind_ln, i;
1312 if (wknguid == NULL) {
1316 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1317 DEBUG(1, ("asprintf failed!\n"));
1321 status = ads_search_dn(ads, &res, base, attrs);
1322 if (!ADS_ERR_OK(status)) {
1323 DEBUG(1,("Failed while searching for: %s\n", base));
1327 if (ads_count_replies(ads, res) != 1) {
1331 /* substitute the bind-path from the well-known-guid-search result */
1332 wkn_dn = ads_get_dn(ads, res);
1337 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1342 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1347 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1349 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1352 new_ln = wkn_ln - bind_ln;
1354 ret = SMB_STRDUP(wkn_dn_exp[0]);
1359 for (i=1; i < new_ln; i++) {
1362 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1368 ret = SMB_STRDUP(s);
1377 ads_msgfree(ads, res);
1378 ads_memfree(ads, wkn_dn);
1380 ldap_value_free(wkn_dn_exp);
1383 ldap_value_free(bind_dn_exp);
1390 * Adds (appends) an item to an attribute array, rather then
1391 * replacing the whole list
1392 * @param ctx An initialized TALLOC_CTX
1393 * @param mods An initialized ADS_MODLIST
1394 * @param name name of the ldap attribute to append to
1395 * @param vals an array of values to add
1396 * @return status of addition
1399 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1400 const char *name, const char **vals)
1402 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1403 (const void *) vals);
1407 * Determines the computer account's current KVNO via an LDAP lookup
1408 * @param ads An initialized ADS_STRUCT
1409 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1410 * @return the kvno for the computer account, or -1 in case of a failure.
1413 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1415 LDAPMessage *res = NULL;
1416 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1418 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1419 char *dn_string = NULL;
1420 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1422 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1423 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1426 ret = ads_search(ads, &res, filter, attrs);
1428 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1429 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1430 ads_msgfree(ads, res);
1434 dn_string = ads_get_dn(ads, res);
1436 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1437 ads_msgfree(ads, res);
1440 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1441 ads_memfree(ads, dn_string);
1443 /* ---------------------------------------------------------
1444 * 0 is returned as a default KVNO from this point on...
1445 * This is done because Windows 2000 does not support key
1446 * version numbers. Chances are that a failure in the next
1447 * step is simply due to Windows 2000 being used for a
1448 * domain controller. */
1451 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1452 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1453 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1454 ads_msgfree(ads, res);
1459 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1460 ads_msgfree(ads, res);
1465 * This clears out all registered spn's for a given hostname
1466 * @param ads An initilaized ADS_STRUCT
1467 * @param machine_name the NetBIOS name of the computer.
1468 * @return 0 upon success, non-zero otherwise.
1471 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1474 LDAPMessage *res = NULL;
1476 const char *servicePrincipalName[1] = {NULL};
1477 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1478 char *dn_string = NULL;
1480 ret = ads_find_machine_acct(ads, &res, machine_name);
1481 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1482 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1483 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1484 ads_msgfree(ads, res);
1485 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1488 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1489 ctx = talloc_init("ads_clear_service_principal_names");
1491 ads_msgfree(ads, res);
1492 return ADS_ERROR(LDAP_NO_MEMORY);
1495 if (!(mods = ads_init_mods(ctx))) {
1496 talloc_destroy(ctx);
1497 ads_msgfree(ads, res);
1498 return ADS_ERROR(LDAP_NO_MEMORY);
1500 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1501 if (!ADS_ERR_OK(ret)) {
1502 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1503 ads_msgfree(ads, res);
1504 talloc_destroy(ctx);
1507 dn_string = ads_get_dn(ads, res);
1509 talloc_destroy(ctx);
1510 ads_msgfree(ads, res);
1511 return ADS_ERROR(LDAP_NO_MEMORY);
1513 ret = ads_gen_mod(ads, dn_string, mods);
1514 ads_memfree(ads,dn_string);
1515 if (!ADS_ERR_OK(ret)) {
1516 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1518 ads_msgfree(ads, res);
1519 talloc_destroy(ctx);
1523 ads_msgfree(ads, res);
1524 talloc_destroy(ctx);
1529 * This adds a service principal name to an existing computer account
1530 * (found by hostname) in AD.
1531 * @param ads An initialized ADS_STRUCT
1532 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1533 * @param my_fqdn The fully qualified DNS name of the machine
1534 * @param spn A string of the service principal to add, i.e. 'host'
1535 * @return 0 upon sucess, or non-zero if a failure occurs
1538 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1539 const char *my_fqdn, const char *spn)
1543 LDAPMessage *res = NULL;
1546 char *dn_string = NULL;
1547 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1549 ret = ads_find_machine_acct(ads, &res, machine_name);
1550 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1551 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1553 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1554 spn, machine_name, ads->config.realm));
1555 ads_msgfree(ads, res);
1556 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1559 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1560 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1561 ads_msgfree(ads, res);
1562 return ADS_ERROR(LDAP_NO_MEMORY);
1565 /* add short name spn */
1567 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1568 talloc_destroy(ctx);
1569 ads_msgfree(ads, res);
1570 return ADS_ERROR(LDAP_NO_MEMORY);
1573 strlower_m(&psp1[strlen(spn)]);
1574 servicePrincipalName[0] = psp1;
1576 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1577 psp1, machine_name));
1580 /* add fully qualified spn */
1582 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1583 ret = ADS_ERROR(LDAP_NO_MEMORY);
1587 strlower_m(&psp2[strlen(spn)]);
1588 servicePrincipalName[1] = psp2;
1590 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1591 psp2, machine_name));
1593 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1594 ret = ADS_ERROR(LDAP_NO_MEMORY);
1598 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1599 if (!ADS_ERR_OK(ret)) {
1600 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1604 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1605 ret = ADS_ERROR(LDAP_NO_MEMORY);
1609 ret = ads_gen_mod(ads, dn_string, mods);
1610 ads_memfree(ads,dn_string);
1611 if (!ADS_ERR_OK(ret)) {
1612 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1618 ads_msgfree(ads, res);
1623 * adds a machine account to the ADS server
1624 * @param ads An intialized ADS_STRUCT
1625 * @param machine_name - the NetBIOS machine name of this account.
1626 * @param account_type A number indicating the type of account to create
1627 * @param org_unit The LDAP path in which to place this account
1628 * @return 0 upon success, or non-zero otherwise
1631 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1632 const char *org_unit)
1635 char *samAccountName, *controlstr;
1639 const char *objectClass[] = {"top", "person", "organizationalPerson",
1640 "user", "computer", NULL};
1641 LDAPMessage *res = NULL;
1642 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1643 UF_DONT_EXPIRE_PASSWD |\
1644 UF_ACCOUNTDISABLE );
1646 if (!(ctx = talloc_init("ads_add_machine_acct")))
1647 return ADS_ERROR(LDAP_NO_MEMORY);
1649 ret = ADS_ERROR(LDAP_NO_MEMORY);
1651 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1652 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1654 if ( !new_dn || !samAccountName ) {
1658 #ifndef ENCTYPE_ARCFOUR_HMAC
1659 acct_control |= UF_USE_DES_KEY_ONLY;
1662 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1666 if (!(mods = ads_init_mods(ctx))) {
1670 ads_mod_str(ctx, &mods, "cn", machine_name);
1671 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1672 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1673 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1675 ret = ads_gen_add(ads, new_dn, mods);
1678 ads_msgfree(ads, res);
1679 talloc_destroy(ctx);
1685 dump a binary result from ldap
1687 static void dump_binary(const char *field, struct berval **values)
1690 for (i=0; values[i]; i++) {
1691 printf("%s: ", field);
1692 for (j=0; j<values[i]->bv_len; j++) {
1693 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1699 static void dump_guid(const char *field, struct berval **values)
1703 for (i=0; values[i]; i++) {
1704 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1705 printf("%s: %s\n", field,
1706 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1711 dump a sid result from ldap
1713 static void dump_sid(const char *field, struct berval **values)
1716 for (i=0; values[i]; i++) {
1718 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1719 printf("%s: %s\n", field, sid_string_static(&sid));
1724 dump ntSecurityDescriptor
1726 static void dump_sd(const char *filed, struct berval **values)
1731 TALLOC_CTX *ctx = 0;
1733 if (!(ctx = talloc_init("sec_io_desc")))
1737 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1738 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1739 prs_set_offset(&ps,0);
1742 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1744 talloc_destroy(ctx);
1747 if (psd) ads_disp_sd(psd);
1750 talloc_destroy(ctx);
1754 dump a string result from ldap
1756 static void dump_string(const char *field, char **values)
1759 for (i=0; values[i]; i++) {
1760 printf("%s: %s\n", field, values[i]);
1765 dump a field from LDAP on stdout
1769 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1774 void (*handler)(const char *, struct berval **);
1776 {"objectGUID", False, dump_guid},
1777 {"netbootGUID", False, dump_guid},
1778 {"nTSecurityDescriptor", False, dump_sd},
1779 {"dnsRecord", False, dump_binary},
1780 {"objectSid", False, dump_sid},
1781 {"tokenGroups", False, dump_sid},
1782 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1783 {"tokengroupsGlobalandUniversal", False, dump_sid},
1784 {"mS-DS-CreatorSID", False, dump_sid},
1789 if (!field) { /* must be end of an entry */
1794 for (i=0; handlers[i].name; i++) {
1795 if (StrCaseCmp(handlers[i].name, field) == 0) {
1796 if (!values) /* first time, indicate string or not */
1797 return handlers[i].string;
1798 handlers[i].handler(field, (struct berval **) values);
1802 if (!handlers[i].name) {
1803 if (!values) /* first time, indicate string conversion */
1805 dump_string(field, (char **)values);
1811 * Dump a result from LDAP on stdout
1812 * used for debugging
1813 * @param ads connection to ads server
1814 * @param res Results to dump
1817 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1819 ads_process_results(ads, res, ads_dump_field, NULL);
1823 * Walk through results, calling a function for each entry found.
1824 * The function receives a field name, a berval * array of values,
1825 * and a data area passed through from the start. The function is
1826 * called once with null for field and values at the end of each
1828 * @param ads connection to ads server
1829 * @param res Results to process
1830 * @param fn Function for processing each result
1831 * @param data_area user-defined area to pass to function
1833 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1834 BOOL(*fn)(char *, void **, void *),
1840 if (!(ctx = talloc_init("ads_process_results")))
1843 for (msg = ads_first_entry(ads, res); msg;
1844 msg = ads_next_entry(ads, msg)) {
1848 for (utf8_field=ldap_first_attribute(ads->ld,
1849 (LDAPMessage *)msg,&b);
1851 utf8_field=ldap_next_attribute(ads->ld,
1852 (LDAPMessage *)msg,b)) {
1853 struct berval **ber_vals;
1854 char **str_vals, **utf8_vals;
1858 pull_utf8_talloc(ctx, &field, utf8_field);
1859 string = fn(field, NULL, data_area);
1862 utf8_vals = ldap_get_values(ads->ld,
1863 (LDAPMessage *)msg, field);
1864 str_vals = ads_pull_strvals(ctx,
1865 (const char **) utf8_vals);
1866 fn(field, (void **) str_vals, data_area);
1867 ldap_value_free(utf8_vals);
1869 ber_vals = ldap_get_values_len(ads->ld,
1870 (LDAPMessage *)msg, field);
1871 fn(field, (void **) ber_vals, data_area);
1873 ldap_value_free_len(ber_vals);
1875 ldap_memfree(utf8_field);
1878 talloc_free_children(ctx);
1879 fn(NULL, NULL, data_area); /* completed an entry */
1882 talloc_destroy(ctx);
1886 * count how many replies are in a LDAPMessage
1887 * @param ads connection to ads server
1888 * @param res Results to count
1889 * @return number of replies
1891 int ads_count_replies(ADS_STRUCT *ads, void *res)
1893 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1897 * pull the first entry from a ADS result
1898 * @param ads connection to ads server
1899 * @param res Results of search
1900 * @return first entry from result
1902 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1904 return ldap_first_entry(ads->ld, res);
1908 * pull the next entry from a ADS result
1909 * @param ads connection to ads server
1910 * @param res Results of search
1911 * @return next entry from result
1913 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1915 return ldap_next_entry(ads->ld, res);
1919 * pull a single string from a ADS result
1920 * @param ads connection to ads server
1921 * @param mem_ctx TALLOC_CTX to use for allocating result string
1922 * @param msg Results of search
1923 * @param field Attribute to retrieve
1924 * @return Result string in talloc context
1926 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
1934 values = ldap_get_values(ads->ld, msg, field);
1939 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1941 if (rc != (size_t)-1)
1945 ldap_value_free(values);
1950 * pull an array of strings from a ADS result
1951 * @param ads connection to ads server
1952 * @param mem_ctx TALLOC_CTX to use for allocating result string
1953 * @param msg Results of search
1954 * @param field Attribute to retrieve
1955 * @return Result strings in talloc context
1957 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1958 LDAPMessage *msg, const char *field,
1965 values = ldap_get_values(ads->ld, msg, field);
1969 *num_values = ldap_count_values(values);
1971 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1973 ldap_value_free(values);
1977 for (i=0;i<*num_values;i++) {
1978 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1979 ldap_value_free(values);
1985 ldap_value_free(values);
1990 * pull an array of strings from a ADS result
1991 * (handle large multivalue attributes with range retrieval)
1992 * @param ads connection to ads server
1993 * @param mem_ctx TALLOC_CTX to use for allocating result string
1994 * @param msg Results of search
1995 * @param field Attribute to retrieve
1996 * @param current_strings strings returned by a previous call to this function
1997 * @param next_attribute The next query should ask for this attribute
1998 * @param num_values How many values did we get this time?
1999 * @param more_values Are there more values to get?
2000 * @return Result strings in talloc context
2002 char **ads_pull_strings_range(ADS_STRUCT *ads,
2003 TALLOC_CTX *mem_ctx,
2004 LDAPMessage *msg, const char *field,
2005 char **current_strings,
2006 const char **next_attribute,
2007 size_t *num_strings,
2011 char *expected_range_attrib, *range_attr;
2012 BerElement *ptr = NULL;
2015 size_t num_new_strings;
2016 unsigned long int range_start;
2017 unsigned long int range_end;
2019 /* we might have been given the whole lot anyway */
2020 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2021 *more_strings = False;
2025 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2027 /* look for Range result */
2028 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2030 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2031 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2032 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2040 /* nothing here - this field is just empty */
2041 *more_strings = False;
2045 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2046 &range_start, &range_end) == 2) {
2047 *more_strings = True;
2049 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2050 &range_start) == 1) {
2051 *more_strings = False;
2053 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2055 ldap_memfree(range_attr);
2056 *more_strings = False;
2061 if ((*num_strings) != range_start) {
2062 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2063 " - aborting range retreival\n",
2064 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2065 ldap_memfree(range_attr);
2066 *more_strings = False;
2070 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2072 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2073 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2074 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2075 range_attr, (unsigned long int)range_end - range_start + 1,
2076 (unsigned long int)num_new_strings));
2077 ldap_memfree(range_attr);
2078 *more_strings = False;
2082 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2083 *num_strings + num_new_strings);
2085 if (strings == NULL) {
2086 ldap_memfree(range_attr);
2087 *more_strings = False;
2091 if (new_strings && num_new_strings) {
2092 memcpy(&strings[*num_strings], new_strings,
2093 sizeof(*new_strings) * num_new_strings);
2096 (*num_strings) += num_new_strings;
2098 if (*more_strings) {
2099 *next_attribute = talloc_asprintf(mem_ctx,
2104 if (!*next_attribute) {
2105 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2106 ldap_memfree(range_attr);
2107 *more_strings = False;
2112 ldap_memfree(range_attr);
2118 * pull a single uint32 from a ADS result
2119 * @param ads connection to ads server
2120 * @param msg Results of search
2121 * @param field Attribute to retrieve
2122 * @param v Pointer to int to store result
2123 * @return boolean inidicating success
2125 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2130 values = ldap_get_values(ads->ld, msg, field);
2134 ldap_value_free(values);
2138 *v = atoi(values[0]);
2139 ldap_value_free(values);
2144 * pull a single objectGUID from an ADS result
2145 * @param ads connection to ADS server
2146 * @param msg results of search
2147 * @param guid 37-byte area to receive text guid
2148 * @return boolean indicating success
2150 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2153 UUID_FLAT flat_guid;
2155 values = ldap_get_values(ads->ld, msg, "objectGUID");
2160 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2161 smb_uuid_unpack(flat_guid, guid);
2162 ldap_value_free(values);
2165 ldap_value_free(values);
2172 * pull a single DOM_SID from a ADS result
2173 * @param ads connection to ads server
2174 * @param msg Results of search
2175 * @param field Attribute to retrieve
2176 * @param sid Pointer to sid to store result
2177 * @return boolean inidicating success
2179 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2182 struct berval **values;
2185 values = ldap_get_values_len(ads->ld, msg, field);
2191 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2193 ldap_value_free_len(values);
2198 * pull an array of DOM_SIDs from a ADS result
2199 * @param ads connection to ads server
2200 * @param mem_ctx TALLOC_CTX for allocating sid array
2201 * @param msg Results of search
2202 * @param field Attribute to retrieve
2203 * @param sids pointer to sid array to allocate
2204 * @return the count of SIDs pulled
2206 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2207 LDAPMessage *msg, const char *field, DOM_SID **sids)
2209 struct berval **values;
2213 values = ldap_get_values_len(ads->ld, msg, field);
2218 for (i=0; values[i]; i++)
2221 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2223 ldap_value_free_len(values);
2228 for (i=0; values[i]; i++) {
2229 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2232 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2237 ldap_value_free_len(values);
2242 * pull a SEC_DESC from a ADS result
2243 * @param ads connection to ads server
2244 * @param mem_ctx TALLOC_CTX for allocating sid array
2245 * @param msg Results of search
2246 * @param field Attribute to retrieve
2247 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2248 * @return boolean inidicating success
2250 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2251 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2253 struct berval **values;
2257 values = ldap_get_values_len(ads->ld, msg, field);
2259 if (!values) return False;
2262 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2263 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2264 prs_set_offset(&ps,0);
2266 ret = sec_io_desc("sd", sd, &ps, 1);
2269 ldap_value_free_len(values);
2274 * in order to support usernames longer than 21 characters we need to
2275 * use both the sAMAccountName and the userPrincipalName attributes
2276 * It seems that not all users have the userPrincipalName attribute set
2278 * @param ads connection to ads server
2279 * @param mem_ctx TALLOC_CTX for allocating sid array
2280 * @param msg Results of search
2281 * @return the username
2283 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2289 /* lookup_name() only works on the sAMAccountName to
2290 returning the username portion of userPrincipalName
2291 breaks winbindd_getpwnam() */
2293 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2294 if (ret && (p = strchr_m(ret, '@'))) {
2299 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2304 * find the update serial number - this is the core of the ldap cache
2305 * @param ads connection to ads server
2306 * @param ads connection to ADS server
2307 * @param usn Pointer to retrieved update serial number
2308 * @return status of search
2310 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2312 const char *attrs[] = {"highestCommittedUSN", NULL};
2316 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2317 if (!ADS_ERR_OK(status))
2320 if (ads_count_replies(ads, res) != 1) {
2321 ads_msgfree(ads, res);
2322 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2325 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2326 ads_msgfree(ads, res);
2327 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2330 ads_msgfree(ads, res);
2334 /* parse a ADS timestring - typical string is
2335 '20020917091222.0Z0' which means 09:12.22 17th September
2337 static time_t ads_parse_time(const char *str)
2343 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2344 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2345 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2354 /********************************************************************
2355 ********************************************************************/
2357 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2359 const char *attrs[] = {"currentTime", NULL};
2364 ADS_STRUCT *ads_s = ads;
2366 if (!(ctx = talloc_init("ads_current_time"))) {
2367 return ADS_ERROR(LDAP_NO_MEMORY);
2370 /* establish a new ldap tcp session if necessary */
2373 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2374 ads->server.ldap_server )) == NULL )
2378 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2379 status = ads_connect( ads_s );
2380 if ( !ADS_ERR_OK(status))
2384 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2385 if (!ADS_ERR_OK(status)) {
2389 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2391 ads_msgfree(ads_s, res);
2392 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2396 /* but save the time and offset in the original ADS_STRUCT */
2398 ads->config.current_time = ads_parse_time(timestr);
2400 if (ads->config.current_time != 0) {
2401 ads->auth.time_offset = ads->config.current_time - time(NULL);
2402 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2405 ads_msgfree(ads, res);
2407 status = ADS_SUCCESS;
2410 /* free any temporary ads connections */
2411 if ( ads_s != ads ) {
2412 ads_destroy( &ads_s );
2414 talloc_destroy(ctx);
2419 /********************************************************************
2420 ********************************************************************/
2422 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2424 const char *attrs[] = {"domainFunctionality", NULL};
2427 ADS_STRUCT *ads_s = ads;
2429 *val = DS_DOMAIN_FUNCTION_2000;
2431 /* establish a new ldap tcp session if necessary */
2434 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2435 ads->server.ldap_server )) == NULL )
2439 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2440 status = ads_connect( ads_s );
2441 if ( !ADS_ERR_OK(status))
2445 /* If the attribute does not exist assume it is a Windows 2000
2446 functional domain */
2448 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2449 if (!ADS_ERR_OK(status)) {
2450 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2451 status = ADS_SUCCESS;
2456 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2457 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2459 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2462 ads_msgfree(ads, res);
2465 /* free any temporary ads connections */
2466 if ( ads_s != ads ) {
2467 ads_destroy( &ads_s );
2474 * find the domain sid for our domain
2475 * @param ads connection to ads server
2476 * @param sid Pointer to domain sid
2477 * @return status of search
2479 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2481 const char *attrs[] = {"objectSid", NULL};
2485 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2487 if (!ADS_ERR_OK(rc)) return rc;
2488 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2489 ads_msgfree(ads, res);
2490 return ADS_ERROR_SYSTEM(ENOENT);
2492 ads_msgfree(ads, res);
2498 * find our site name
2499 * @param ads connection to ads server
2500 * @param mem_ctx Pointer to talloc context
2501 * @param site_name Pointer to the sitename
2502 * @return status of search
2504 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2508 const char *dn, *service_name;
2509 const char *attrs[] = { "dsServiceName", NULL };
2511 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2512 if (!ADS_ERR_OK(status)) {
2516 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2517 if (service_name == NULL) {
2518 ads_msgfree(ads, res);
2519 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2522 ads_msgfree(ads, res);
2524 /* go up three levels */
2525 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2527 return ADS_ERROR(LDAP_NO_MEMORY);
2530 *site_name = talloc_strdup(mem_ctx, dn);
2531 if (*site_name == NULL) {
2532 return ADS_ERROR(LDAP_NO_MEMORY);
2537 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2542 * find the site dn where a machine resides
2543 * @param ads connection to ads server
2544 * @param mem_ctx Pointer to talloc context
2545 * @param computer_name name of the machine
2546 * @param site_name Pointer to the sitename
2547 * @return status of search
2549 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2553 const char *parent, *config_context, *filter;
2554 const char *attrs[] = { "configurationNamingContext", NULL };
2557 /* shortcut a query */
2558 if (strequal(computer_name, ads->config.ldap_server_name)) {
2559 return ads_site_dn(ads, mem_ctx, site_dn);
2562 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2563 if (!ADS_ERR_OK(status)) {
2567 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2568 if (config_context == NULL) {
2569 ads_msgfree(ads, res);
2570 return ADS_ERROR(LDAP_NO_MEMORY);
2573 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2574 if (filter == NULL) {
2575 ads_msgfree(ads, res);
2576 return ADS_ERROR(LDAP_NO_MEMORY);
2579 ads_msgfree(ads, res);
2581 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2582 if (!ADS_ERR_OK(status)) {
2586 if (ads_count_replies(ads, res) != 1) {
2587 ads_msgfree(ads, res);
2588 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2591 dn = ads_get_dn(ads, res);
2593 ads_msgfree(ads, res);
2594 return ADS_ERROR(LDAP_NO_MEMORY);
2597 /* go up three levels */
2598 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2599 if (parent == NULL) {
2600 ads_msgfree(ads, res);
2601 ads_memfree(ads, dn);
2602 return ADS_ERROR(LDAP_NO_MEMORY);
2605 *site_dn = talloc_strdup(mem_ctx, parent);
2606 if (*site_dn == NULL) {
2607 ads_msgfree(ads, res);
2608 ads_memfree(ads, dn);
2609 ADS_ERROR(LDAP_NO_MEMORY);
2612 ads_memfree(ads, dn);
2613 ads_msgfree(ads, res);
2619 * get the upn suffixes for a domain
2620 * @param ads connection to ads server
2621 * @param mem_ctx Pointer to talloc context
2622 * @param suffixes Pointer to an array of suffixes
2623 * @param site_name Pointer to the number of suffixes
2624 * @return status of search
2626 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2630 const char *config_context, *base;
2631 const char *attrs[] = { "configurationNamingContext", NULL };
2632 const char *attrs2[] = { "uPNSuffixes", NULL };
2634 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2635 if (!ADS_ERR_OK(status)) {
2639 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2640 if (config_context == NULL) {
2641 ads_msgfree(ads, res);
2642 return ADS_ERROR(LDAP_NO_MEMORY);
2645 ads_msgfree(ads, res);
2647 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2649 return ADS_ERROR(LDAP_NO_MEMORY);
2652 status = ads_search_dn(ads, &res, base, attrs2);
2653 if (!ADS_ERR_OK(status)) {
2657 if (ads_count_replies(ads, res) != 1) {
2658 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2661 suffixes = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2662 if (suffixes == NULL) {
2663 ads_msgfree(ads, res);
2664 return ADS_ERROR(LDAP_NO_MEMORY);
2667 ads_msgfree(ads, res);
2673 * pull a DOM_SID from an extended dn string
2674 * @param mem_ctx TALLOC_CTX
2675 * @param flags string type of extended_dn
2676 * @param sid pointer to a DOM_SID
2677 * @return boolean inidicating success
2679 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2681 enum ads_extended_dn_flags flags,
2691 * ADS_EXTENDED_DN_HEX_STRING:
2692 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2694 * ADS_EXTENDED_DN_STRING (only with w2k3):
2695 <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
2698 p = strchr(dn, ';');
2703 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2707 p += strlen(";<SID=");
2716 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2720 case ADS_EXTENDED_DN_STRING:
2721 if (!string_to_sid(sid, p)) {
2725 case ADS_EXTENDED_DN_HEX_STRING: {
2729 buf_len = strhex_to_str(buf, strlen(p), p);
2734 if (!sid_parse(buf, buf_len, sid)) {
2735 DEBUG(10,("failed to parse sid\n"));
2741 DEBUG(10,("unknown extended dn format\n"));
2749 * pull an array of DOM_SIDs from a ADS result
2750 * @param ads connection to ads server
2751 * @param mem_ctx TALLOC_CTX for allocating sid array
2752 * @param msg Results of search
2753 * @param field Attribute to retrieve
2754 * @param flags string type of extended_dn
2755 * @param sids pointer to sid array to allocate
2756 * @return the count of SIDs pulled
2758 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2759 TALLOC_CTX *mem_ctx,
2762 enum ads_extended_dn_flags flags,
2769 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2770 &dn_count)) == NULL) {
2774 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2776 TALLOC_FREE(dn_strings);
2780 for (i=0; i<dn_count; i++) {
2782 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2783 flags, &(*sids)[i])) {
2785 TALLOC_FREE(dn_strings);
2790 TALLOC_FREE(dn_strings);
2795 /********************************************************************
2796 ********************************************************************/
2798 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2800 LDAPMessage *res = NULL;
2805 status = ads_find_machine_acct(ads, &res, global_myname());
2806 if (!ADS_ERR_OK(status)) {
2807 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2812 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2813 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2817 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2818 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2822 ads_msgfree(ads, res);
2827 /********************************************************************
2828 ********************************************************************/
2830 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2832 LDAPMessage *res = NULL;
2837 status = ads_find_machine_acct(ads, &res, global_myname());
2838 if (!ADS_ERR_OK(status)) {
2839 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2844 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2845 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2849 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2850 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2854 ads_msgfree(ads, res);
2859 /********************************************************************
2860 ********************************************************************/
2862 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2864 LDAPMessage *res = NULL;
2869 status = ads_find_machine_acct(ads, &res, global_myname());
2870 if (!ADS_ERR_OK(status)) {
2871 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2876 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2877 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2881 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2882 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2886 ads_msgfree(ads, res);
2893 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2896 * Join a machine to a realm
2897 * Creates the machine account and sets the machine password
2898 * @param ads connection to ads server
2899 * @param machine name of host to add
2900 * @param org_unit Organizational unit to place machine in
2901 * @return status of join
2903 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2904 uint32 account_type, const char *org_unit)
2907 LDAPMessage *res = NULL;
2910 /* machine name must be lowercase */
2911 machine = SMB_STRDUP(machine_name);
2912 strlower_m(machine);
2915 status = ads_find_machine_acct(ads, (void **)&res, machine);
2916 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2917 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2918 status = ads_leave_realm(ads, machine);
2919 if (!ADS_ERR_OK(status)) {
2920 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2921 machine, ads->config.realm));
2926 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2927 if (!ADS_ERR_OK(status)) {
2928 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2933 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2934 if (!ADS_ERR_OK(status)) {
2935 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2941 ads_msgfree(ads, res);
2948 * Delete a machine from the realm
2949 * @param ads connection to ads server
2950 * @param hostname Machine to remove
2951 * @return status of delete
2953 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2958 char *hostnameDN, *host;
2960 LDAPControl ldap_control;
2961 LDAPControl * pldap_control[2] = {NULL, NULL};
2963 pldap_control[0] = &ldap_control;
2964 memset(&ldap_control, 0, sizeof(LDAPControl));
2965 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
2967 /* hostname must be lowercase */
2968 host = SMB_STRDUP(hostname);
2971 status = ads_find_machine_acct(ads, &res, host);
2972 if (!ADS_ERR_OK(status)) {
2973 DEBUG(0, ("Host account for %s does not exist.\n", host));
2978 msg = ads_first_entry(ads, res);
2981 return ADS_ERROR_SYSTEM(ENOENT);
2984 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
2986 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
2988 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
2990 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
2993 if (rc != LDAP_SUCCESS) {
2994 const char *attrs[] = { "cn", NULL };
2995 LDAPMessage *msg_sub;
2997 /* we only search with scope ONE, we do not expect any further
2998 * objects to be created deeper */
3000 status = ads_do_search_retry(ads, hostnameDN,
3001 LDAP_SCOPE_ONELEVEL,
3002 "(objectclass=*)", attrs, &res);
3004 if (!ADS_ERR_OK(status)) {
3006 ads_memfree(ads, hostnameDN);
3010 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3011 msg_sub = ads_next_entry(ads, msg_sub)) {
3015 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3017 ads_memfree(ads, hostnameDN);
3018 return ADS_ERROR(LDAP_NO_MEMORY);
3021 status = ads_del_dn(ads, dn);
3022 if (!ADS_ERR_OK(status)) {
3023 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3025 ads_memfree(ads, dn);
3026 ads_memfree(ads, hostnameDN);
3030 ads_memfree(ads, dn);
3033 /* there should be no subordinate objects anymore */
3034 status = ads_do_search_retry(ads, hostnameDN,
3035 LDAP_SCOPE_ONELEVEL,
3036 "(objectclass=*)", attrs, &res);
3038 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3040 ads_memfree(ads, hostnameDN);
3044 /* delete hostnameDN now */
3045 status = ads_del_dn(ads, hostnameDN);
3046 if (!ADS_ERR_OK(status)) {
3048 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3049 ads_memfree(ads, hostnameDN);
3054 ads_memfree(ads, hostnameDN);
3056 status = ads_find_machine_acct(ads, &res, host);
3057 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3058 DEBUG(3, ("Failed to remove host account.\n"));