2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
60 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
61 "%u seconds\n", server, port, to));
65 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 /* End setup timeout. */
69 ldp = ldap_open(server, port);
72 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
73 server, port, strerror(errno)));
75 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
78 /* Teardown timeout. */
79 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
85 static int ldap_search_with_timeout(LDAP *ld,
86 LDAP_CONST char *base,
88 LDAP_CONST char *filter,
96 struct timeval timeout;
99 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
100 timeout.tv_sec = lp_ldap_timeout();
103 /* Setup alarm timeout.... Do we need both of these ? JRA. */
105 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
106 alarm(lp_ldap_timeout());
107 /* End setup timeout. */
109 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
110 attrsonly, sctrls, cctrls, &timeout,
113 /* Teardown timeout. */
114 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
118 return LDAP_TIMELIMIT_EXCEEDED;
123 /**********************************************
124 Do client and server sitename match ?
125 **********************************************/
127 bool ads_sitename_match(ADS_STRUCT *ads)
129 if (ads->config.server_site_name == NULL &&
130 ads->config.client_site_name == NULL ) {
131 DEBUG(10,("ads_sitename_match: both null\n"));
134 if (ads->config.server_site_name &&
135 ads->config.client_site_name &&
136 strequal(ads->config.server_site_name,
137 ads->config.client_site_name)) {
138 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
141 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
142 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
143 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
147 /**********************************************
148 Is this the closest DC ?
149 **********************************************/
151 bool ads_closest_dc(ADS_STRUCT *ads)
153 if (ads->config.flags & ADS_CLOSEST) {
154 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
158 /* not sure if this can ever happen */
159 if (ads_sitename_match(ads)) {
160 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
164 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
165 ads->config.ldap_server_name));
172 try a connection to a given ldap server, returning True and setting the servers IP
173 in the ads struct if successful
175 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
178 struct cldap_netlogon_reply cldap_reply;
180 if (!server || !*server) {
184 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
185 server, ads->server.realm));
187 /* this copes with inet_ntoa brokenness */
189 srv = SMB_STRDUP(server);
191 ZERO_STRUCT( cldap_reply );
193 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
194 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
199 /* Check the CLDAP reply flags */
201 if ( !(cldap_reply.flags & ADS_LDAP) ) {
202 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
208 /* Fill in the ads->config values */
210 SAFE_FREE(ads->config.realm);
211 SAFE_FREE(ads->config.bind_path);
212 SAFE_FREE(ads->config.ldap_server_name);
213 SAFE_FREE(ads->config.server_site_name);
214 SAFE_FREE(ads->config.client_site_name);
215 SAFE_FREE(ads->server.workgroup);
217 ads->config.flags = cldap_reply.flags;
218 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
219 strupper_m(cldap_reply.domain);
220 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
221 ads->config.bind_path = ads_build_dn(ads->config.realm);
222 if (*cldap_reply.server_site_name) {
223 ads->config.server_site_name =
224 SMB_STRDUP(cldap_reply.server_site_name);
226 if (*cldap_reply.client_site_name) {
227 ads->config.client_site_name =
228 SMB_STRDUP(cldap_reply.client_site_name);
230 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
232 ads->ldap.port = LDAP_PORT;
233 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
234 DEBUG(1,("ads_try_connect: unable to convert %s "
243 /* Store our site name. */
244 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
249 /**********************************************************************
250 Try to find an AD dc using our internal name resolution routines
251 Try the realm first and then then workgroup name if netbios is not
253 **********************************************************************/
255 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
259 struct ip_service *ip_list;
261 bool got_realm = False;
262 bool use_own_domain = False;
264 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
266 /* if the realm and workgroup are both empty, assume they are ours */
269 c_realm = ads->server.realm;
271 if ( !c_realm || !*c_realm ) {
272 /* special case where no realm and no workgroup means our own */
273 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
274 use_own_domain = True;
275 c_realm = lp_realm();
279 if (c_realm && *c_realm)
282 /* we need to try once with the realm name and fallback to the
283 netbios domain name if we fail (if netbios has not been disabled */
285 if ( !got_realm && !lp_disable_netbios() ) {
286 c_realm = ads->server.workgroup;
287 if (!c_realm || !*c_realm) {
288 if ( use_own_domain )
289 c_realm = lp_workgroup();
292 if ( !c_realm || !*c_realm ) {
293 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
294 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
300 sitename = sitename_fetch(realm);
304 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
305 (got_realm ? "realm" : "domain"), realm));
307 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
308 if (!NT_STATUS_IS_OK(status)) {
309 /* fall back to netbios if we can */
310 if ( got_realm && !lp_disable_netbios() ) {
319 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
320 for ( i=0; i<count; i++ ) {
321 char server[INET6_ADDRSTRLEN];
323 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
325 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
329 /* realm in this case is a workgroup name. We need
330 to ignore any IP addresses in the negative connection
331 cache that match ip addresses returned in the ad realm
332 case. It sucks that I have to reproduce the logic above... */
333 c_realm = ads->server.realm;
334 if ( !c_realm || !*c_realm ) {
335 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
336 c_realm = lp_realm();
339 if (c_realm && *c_realm &&
340 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
341 /* Ensure we add the workgroup name for this
342 IP address as negative too. */
343 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
348 if ( ads_try_connect(ads, server) ) {
354 /* keep track of failures */
355 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
360 /* In case we failed to contact one of our closest DC on our site we
361 * need to try to find another DC, retry with a site-less SRV DNS query
365 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
366 "trying to find another DC\n", sitename));
368 namecache_delete(realm, 0x1C);
372 return NT_STATUS_NO_LOGON_SERVERS;
377 * Connect to the LDAP server
378 * @param ads Pointer to an existing ADS_STRUCT
379 * @return status of connection
381 ADS_STATUS ads_connect(ADS_STRUCT *ads)
383 int version = LDAP_VERSION3;
386 char addr[INET6_ADDRSTRLEN];
388 ZERO_STRUCT(ads->ldap);
389 ads->ldap.last_attempt = time(NULL);
390 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
392 /* try with a user specified server */
394 if (ads->server.ldap_server &&
395 ads_try_connect(ads, ads->server.ldap_server)) {
399 ntstatus = ads_find_dc(ads);
400 if (NT_STATUS_IS_OK(ntstatus)) {
404 return ADS_ERROR_NT(ntstatus);
408 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
409 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
411 if (!ads->auth.user_name) {
412 /* Must use the userPrincipalName value here or sAMAccountName
413 and not servicePrincipalName; found by Guenther Deschner */
415 asprintf(&ads->auth.user_name, "%s$", global_myname() );
418 if (!ads->auth.realm) {
419 ads->auth.realm = SMB_STRDUP(ads->config.realm);
422 if (!ads->auth.kdc_server) {
423 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
424 ads->auth.kdc_server = SMB_STRDUP(addr);
428 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
429 to MIT kerberos to work (tridge) */
432 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
433 setenv(env, ads->auth.kdc_server, 1);
438 /* If the caller() requested no LDAP bind, then we are done */
440 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
444 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
445 if (!ads->ldap.mem_ctx) {
446 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
449 /* Otherwise setup the TCP LDAP session */
451 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
452 LDAP_PORT, lp_ldap_timeout());
453 if (ads->ldap.ld == NULL) {
454 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
457 /* cache the successful connection for workgroup and realm */
458 if (ads_closest_dc(ads)) {
459 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
460 saf_store( ads->server.workgroup, addr);
461 saf_store( ads->server.realm, addr);
464 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
466 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
467 if (!ADS_ERR_OK(status)) {
471 /* fill in the current time and offsets */
473 status = ads_current_time( ads );
474 if ( !ADS_ERR_OK(status) ) {
478 /* Now do the bind */
480 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
481 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, NULL, NULL));
484 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
485 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
488 return ads_sasl_bind(ads);
492 * Disconnect the LDAP server
493 * @param ads Pointer to an existing ADS_STRUCT
495 void ads_disconnect(ADS_STRUCT *ads)
498 ldap_unbind(ads->ldap.ld);
501 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
502 ads->ldap.wrap_ops->disconnect(ads);
504 if (ads->ldap.mem_ctx) {
505 talloc_free(ads->ldap.mem_ctx);
507 ZERO_STRUCT(ads->ldap);
511 Duplicate a struct berval into talloc'ed memory
513 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
515 struct berval *value;
517 if (!in_val) return NULL;
519 value = TALLOC_ZERO_P(ctx, struct berval);
522 if (in_val->bv_len == 0) return value;
524 value->bv_len = in_val->bv_len;
525 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
531 Make a values list out of an array of (struct berval *)
533 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
534 const struct berval **in_vals)
536 struct berval **values;
539 if (!in_vals) return NULL;
540 for (i=0; in_vals[i]; i++)
542 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
543 if (!values) return NULL;
545 for (i=0; in_vals[i]; i++) {
546 values[i] = dup_berval(ctx, in_vals[i]);
552 UTF8-encode a values list out of an array of (char *)
554 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
559 if (!in_vals) return NULL;
560 for (i=0; in_vals[i]; i++)
562 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
563 if (!values) return NULL;
565 for (i=0; in_vals[i]; i++) {
566 push_utf8_talloc(ctx, &values[i], in_vals[i]);
572 Pull a (char *) array out of a UTF8-encoded values list
574 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
579 if (!in_vals) return NULL;
580 for (i=0; in_vals[i]; i++)
582 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
583 if (!values) return NULL;
585 for (i=0; in_vals[i]; i++) {
586 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
592 * Do a search with paged results. cookie must be null on the first
593 * call, and then returned on each subsequent call. It will be null
594 * again when the entire search is complete
595 * @param ads connection to ads server
596 * @param bind_path Base dn for the search
597 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
598 * @param expr Search expression - specified in local charset
599 * @param attrs Attributes to retrieve - specified in utf8 or ascii
600 * @param res ** which will contain results - free res* with ads_msgfree()
601 * @param count Number of entries retrieved on this page
602 * @param cookie The paged results cookie to be returned on subsequent calls
603 * @return status of search
605 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
606 const char *bind_path,
607 int scope, const char *expr,
608 const char **attrs, void *args,
610 int *count, struct berval **cookie)
613 char *utf8_expr, *utf8_path, **search_attrs;
614 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
615 BerElement *cookie_be = NULL;
616 struct berval *cookie_bv= NULL;
617 BerElement *ext_be = NULL;
618 struct berval *ext_bv= NULL;
621 ads_control *external_control = (ads_control *) args;
625 if (!(ctx = talloc_init("ads_do_paged_search_args")))
626 return ADS_ERROR(LDAP_NO_MEMORY);
628 /* 0 means the conversion worked but the result was empty
629 so we only fail if it's -1. In any case, it always
630 at least nulls out the dest */
631 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
632 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
637 if (!attrs || !(*attrs))
640 /* This would be the utf8-encoded version...*/
641 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
642 if (!(str_list_copy(&search_attrs, attrs))) {
648 /* Paged results only available on ldap v3 or later */
649 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
650 if (version < LDAP_VERSION3) {
651 rc = LDAP_NOT_SUPPORTED;
655 cookie_be = ber_alloc_t(LBER_USE_DER);
657 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
658 ber_bvfree(*cookie); /* don't need it from last time */
661 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
663 ber_flatten(cookie_be, &cookie_bv);
664 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
665 PagedResults.ldctl_iscritical = (char) 1;
666 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
667 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
669 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
670 NoReferrals.ldctl_iscritical = (char) 0;
671 NoReferrals.ldctl_value.bv_len = 0;
672 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
674 if (external_control &&
675 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
676 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
678 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
679 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
681 /* win2k does not accept a ldctl_value beeing passed in */
683 if (external_control->val != 0) {
685 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
690 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
694 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
699 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
700 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
703 ExternalCtrl.ldctl_value.bv_len = 0;
704 ExternalCtrl.ldctl_value.bv_val = NULL;
707 controls[0] = &NoReferrals;
708 controls[1] = &PagedResults;
709 controls[2] = &ExternalCtrl;
713 controls[0] = &NoReferrals;
714 controls[1] = &PagedResults;
718 /* we need to disable referrals as the openldap libs don't
719 handle them and paged results at the same time. Using them
720 together results in the result record containing the server
721 page control being removed from the result list (tridge/jmcd)
723 leaving this in despite the control that says don't generate
724 referrals, in case the server doesn't support it (jmcd)
726 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
728 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
729 search_attrs, 0, controls,
731 (LDAPMessage **)res);
733 ber_free(cookie_be, 1);
734 ber_bvfree(cookie_bv);
737 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
738 ldap_err2string(rc)));
742 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
743 NULL, &rcontrols, 0);
749 for (i=0; rcontrols[i]; i++) {
750 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
751 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
752 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
754 /* the berval is the cookie, but must be freed when
756 if (cookie_bv->bv_len) /* still more to do */
757 *cookie=ber_bvdup(cookie_bv);
760 ber_bvfree(cookie_bv);
761 ber_free(cookie_be, 1);
765 ldap_controls_free(rcontrols);
778 /* if/when we decide to utf8-encode attrs, take out this next line */
779 str_list_free(&search_attrs);
781 return ADS_ERROR(rc);
784 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
785 int scope, const char *expr,
786 const char **attrs, LDAPMessage **res,
787 int *count, struct berval **cookie)
789 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
794 * Get all results for a search. This uses ads_do_paged_search() to return
795 * all entries in a large search.
796 * @param ads connection to ads server
797 * @param bind_path Base dn for the search
798 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
799 * @param expr Search expression
800 * @param attrs Attributes to retrieve
801 * @param res ** which will contain results - free res* with ads_msgfree()
802 * @return status of search
804 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
805 int scope, const char *expr,
806 const char **attrs, void *args,
809 struct berval *cookie = NULL;
814 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
817 if (!ADS_ERR_OK(status))
820 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
822 LDAPMessage *res2 = NULL;
824 LDAPMessage *msg, *next;
826 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
827 attrs, args, &res2, &count, &cookie);
829 if (!ADS_ERR_OK(status2)) break;
831 /* this relies on the way that ldap_add_result_entry() works internally. I hope
832 that this works on all ldap libs, but I have only tested with openldap */
833 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
834 next = ads_next_entry(ads, msg);
835 ldap_add_result_entry((LDAPMessage **)res, msg);
837 /* note that we do not free res2, as the memory is now
838 part of the main returned list */
841 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
842 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
848 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
849 int scope, const char *expr,
850 const char **attrs, LDAPMessage **res)
852 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
855 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
856 int scope, const char *expr,
857 const char **attrs, uint32 sd_flags,
862 args.control = ADS_SD_FLAGS_OID;
864 args.critical = True;
866 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
871 * Run a function on all results for a search. Uses ads_do_paged_search() and
872 * runs the function as each page is returned, using ads_process_results()
873 * @param ads connection to ads server
874 * @param bind_path Base dn for the search
875 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
876 * @param expr Search expression - specified in local charset
877 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
878 * @param fn Function which takes attr name, values list, and data_area
879 * @param data_area Pointer which is passed to function on each call
880 * @return status of search
882 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
883 int scope, const char *expr, const char **attrs,
884 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
887 struct berval *cookie = NULL;
892 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
895 if (!ADS_ERR_OK(status)) return status;
897 ads_process_results(ads, res, fn, data_area);
898 ads_msgfree(ads, res);
901 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
902 &res, &count, &cookie);
904 if (!ADS_ERR_OK(status)) break;
906 ads_process_results(ads, res, fn, data_area);
907 ads_msgfree(ads, res);
914 * Do a search with a timeout.
915 * @param ads connection to ads server
916 * @param bind_path Base dn for the search
917 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
918 * @param expr Search expression
919 * @param attrs Attributes to retrieve
920 * @param res ** which will contain results - free res* with ads_msgfree()
921 * @return status of search
923 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
925 const char **attrs, LDAPMessage **res)
928 char *utf8_expr, *utf8_path, **search_attrs = NULL;
932 if (!(ctx = talloc_init("ads_do_search"))) {
933 DEBUG(1,("ads_do_search: talloc_init() failed!"));
934 return ADS_ERROR(LDAP_NO_MEMORY);
937 /* 0 means the conversion worked but the result was empty
938 so we only fail if it's negative. In any case, it always
939 at least nulls out the dest */
940 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
941 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
942 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
947 if (!attrs || !(*attrs))
950 /* This would be the utf8-encoded version...*/
951 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
952 if (!(str_list_copy(&search_attrs, attrs)))
954 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
960 /* see the note in ads_do_paged_search - we *must* disable referrals */
961 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
963 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
964 search_attrs, 0, NULL, NULL,
966 (LDAPMessage **)res);
968 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
969 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
975 /* if/when we decide to utf8-encode attrs, take out this next line */
976 str_list_free(&search_attrs);
977 return ADS_ERROR(rc);
980 * Do a general ADS search
981 * @param ads connection to ads server
982 * @param res ** which will contain results - free res* with ads_msgfree()
983 * @param expr Search expression
984 * @param attrs Attributes to retrieve
985 * @return status of search
987 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
988 const char *expr, const char **attrs)
990 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
995 * Do a search on a specific DistinguishedName
996 * @param ads connection to ads server
997 * @param res ** which will contain results - free res* with ads_msgfree()
998 * @param dn DistinguishName to search
999 * @param attrs Attributes to retrieve
1000 * @return status of search
1002 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1003 const char *dn, const char **attrs)
1005 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1010 * Free up memory from a ads_search
1011 * @param ads connection to ads server
1012 * @param msg Search results to free
1014 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1021 * Free up memory from various ads requests
1022 * @param ads connection to ads server
1023 * @param mem Area to free
1025 void ads_memfree(ADS_STRUCT *ads, void *mem)
1031 * Get a dn from search results
1032 * @param ads connection to ads server
1033 * @param msg Search result
1036 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1038 char *utf8_dn, *unix_dn;
1040 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1043 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1047 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1048 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1052 ldap_memfree(utf8_dn);
1057 * Get the parent from a dn
1058 * @param dn the dn to return the parent from
1059 * @return parent dn string
1061 char *ads_parent_dn(const char *dn)
1069 p = strchr(dn, ',');
1079 * Find a machine account given a hostname
1080 * @param ads connection to ads server
1081 * @param res ** which will contain results - free res* with ads_msgfree()
1082 * @param host Hostname to search for
1083 * @return status of search
1085 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1086 const char *machine)
1090 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1094 /* the easiest way to find a machine account anywhere in the tree
1095 is to look for hostname$ */
1096 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1097 DEBUG(1, ("asprintf failed!\n"));
1098 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1101 status = ads_search(ads, res, expr, attrs);
1107 * Initialize a list of mods to be used in a modify request
1108 * @param ctx An initialized TALLOC_CTX
1109 * @return allocated ADS_MODLIST
1111 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1113 #define ADS_MODLIST_ALLOC_SIZE 10
1116 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1117 /* -1 is safety to make sure we don't go over the end.
1118 need to reset it to NULL before doing ldap modify */
1119 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1121 return (ADS_MODLIST)mods;
1126 add an attribute to the list, with values list already constructed
1128 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1129 int mod_op, const char *name,
1130 const void *_invals)
1132 const void **invals = (const void **)_invals;
1134 LDAPMod **modlist = (LDAPMod **) *mods;
1135 struct berval **ber_values = NULL;
1136 char **char_values = NULL;
1139 mod_op = LDAP_MOD_DELETE;
1141 if (mod_op & LDAP_MOD_BVALUES)
1142 ber_values = ads_dup_values(ctx,
1143 (const struct berval **)invals);
1145 char_values = ads_push_strvals(ctx,
1146 (const char **) invals);
1149 /* find the first empty slot */
1150 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1152 if (modlist[curmod] == (LDAPMod *) -1) {
1153 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1154 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1155 return ADS_ERROR(LDAP_NO_MEMORY);
1156 memset(&modlist[curmod], 0,
1157 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1158 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1159 *mods = (ADS_MODLIST)modlist;
1162 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1163 return ADS_ERROR(LDAP_NO_MEMORY);
1164 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1165 if (mod_op & LDAP_MOD_BVALUES) {
1166 modlist[curmod]->mod_bvalues = ber_values;
1167 } else if (mod_op & LDAP_MOD_DELETE) {
1168 modlist[curmod]->mod_values = NULL;
1170 modlist[curmod]->mod_values = char_values;
1173 modlist[curmod]->mod_op = mod_op;
1174 return ADS_ERROR(LDAP_SUCCESS);
1178 * Add a single string value to a mod list
1179 * @param ctx An initialized TALLOC_CTX
1180 * @param mods An initialized ADS_MODLIST
1181 * @param name The attribute name to add
1182 * @param val The value to add - NULL means DELETE
1183 * @return ADS STATUS indicating success of add
1185 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1186 const char *name, const char *val)
1188 const char *values[2];
1194 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1195 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1199 * Add an array of string values to a mod list
1200 * @param ctx An initialized TALLOC_CTX
1201 * @param mods An initialized ADS_MODLIST
1202 * @param name The attribute name to add
1203 * @param vals The array of string values to add - NULL means DELETE
1204 * @return ADS STATUS indicating success of add
1206 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1207 const char *name, const char **vals)
1210 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1211 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1212 name, (const void **) vals);
1217 * Add a single ber-encoded value to a mod list
1218 * @param ctx An initialized TALLOC_CTX
1219 * @param mods An initialized ADS_MODLIST
1220 * @param name The attribute name to add
1221 * @param val The value to add - NULL means DELETE
1222 * @return ADS STATUS indicating success of add
1224 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1225 const char *name, const struct berval *val)
1227 const struct berval *values[2];
1232 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1233 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1234 name, (const void **) values);
1239 * Perform an ldap modify
1240 * @param ads connection to ads server
1241 * @param mod_dn DistinguishedName to modify
1242 * @param mods list of modifications to perform
1243 * @return status of modify
1245 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1248 char *utf8_dn = NULL;
1250 this control is needed to modify that contains a currently
1251 non-existent attribute (but allowable for the object) to run
1253 LDAPControl PermitModify = {
1254 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1257 LDAPControl *controls[2];
1259 controls[0] = &PermitModify;
1262 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1263 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1266 /* find the end of the list, marked by NULL or -1 */
1267 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1268 /* make sure the end of the list is NULL */
1270 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1271 (LDAPMod **) mods, controls, NULL);
1273 return ADS_ERROR(ret);
1277 * Perform an ldap add
1278 * @param ads connection to ads server
1279 * @param new_dn DistinguishedName to add
1280 * @param mods list of attributes and values for DN
1281 * @return status of add
1283 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1286 char *utf8_dn = NULL;
1288 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1289 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1290 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1293 /* find the end of the list, marked by NULL or -1 */
1294 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1295 /* make sure the end of the list is NULL */
1298 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1300 return ADS_ERROR(ret);
1304 * Delete a DistinguishedName
1305 * @param ads connection to ads server
1306 * @param new_dn DistinguishedName to delete
1307 * @return status of delete
1309 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1312 char *utf8_dn = NULL;
1313 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1314 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1315 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1318 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1320 return ADS_ERROR(ret);
1324 * Build an org unit string
1325 * if org unit is Computers or blank then assume a container, otherwise
1326 * assume a / separated list of organisational units.
1327 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1328 * @param ads connection to ads server
1329 * @param org_unit Organizational unit
1330 * @return org unit string - caller must free
1332 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1336 if (!org_unit || !*org_unit) {
1338 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1340 /* samba4 might not yet respond to a wellknownobject-query */
1341 return ret ? ret : SMB_STRDUP("cn=Computers");
1344 if (strequal(org_unit, "Computers")) {
1345 return SMB_STRDUP("cn=Computers");
1348 /* jmcd: removed "\\" from the separation chars, because it is
1349 needed as an escape for chars like '#' which are valid in an
1351 return ads_build_path(org_unit, "/", "ou=", 1);
1355 * Get a org unit string for a well-known GUID
1356 * @param ads connection to ads server
1357 * @param wknguid Well known GUID
1358 * @return org unit string - caller must free
1360 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1363 LDAPMessage *res = NULL;
1364 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1365 **bind_dn_exp = NULL;
1366 const char *attrs[] = {"distinguishedName", NULL};
1367 int new_ln, wkn_ln, bind_ln, i;
1369 if (wknguid == NULL) {
1373 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1374 DEBUG(1, ("asprintf failed!\n"));
1378 status = ads_search_dn(ads, &res, base, attrs);
1379 if (!ADS_ERR_OK(status)) {
1380 DEBUG(1,("Failed while searching for: %s\n", base));
1384 if (ads_count_replies(ads, res) != 1) {
1388 /* substitute the bind-path from the well-known-guid-search result */
1389 wkn_dn = ads_get_dn(ads, res);
1394 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1399 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1404 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1406 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1409 new_ln = wkn_ln - bind_ln;
1411 ret = SMB_STRDUP(wkn_dn_exp[0]);
1416 for (i=1; i < new_ln; i++) {
1419 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1425 ret = SMB_STRDUP(s);
1434 ads_msgfree(ads, res);
1435 ads_memfree(ads, wkn_dn);
1437 ldap_value_free(wkn_dn_exp);
1440 ldap_value_free(bind_dn_exp);
1447 * Adds (appends) an item to an attribute array, rather then
1448 * replacing the whole list
1449 * @param ctx An initialized TALLOC_CTX
1450 * @param mods An initialized ADS_MODLIST
1451 * @param name name of the ldap attribute to append to
1452 * @param vals an array of values to add
1453 * @return status of addition
1456 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1457 const char *name, const char **vals)
1459 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1460 (const void *) vals);
1464 * Determines the computer account's current KVNO via an LDAP lookup
1465 * @param ads An initialized ADS_STRUCT
1466 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1467 * @return the kvno for the computer account, or -1 in case of a failure.
1470 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1472 LDAPMessage *res = NULL;
1473 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1475 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1476 char *dn_string = NULL;
1477 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1479 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1480 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1483 ret = ads_search(ads, &res, filter, attrs);
1485 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1486 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1487 ads_msgfree(ads, res);
1491 dn_string = ads_get_dn(ads, res);
1493 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1494 ads_msgfree(ads, res);
1497 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1498 ads_memfree(ads, dn_string);
1500 /* ---------------------------------------------------------
1501 * 0 is returned as a default KVNO from this point on...
1502 * This is done because Windows 2000 does not support key
1503 * version numbers. Chances are that a failure in the next
1504 * step is simply due to Windows 2000 being used for a
1505 * domain controller. */
1508 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1509 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1510 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1511 ads_msgfree(ads, res);
1516 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1517 ads_msgfree(ads, res);
1522 * This clears out all registered spn's for a given hostname
1523 * @param ads An initilaized ADS_STRUCT
1524 * @param machine_name the NetBIOS name of the computer.
1525 * @return 0 upon success, non-zero otherwise.
1528 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1531 LDAPMessage *res = NULL;
1533 const char *servicePrincipalName[1] = {NULL};
1534 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1535 char *dn_string = NULL;
1537 ret = ads_find_machine_acct(ads, &res, machine_name);
1538 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1539 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1540 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1541 ads_msgfree(ads, res);
1542 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1545 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1546 ctx = talloc_init("ads_clear_service_principal_names");
1548 ads_msgfree(ads, res);
1549 return ADS_ERROR(LDAP_NO_MEMORY);
1552 if (!(mods = ads_init_mods(ctx))) {
1553 talloc_destroy(ctx);
1554 ads_msgfree(ads, res);
1555 return ADS_ERROR(LDAP_NO_MEMORY);
1557 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1558 if (!ADS_ERR_OK(ret)) {
1559 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1560 ads_msgfree(ads, res);
1561 talloc_destroy(ctx);
1564 dn_string = ads_get_dn(ads, res);
1566 talloc_destroy(ctx);
1567 ads_msgfree(ads, res);
1568 return ADS_ERROR(LDAP_NO_MEMORY);
1570 ret = ads_gen_mod(ads, dn_string, mods);
1571 ads_memfree(ads,dn_string);
1572 if (!ADS_ERR_OK(ret)) {
1573 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1575 ads_msgfree(ads, res);
1576 talloc_destroy(ctx);
1580 ads_msgfree(ads, res);
1581 talloc_destroy(ctx);
1586 * This adds a service principal name to an existing computer account
1587 * (found by hostname) in AD.
1588 * @param ads An initialized ADS_STRUCT
1589 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1590 * @param my_fqdn The fully qualified DNS name of the machine
1591 * @param spn A string of the service principal to add, i.e. 'host'
1592 * @return 0 upon sucess, or non-zero if a failure occurs
1595 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1596 const char *my_fqdn, const char *spn)
1600 LDAPMessage *res = NULL;
1603 char *dn_string = NULL;
1604 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1606 ret = ads_find_machine_acct(ads, &res, machine_name);
1607 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1608 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1610 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1611 spn, machine_name, ads->config.realm));
1612 ads_msgfree(ads, res);
1613 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1616 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1617 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1618 ads_msgfree(ads, res);
1619 return ADS_ERROR(LDAP_NO_MEMORY);
1622 /* add short name spn */
1624 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1625 talloc_destroy(ctx);
1626 ads_msgfree(ads, res);
1627 return ADS_ERROR(LDAP_NO_MEMORY);
1630 strlower_m(&psp1[strlen(spn)]);
1631 servicePrincipalName[0] = psp1;
1633 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1634 psp1, machine_name));
1637 /* add fully qualified spn */
1639 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1640 ret = ADS_ERROR(LDAP_NO_MEMORY);
1644 strlower_m(&psp2[strlen(spn)]);
1645 servicePrincipalName[1] = psp2;
1647 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1648 psp2, machine_name));
1650 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1651 ret = ADS_ERROR(LDAP_NO_MEMORY);
1655 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1656 if (!ADS_ERR_OK(ret)) {
1657 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1661 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1662 ret = ADS_ERROR(LDAP_NO_MEMORY);
1666 ret = ads_gen_mod(ads, dn_string, mods);
1667 ads_memfree(ads,dn_string);
1668 if (!ADS_ERR_OK(ret)) {
1669 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1675 ads_msgfree(ads, res);
1680 * adds a machine account to the ADS server
1681 * @param ads An intialized ADS_STRUCT
1682 * @param machine_name - the NetBIOS machine name of this account.
1683 * @param account_type A number indicating the type of account to create
1684 * @param org_unit The LDAP path in which to place this account
1685 * @return 0 upon success, or non-zero otherwise
1688 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1689 const char *org_unit)
1692 char *samAccountName, *controlstr;
1695 char *machine_escaped = NULL;
1697 const char *objectClass[] = {"top", "person", "organizationalPerson",
1698 "user", "computer", NULL};
1699 LDAPMessage *res = NULL;
1700 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1701 UF_DONT_EXPIRE_PASSWD |\
1702 UF_ACCOUNTDISABLE );
1704 if (!(ctx = talloc_init("ads_add_machine_acct")))
1705 return ADS_ERROR(LDAP_NO_MEMORY);
1707 ret = ADS_ERROR(LDAP_NO_MEMORY);
1709 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1710 if (!machine_escaped) {
1714 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1715 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1717 if ( !new_dn || !samAccountName ) {
1721 #ifndef ENCTYPE_ARCFOUR_HMAC
1722 acct_control |= UF_USE_DES_KEY_ONLY;
1725 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1729 if (!(mods = ads_init_mods(ctx))) {
1733 ads_mod_str(ctx, &mods, "cn", machine_name);
1734 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1735 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1736 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1738 ret = ads_gen_add(ads, new_dn, mods);
1741 SAFE_FREE(machine_escaped);
1742 ads_msgfree(ads, res);
1743 talloc_destroy(ctx);
1749 * move a machine account to another OU on the ADS server
1750 * @param ads - An intialized ADS_STRUCT
1751 * @param machine_name - the NetBIOS machine name of this account.
1752 * @param org_unit - The LDAP path in which to place this account
1753 * @param moved - whether we moved the machine account (optional)
1754 * @return 0 upon success, or non-zero otherwise
1757 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1758 const char *org_unit, bool *moved)
1762 LDAPMessage *res = NULL;
1763 char *filter = NULL;
1764 char *computer_dn = NULL;
1766 char *computer_rdn = NULL;
1767 bool need_move = False;
1769 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1770 rc = ADS_ERROR(LDAP_NO_MEMORY);
1774 /* Find pre-existing machine */
1775 rc = ads_search(ads, &res, filter, NULL);
1776 if (!ADS_ERR_OK(rc)) {
1780 computer_dn = ads_get_dn(ads, res);
1782 rc = ADS_ERROR(LDAP_NO_MEMORY);
1786 parent_dn = ads_parent_dn(computer_dn);
1787 if (strequal(parent_dn, org_unit)) {
1793 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1794 rc = ADS_ERROR(LDAP_NO_MEMORY);
1798 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1799 org_unit, 1, NULL, NULL);
1800 rc = ADS_ERROR(ldap_status);
1803 ads_msgfree(ads, res);
1805 SAFE_FREE(computer_dn);
1806 SAFE_FREE(computer_rdn);
1808 if (!ADS_ERR_OK(rc)) {
1820 dump a binary result from ldap
1822 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1825 for (i=0; values[i]; i++) {
1826 printf("%s: ", field);
1827 for (j=0; j<values[i]->bv_len; j++) {
1828 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1834 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1837 for (i=0; values[i]; i++) {
1842 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1843 smb_uuid_unpack(guid, &tmp);
1844 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1849 dump a sid result from ldap
1851 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1854 for (i=0; values[i]; i++) {
1857 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1858 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1863 dump ntSecurityDescriptor
1865 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1867 TALLOC_CTX *frame = talloc_stackframe();
1868 struct security_descriptor *psd;
1871 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1872 values[0]->bv_len, &psd);
1873 if (!NT_STATUS_IS_OK(status)) {
1874 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1875 nt_errstr(status)));
1881 ads_disp_sd(ads, talloc_tos(), psd);
1888 dump a string result from ldap
1890 static void dump_string(const char *field, char **values)
1893 for (i=0; values[i]; i++) {
1894 printf("%s: %s\n", field, values[i]);
1899 dump a field from LDAP on stdout
1903 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1908 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1910 {"objectGUID", False, dump_guid},
1911 {"netbootGUID", False, dump_guid},
1912 {"nTSecurityDescriptor", False, dump_sd},
1913 {"dnsRecord", False, dump_binary},
1914 {"objectSid", False, dump_sid},
1915 {"tokenGroups", False, dump_sid},
1916 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1917 {"tokengroupsGlobalandUniversal", False, dump_sid},
1918 {"mS-DS-CreatorSID", False, dump_sid},
1919 {"msExchMailboxGuid", False, dump_guid},
1924 if (!field) { /* must be end of an entry */
1929 for (i=0; handlers[i].name; i++) {
1930 if (StrCaseCmp(handlers[i].name, field) == 0) {
1931 if (!values) /* first time, indicate string or not */
1932 return handlers[i].string;
1933 handlers[i].handler(ads, field, (struct berval **) values);
1937 if (!handlers[i].name) {
1938 if (!values) /* first time, indicate string conversion */
1940 dump_string(field, (char **)values);
1946 * Dump a result from LDAP on stdout
1947 * used for debugging
1948 * @param ads connection to ads server
1949 * @param res Results to dump
1952 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1954 ads_process_results(ads, res, ads_dump_field, NULL);
1958 * Walk through results, calling a function for each entry found.
1959 * The function receives a field name, a berval * array of values,
1960 * and a data area passed through from the start. The function is
1961 * called once with null for field and values at the end of each
1963 * @param ads connection to ads server
1964 * @param res Results to process
1965 * @param fn Function for processing each result
1966 * @param data_area user-defined area to pass to function
1968 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1969 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1975 if (!(ctx = talloc_init("ads_process_results")))
1978 for (msg = ads_first_entry(ads, res); msg;
1979 msg = ads_next_entry(ads, msg)) {
1983 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1984 (LDAPMessage *)msg,&b);
1986 utf8_field=ldap_next_attribute(ads->ldap.ld,
1987 (LDAPMessage *)msg,b)) {
1988 struct berval **ber_vals;
1989 char **str_vals, **utf8_vals;
1993 pull_utf8_talloc(ctx, &field, utf8_field);
1994 string = fn(ads, field, NULL, data_area);
1997 utf8_vals = ldap_get_values(ads->ldap.ld,
1998 (LDAPMessage *)msg, field);
1999 str_vals = ads_pull_strvals(ctx,
2000 (const char **) utf8_vals);
2001 fn(ads, field, (void **) str_vals, data_area);
2002 ldap_value_free(utf8_vals);
2004 ber_vals = ldap_get_values_len(ads->ldap.ld,
2005 (LDAPMessage *)msg, field);
2006 fn(ads, field, (void **) ber_vals, data_area);
2008 ldap_value_free_len(ber_vals);
2010 ldap_memfree(utf8_field);
2013 talloc_free_children(ctx);
2014 fn(ads, NULL, NULL, data_area); /* completed an entry */
2017 talloc_destroy(ctx);
2021 * count how many replies are in a LDAPMessage
2022 * @param ads connection to ads server
2023 * @param res Results to count
2024 * @return number of replies
2026 int ads_count_replies(ADS_STRUCT *ads, void *res)
2028 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2032 * pull the first entry from a ADS result
2033 * @param ads connection to ads server
2034 * @param res Results of search
2035 * @return first entry from result
2037 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2039 return ldap_first_entry(ads->ldap.ld, res);
2043 * pull the next entry from a ADS result
2044 * @param ads connection to ads server
2045 * @param res Results of search
2046 * @return next entry from result
2048 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2050 return ldap_next_entry(ads->ldap.ld, res);
2054 * pull a single string from a ADS result
2055 * @param ads connection to ads server
2056 * @param mem_ctx TALLOC_CTX to use for allocating result string
2057 * @param msg Results of search
2058 * @param field Attribute to retrieve
2059 * @return Result string in talloc context
2061 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2069 values = ldap_get_values(ads->ldap.ld, msg, field);
2074 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2076 if (rc != (size_t)-1)
2080 ldap_value_free(values);
2085 * pull an array of strings from a ADS result
2086 * @param ads connection to ads server
2087 * @param mem_ctx TALLOC_CTX to use for allocating result string
2088 * @param msg Results of search
2089 * @param field Attribute to retrieve
2090 * @return Result strings in talloc context
2092 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2093 LDAPMessage *msg, const char *field,
2100 values = ldap_get_values(ads->ldap.ld, msg, field);
2104 *num_values = ldap_count_values(values);
2106 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2108 ldap_value_free(values);
2112 for (i=0;i<*num_values;i++) {
2113 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2114 ldap_value_free(values);
2120 ldap_value_free(values);
2125 * pull an array of strings from a ADS result
2126 * (handle large multivalue attributes with range retrieval)
2127 * @param ads connection to ads server
2128 * @param mem_ctx TALLOC_CTX to use for allocating result string
2129 * @param msg Results of search
2130 * @param field Attribute to retrieve
2131 * @param current_strings strings returned by a previous call to this function
2132 * @param next_attribute The next query should ask for this attribute
2133 * @param num_values How many values did we get this time?
2134 * @param more_values Are there more values to get?
2135 * @return Result strings in talloc context
2137 char **ads_pull_strings_range(ADS_STRUCT *ads,
2138 TALLOC_CTX *mem_ctx,
2139 LDAPMessage *msg, const char *field,
2140 char **current_strings,
2141 const char **next_attribute,
2142 size_t *num_strings,
2146 char *expected_range_attrib, *range_attr;
2147 BerElement *ptr = NULL;
2150 size_t num_new_strings;
2151 unsigned long int range_start;
2152 unsigned long int range_end;
2154 /* we might have been given the whole lot anyway */
2155 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2156 *more_strings = False;
2160 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2162 /* look for Range result */
2163 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2165 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2166 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2167 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2175 /* nothing here - this field is just empty */
2176 *more_strings = False;
2180 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2181 &range_start, &range_end) == 2) {
2182 *more_strings = True;
2184 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2185 &range_start) == 1) {
2186 *more_strings = False;
2188 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2190 ldap_memfree(range_attr);
2191 *more_strings = False;
2196 if ((*num_strings) != range_start) {
2197 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2198 " - aborting range retreival\n",
2199 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2200 ldap_memfree(range_attr);
2201 *more_strings = False;
2205 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2207 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2208 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2209 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2210 range_attr, (unsigned long int)range_end - range_start + 1,
2211 (unsigned long int)num_new_strings));
2212 ldap_memfree(range_attr);
2213 *more_strings = False;
2217 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2218 *num_strings + num_new_strings);
2220 if (strings == NULL) {
2221 ldap_memfree(range_attr);
2222 *more_strings = False;
2226 if (new_strings && num_new_strings) {
2227 memcpy(&strings[*num_strings], new_strings,
2228 sizeof(*new_strings) * num_new_strings);
2231 (*num_strings) += num_new_strings;
2233 if (*more_strings) {
2234 *next_attribute = talloc_asprintf(mem_ctx,
2239 if (!*next_attribute) {
2240 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2241 ldap_memfree(range_attr);
2242 *more_strings = False;
2247 ldap_memfree(range_attr);
2253 * pull a single uint32 from a ADS result
2254 * @param ads connection to ads server
2255 * @param msg Results of search
2256 * @param field Attribute to retrieve
2257 * @param v Pointer to int to store result
2258 * @return boolean inidicating success
2260 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2265 values = ldap_get_values(ads->ldap.ld, msg, field);
2269 ldap_value_free(values);
2273 *v = atoi(values[0]);
2274 ldap_value_free(values);
2279 * pull a single objectGUID from an ADS result
2280 * @param ads connection to ADS server
2281 * @param msg results of search
2282 * @param guid 37-byte area to receive text guid
2283 * @return boolean indicating success
2285 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2288 UUID_FLAT flat_guid;
2290 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2295 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2296 smb_uuid_unpack(flat_guid, guid);
2297 ldap_value_free(values);
2300 ldap_value_free(values);
2307 * pull a single DOM_SID from a ADS result
2308 * @param ads connection to ads server
2309 * @param msg Results of search
2310 * @param field Attribute to retrieve
2311 * @param sid Pointer to sid to store result
2312 * @return boolean inidicating success
2314 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2317 struct berval **values;
2320 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2326 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2328 ldap_value_free_len(values);
2333 * pull an array of DOM_SIDs from a ADS result
2334 * @param ads connection to ads server
2335 * @param mem_ctx TALLOC_CTX for allocating sid array
2336 * @param msg Results of search
2337 * @param field Attribute to retrieve
2338 * @param sids pointer to sid array to allocate
2339 * @return the count of SIDs pulled
2341 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2342 LDAPMessage *msg, const char *field, DOM_SID **sids)
2344 struct berval **values;
2348 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2353 for (i=0; values[i]; i++)
2357 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2359 ldap_value_free_len(values);
2367 for (i=0; values[i]; i++) {
2368 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2370 DEBUG(10, ("pulling SID: %s\n",
2371 sid_string_dbg(&(*sids)[count])));
2376 ldap_value_free_len(values);
2381 * pull a SEC_DESC from a ADS result
2382 * @param ads connection to ads server
2383 * @param mem_ctx TALLOC_CTX for allocating sid array
2384 * @param msg Results of search
2385 * @param field Attribute to retrieve
2386 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2387 * @return boolean inidicating success
2389 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2390 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2392 struct berval **values;
2395 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2397 if (!values) return false;
2401 status = unmarshall_sec_desc(mem_ctx,
2402 (uint8 *)values[0]->bv_val,
2403 values[0]->bv_len, sd);
2404 if (!NT_STATUS_IS_OK(status)) {
2405 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2406 nt_errstr(status)));
2411 ldap_value_free_len(values);
2416 * in order to support usernames longer than 21 characters we need to
2417 * use both the sAMAccountName and the userPrincipalName attributes
2418 * It seems that not all users have the userPrincipalName attribute set
2420 * @param ads connection to ads server
2421 * @param mem_ctx TALLOC_CTX for allocating sid array
2422 * @param msg Results of search
2423 * @return the username
2425 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2431 /* lookup_name() only works on the sAMAccountName to
2432 returning the username portion of userPrincipalName
2433 breaks winbindd_getpwnam() */
2435 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2436 if (ret && (p = strchr_m(ret, '@'))) {
2441 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2446 * find the update serial number - this is the core of the ldap cache
2447 * @param ads connection to ads server
2448 * @param ads connection to ADS server
2449 * @param usn Pointer to retrieved update serial number
2450 * @return status of search
2452 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2454 const char *attrs[] = {"highestCommittedUSN", NULL};
2458 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2459 if (!ADS_ERR_OK(status))
2462 if (ads_count_replies(ads, res) != 1) {
2463 ads_msgfree(ads, res);
2464 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2467 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2468 ads_msgfree(ads, res);
2469 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2472 ads_msgfree(ads, res);
2476 /* parse a ADS timestring - typical string is
2477 '20020917091222.0Z0' which means 09:12.22 17th September
2479 static time_t ads_parse_time(const char *str)
2485 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2486 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2487 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2496 /********************************************************************
2497 ********************************************************************/
2499 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2501 const char *attrs[] = {"currentTime", NULL};
2506 ADS_STRUCT *ads_s = ads;
2508 if (!(ctx = talloc_init("ads_current_time"))) {
2509 return ADS_ERROR(LDAP_NO_MEMORY);
2512 /* establish a new ldap tcp session if necessary */
2514 if ( !ads->ldap.ld ) {
2515 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2516 ads->server.ldap_server )) == NULL )
2520 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2521 status = ads_connect( ads_s );
2522 if ( !ADS_ERR_OK(status))
2526 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2527 if (!ADS_ERR_OK(status)) {
2531 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2533 ads_msgfree(ads_s, res);
2534 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2538 /* but save the time and offset in the original ADS_STRUCT */
2540 ads->config.current_time = ads_parse_time(timestr);
2542 if (ads->config.current_time != 0) {
2543 ads->auth.time_offset = ads->config.current_time - time(NULL);
2544 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2547 ads_msgfree(ads, res);
2549 status = ADS_SUCCESS;
2552 /* free any temporary ads connections */
2553 if ( ads_s != ads ) {
2554 ads_destroy( &ads_s );
2556 talloc_destroy(ctx);
2561 /********************************************************************
2562 ********************************************************************/
2564 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2566 const char *attrs[] = {"domainFunctionality", NULL};
2569 ADS_STRUCT *ads_s = ads;
2571 *val = DS_DOMAIN_FUNCTION_2000;
2573 /* establish a new ldap tcp session if necessary */
2575 if ( !ads->ldap.ld ) {
2576 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2577 ads->server.ldap_server )) == NULL )
2581 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2582 status = ads_connect( ads_s );
2583 if ( !ADS_ERR_OK(status))
2587 /* If the attribute does not exist assume it is a Windows 2000
2588 functional domain */
2590 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2591 if (!ADS_ERR_OK(status)) {
2592 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2593 status = ADS_SUCCESS;
2598 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2599 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2601 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2604 ads_msgfree(ads, res);
2607 /* free any temporary ads connections */
2608 if ( ads_s != ads ) {
2609 ads_destroy( &ads_s );
2616 * find the domain sid for our domain
2617 * @param ads connection to ads server
2618 * @param sid Pointer to domain sid
2619 * @return status of search
2621 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2623 const char *attrs[] = {"objectSid", NULL};
2627 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2629 if (!ADS_ERR_OK(rc)) return rc;
2630 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2631 ads_msgfree(ads, res);
2632 return ADS_ERROR_SYSTEM(ENOENT);
2634 ads_msgfree(ads, res);
2640 * find our site name
2641 * @param ads connection to ads server
2642 * @param mem_ctx Pointer to talloc context
2643 * @param site_name Pointer to the sitename
2644 * @return status of search
2646 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2650 const char *dn, *service_name;
2651 const char *attrs[] = { "dsServiceName", NULL };
2653 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2654 if (!ADS_ERR_OK(status)) {
2658 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2659 if (service_name == NULL) {
2660 ads_msgfree(ads, res);
2661 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2664 ads_msgfree(ads, res);
2666 /* go up three levels */
2667 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2669 return ADS_ERROR(LDAP_NO_MEMORY);
2672 *site_name = talloc_strdup(mem_ctx, dn);
2673 if (*site_name == NULL) {
2674 return ADS_ERROR(LDAP_NO_MEMORY);
2679 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2684 * find the site dn where a machine resides
2685 * @param ads connection to ads server
2686 * @param mem_ctx Pointer to talloc context
2687 * @param computer_name name of the machine
2688 * @param site_name Pointer to the sitename
2689 * @return status of search
2691 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2695 const char *parent, *filter;
2696 char *config_context = NULL;
2699 /* shortcut a query */
2700 if (strequal(computer_name, ads->config.ldap_server_name)) {
2701 return ads_site_dn(ads, mem_ctx, site_dn);
2704 status = ads_config_path(ads, mem_ctx, &config_context);
2705 if (!ADS_ERR_OK(status)) {
2709 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2710 if (filter == NULL) {
2711 return ADS_ERROR(LDAP_NO_MEMORY);
2714 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2715 filter, NULL, &res);
2716 if (!ADS_ERR_OK(status)) {
2720 if (ads_count_replies(ads, res) != 1) {
2721 ads_msgfree(ads, res);
2722 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2725 dn = ads_get_dn(ads, res);
2727 ads_msgfree(ads, res);
2728 return ADS_ERROR(LDAP_NO_MEMORY);
2731 /* go up three levels */
2732 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2733 if (parent == NULL) {
2734 ads_msgfree(ads, res);
2735 ads_memfree(ads, dn);
2736 return ADS_ERROR(LDAP_NO_MEMORY);
2739 *site_dn = talloc_strdup(mem_ctx, parent);
2740 if (*site_dn == NULL) {
2741 ads_msgfree(ads, res);
2742 ads_memfree(ads, dn);
2743 return ADS_ERROR(LDAP_NO_MEMORY);
2746 ads_memfree(ads, dn);
2747 ads_msgfree(ads, res);
2753 * get the upn suffixes for a domain
2754 * @param ads connection to ads server
2755 * @param mem_ctx Pointer to talloc context
2756 * @param suffixes Pointer to an array of suffixes
2757 * @param num_suffixes Pointer to the number of suffixes
2758 * @return status of search
2760 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2765 char *config_context = NULL;
2766 const char *attrs[] = { "uPNSuffixes", NULL };
2768 status = ads_config_path(ads, mem_ctx, &config_context);
2769 if (!ADS_ERR_OK(status)) {
2773 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2775 return ADS_ERROR(LDAP_NO_MEMORY);
2778 status = ads_search_dn(ads, &res, base, attrs);
2779 if (!ADS_ERR_OK(status)) {
2783 if (ads_count_replies(ads, res) != 1) {
2784 ads_msgfree(ads, res);
2785 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2788 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2789 if ((*suffixes) == NULL) {
2790 ads_msgfree(ads, res);
2791 return ADS_ERROR(LDAP_NO_MEMORY);
2794 ads_msgfree(ads, res);
2800 * get the joinable ous for a domain
2801 * @param ads connection to ads server
2802 * @param mem_ctx Pointer to talloc context
2803 * @param ous Pointer to an array of ous
2804 * @param num_ous Pointer to the number of ous
2805 * @return status of search
2807 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2808 TALLOC_CTX *mem_ctx,
2813 LDAPMessage *res = NULL;
2814 LDAPMessage *msg = NULL;
2815 const char *attrs[] = { "dn", NULL };
2818 status = ads_search(ads, &res,
2819 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2821 if (!ADS_ERR_OK(status)) {
2825 count = ads_count_replies(ads, res);
2827 ads_msgfree(ads, res);
2828 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2831 for (msg = ads_first_entry(ads, res); msg;
2832 msg = ads_next_entry(ads, msg)) {
2836 dn = ads_get_dn(ads, msg);
2838 ads_msgfree(ads, res);
2839 return ADS_ERROR(LDAP_NO_MEMORY);
2842 if (!add_string_to_array(mem_ctx, dn,
2843 (const char ***)ous,
2845 ads_memfree(ads, dn);
2846 ads_msgfree(ads, res);
2847 return ADS_ERROR(LDAP_NO_MEMORY);
2850 ads_memfree(ads, dn);
2853 ads_msgfree(ads, res);
2860 * pull a DOM_SID from an extended dn string
2861 * @param mem_ctx TALLOC_CTX
2862 * @param extended_dn string
2863 * @param flags string type of extended_dn
2864 * @param sid pointer to a DOM_SID
2865 * @return boolean inidicating success
2867 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2868 const char *extended_dn,
2869 enum ads_extended_dn_flags flags,
2878 /* otherwise extended_dn gets stripped off */
2879 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2883 * ADS_EXTENDED_DN_HEX_STRING:
2884 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2886 * ADS_EXTENDED_DN_STRING (only with w2k3):
2887 <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
2890 p = strchr(dn, ';');
2895 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2899 p += strlen(";<SID=");
2908 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2912 case ADS_EXTENDED_DN_STRING:
2913 if (!string_to_sid(sid, p)) {
2917 case ADS_EXTENDED_DN_HEX_STRING: {
2921 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2926 if (!sid_parse(buf, buf_len, sid)) {
2927 DEBUG(10,("failed to parse sid\n"));
2933 DEBUG(10,("unknown extended dn format\n"));
2941 * pull an array of DOM_SIDs from a ADS result
2942 * @param ads connection to ads server
2943 * @param mem_ctx TALLOC_CTX for allocating sid array
2944 * @param msg Results of search
2945 * @param field Attribute to retrieve
2946 * @param flags string type of extended_dn
2947 * @param sids pointer to sid array to allocate
2948 * @return the count of SIDs pulled
2950 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2951 TALLOC_CTX *mem_ctx,
2954 enum ads_extended_dn_flags flags,
2961 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2962 &dn_count)) == NULL) {
2966 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2968 TALLOC_FREE(dn_strings);
2972 for (i=0; i<dn_count; i++) {
2974 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2975 flags, &(*sids)[i])) {
2977 TALLOC_FREE(dn_strings);
2982 TALLOC_FREE(dn_strings);
2987 /********************************************************************
2988 ********************************************************************/
2990 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2992 LDAPMessage *res = NULL;
2997 status = ads_find_machine_acct(ads, &res, global_myname());
2998 if (!ADS_ERR_OK(status)) {
2999 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3004 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3005 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3009 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3010 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3014 ads_msgfree(ads, res);
3019 /********************************************************************
3020 ********************************************************************/
3022 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3024 LDAPMessage *res = NULL;
3029 status = ads_find_machine_acct(ads, &res, global_myname());
3030 if (!ADS_ERR_OK(status)) {
3031 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3036 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3037 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3041 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3042 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3046 ads_msgfree(ads, res);
3051 /********************************************************************
3052 ********************************************************************/
3054 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3056 LDAPMessage *res = NULL;
3061 status = ads_find_machine_acct(ads, &res, global_myname());
3062 if (!ADS_ERR_OK(status)) {
3063 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3068 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3069 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3073 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3074 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3078 ads_msgfree(ads, res);
3085 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3088 * Join a machine to a realm
3089 * Creates the machine account and sets the machine password
3090 * @param ads connection to ads server
3091 * @param machine name of host to add
3092 * @param org_unit Organizational unit to place machine in
3093 * @return status of join
3095 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3096 uint32 account_type, const char *org_unit)
3099 LDAPMessage *res = NULL;
3102 /* machine name must be lowercase */
3103 machine = SMB_STRDUP(machine_name);
3104 strlower_m(machine);
3107 status = ads_find_machine_acct(ads, (void **)&res, machine);
3108 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3109 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3110 status = ads_leave_realm(ads, machine);
3111 if (!ADS_ERR_OK(status)) {
3112 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3113 machine, ads->config.realm));
3118 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3119 if (!ADS_ERR_OK(status)) {
3120 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3125 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3126 if (!ADS_ERR_OK(status)) {
3127 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3133 ads_msgfree(ads, res);
3140 * Delete a machine from the realm
3141 * @param ads connection to ads server
3142 * @param hostname Machine to remove
3143 * @return status of delete
3145 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3150 char *hostnameDN, *host;
3152 LDAPControl ldap_control;
3153 LDAPControl * pldap_control[2] = {NULL, NULL};
3155 pldap_control[0] = &ldap_control;
3156 memset(&ldap_control, 0, sizeof(LDAPControl));
3157 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3159 /* hostname must be lowercase */
3160 host = SMB_STRDUP(hostname);
3163 status = ads_find_machine_acct(ads, &res, host);
3164 if (!ADS_ERR_OK(status)) {
3165 DEBUG(0, ("Host account for %s does not exist.\n", host));
3170 msg = ads_first_entry(ads, res);
3173 return ADS_ERROR_SYSTEM(ENOENT);
3176 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3178 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3180 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3182 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3185 if (rc != LDAP_SUCCESS) {
3186 const char *attrs[] = { "cn", NULL };
3187 LDAPMessage *msg_sub;
3189 /* we only search with scope ONE, we do not expect any further
3190 * objects to be created deeper */
3192 status = ads_do_search_retry(ads, hostnameDN,
3193 LDAP_SCOPE_ONELEVEL,
3194 "(objectclass=*)", attrs, &res);
3196 if (!ADS_ERR_OK(status)) {
3198 ads_memfree(ads, hostnameDN);
3202 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3203 msg_sub = ads_next_entry(ads, msg_sub)) {
3207 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3209 ads_memfree(ads, hostnameDN);
3210 return ADS_ERROR(LDAP_NO_MEMORY);
3213 status = ads_del_dn(ads, dn);
3214 if (!ADS_ERR_OK(status)) {
3215 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3217 ads_memfree(ads, dn);
3218 ads_memfree(ads, hostnameDN);
3222 ads_memfree(ads, dn);
3225 /* there should be no subordinate objects anymore */
3226 status = ads_do_search_retry(ads, hostnameDN,
3227 LDAP_SCOPE_ONELEVEL,
3228 "(objectclass=*)", attrs, &res);
3230 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3232 ads_memfree(ads, hostnameDN);
3236 /* delete hostnameDN now */
3237 status = ads_del_dn(ads, hostnameDN);
3238 if (!ADS_ERR_OK(status)) {
3240 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3241 ads_memfree(ads, hostnameDN);
3246 ads_memfree(ads, hostnameDN);
3248 status = ads_find_machine_acct(ads, &res, host);
3249 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3250 DEBUG(3, ("Failed to remove host account.\n"));
3260 * pull all token-sids from an LDAP dn
3261 * @param ads connection to ads server
3262 * @param mem_ctx TALLOC_CTX for allocating sid array
3263 * @param dn of LDAP object
3264 * @param user_sid pointer to DOM_SID (objectSid)
3265 * @param primary_group_sid pointer to DOM_SID (self composed)
3266 * @param sids pointer to sid array to allocate
3267 * @param num_sids counter of SIDs pulled
3268 * @return status of token query
3270 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3271 TALLOC_CTX *mem_ctx,
3274 DOM_SID *primary_group_sid,
3279 LDAPMessage *res = NULL;
3281 size_t tmp_num_sids;
3283 DOM_SID tmp_user_sid;
3284 DOM_SID tmp_primary_group_sid;
3286 const char *attrs[] = {
3293 status = ads_search_retry_dn(ads, &res, dn, attrs);
3294 if (!ADS_ERR_OK(status)) {
3298 count = ads_count_replies(ads, res);
3300 ads_msgfree(ads, res);
3301 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3304 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3305 ads_msgfree(ads, res);
3306 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3309 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3310 ads_msgfree(ads, res);
3311 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3315 /* hack to compose the primary group sid without knowing the
3321 sid_copy(&domsid, &tmp_user_sid);
3323 if (!sid_split_rid(&domsid, &dummy_rid)) {
3324 ads_msgfree(ads, res);
3325 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3328 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3329 ads_msgfree(ads, res);
3330 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3334 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3336 if (tmp_num_sids == 0 || !tmp_sids) {
3337 ads_msgfree(ads, res);
3338 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3342 *num_sids = tmp_num_sids;
3350 *user_sid = tmp_user_sid;
3353 if (primary_group_sid) {
3354 *primary_group_sid = tmp_primary_group_sid;
3357 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3359 ads_msgfree(ads, res);
3360 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3364 * Find a sAMAccoutName in LDAP
3365 * @param ads connection to ads server
3366 * @param mem_ctx TALLOC_CTX for allocating sid array
3367 * @param samaccountname to search
3368 * @param uac_ret uint32 pointer userAccountControl attribute value
3369 * @param dn_ret pointer to dn
3370 * @return status of token query
3372 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3373 TALLOC_CTX *mem_ctx,
3374 const char *samaccountname,
3376 const char **dn_ret)
3379 const char *attrs[] = { "userAccountControl", NULL };
3381 LDAPMessage *res = NULL;
3385 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3387 if (filter == NULL) {
3391 status = ads_do_search_all(ads, ads->config.bind_path,
3393 filter, attrs, &res);
3395 if (!ADS_ERR_OK(status)) {
3399 if (ads_count_replies(ads, res) != 1) {
3400 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3404 dn = ads_get_dn(ads, res);
3406 status = ADS_ERROR(LDAP_NO_MEMORY);
3410 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3411 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3420 *dn_ret = talloc_strdup(mem_ctx, dn);
3422 status = ADS_ERROR(LDAP_NO_MEMORY);
3427 ads_memfree(ads, dn);
3428 ads_msgfree(ads, res);
3434 * find our configuration path
3435 * @param ads connection to ads server
3436 * @param mem_ctx Pointer to talloc context
3437 * @param config_path Pointer to the config path
3438 * @return status of search
3440 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3441 TALLOC_CTX *mem_ctx,
3445 LDAPMessage *res = NULL;
3446 const char *config_context = NULL;
3447 const char *attrs[] = { "configurationNamingContext", NULL };
3449 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3450 "(objectclass=*)", attrs, &res);
3451 if (!ADS_ERR_OK(status)) {
3455 config_context = ads_pull_string(ads, mem_ctx, res,
3456 "configurationNamingContext");
3457 ads_msgfree(ads, res);
3458 if (!config_context) {
3459 return ADS_ERROR(LDAP_NO_MEMORY);
3463 *config_path = talloc_strdup(mem_ctx, config_context);
3464 if (!*config_path) {
3465 return ADS_ERROR(LDAP_NO_MEMORY);
3469 return ADS_ERROR(LDAP_SUCCESS);
3473 * find the displayName of an extended right
3474 * @param ads connection to ads server
3475 * @param config_path The config path
3476 * @param mem_ctx Pointer to talloc context
3477 * @param GUID struct of the rightsGUID
3478 * @return status of search
3480 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3481 const char *config_path,
3482 TALLOC_CTX *mem_ctx,
3483 const struct GUID *rights_guid)
3486 LDAPMessage *res = NULL;
3488 const char *attrs[] = { "displayName", NULL };
3489 const char *result = NULL;
3492 if (!ads || !mem_ctx || !rights_guid) {
3496 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3497 smb_uuid_string(mem_ctx, *rights_guid));
3502 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3507 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3509 if (!ADS_ERR_OK(rc)) {
3513 if (ads_count_replies(ads, res) != 1) {
3517 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3520 ads_msgfree(ads, res);