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)
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
68 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
69 server, port, strerror(errno)));
72 /* Teardown timeout. */
73 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
79 static int ldap_search_with_timeout(LDAP *ld,
80 LDAP_CONST char *base,
82 LDAP_CONST char *filter,
90 struct timeval timeout;
93 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
94 timeout.tv_sec = lp_ldap_timeout();
97 /* Setup alarm timeout.... Do we need both of these ? JRA. */
99 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
100 alarm(lp_ldap_timeout());
101 /* End setup timeout. */
103 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
104 attrsonly, sctrls, cctrls, &timeout,
107 /* Teardown timeout. */
108 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
112 return LDAP_TIMELIMIT_EXCEEDED;
117 /**********************************************
118 Do client and server sitename match ?
119 **********************************************/
121 bool ads_sitename_match(ADS_STRUCT *ads)
123 if (ads->config.server_site_name == NULL &&
124 ads->config.client_site_name == NULL ) {
125 DEBUG(10,("ads_sitename_match: both null\n"));
128 if (ads->config.server_site_name &&
129 ads->config.client_site_name &&
130 strequal(ads->config.server_site_name,
131 ads->config.client_site_name)) {
132 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
135 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
136 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
137 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
141 /**********************************************
142 Is this the closest DC ?
143 **********************************************/
145 bool ads_closest_dc(ADS_STRUCT *ads)
147 if (ads->config.flags & ADS_CLOSEST) {
148 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
152 /* not sure if this can ever happen */
153 if (ads_sitename_match(ads)) {
154 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
158 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
159 ads->config.ldap_server_name));
166 try a connection to a given ldap server, returning True and setting the servers IP
167 in the ads struct if successful
169 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
172 struct cldap_netlogon_reply cldap_reply;
174 if (!server || !*server) {
178 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
179 server, ads->server.realm));
181 /* this copes with inet_ntoa brokenness */
183 srv = SMB_STRDUP(server);
185 ZERO_STRUCT( cldap_reply );
187 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
188 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
193 /* Check the CLDAP reply flags */
195 if ( !(cldap_reply.flags & ADS_LDAP) ) {
196 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
202 /* Fill in the ads->config values */
204 SAFE_FREE(ads->config.realm);
205 SAFE_FREE(ads->config.bind_path);
206 SAFE_FREE(ads->config.ldap_server_name);
207 SAFE_FREE(ads->config.server_site_name);
208 SAFE_FREE(ads->config.client_site_name);
209 SAFE_FREE(ads->server.workgroup);
211 ads->config.flags = cldap_reply.flags;
212 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
213 strupper_m(cldap_reply.domain);
214 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
215 ads->config.bind_path = ads_build_dn(ads->config.realm);
216 if (*cldap_reply.server_site_name) {
217 ads->config.server_site_name =
218 SMB_STRDUP(cldap_reply.server_site_name);
220 if (*cldap_reply.client_site_name) {
221 ads->config.client_site_name =
222 SMB_STRDUP(cldap_reply.client_site_name);
224 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
226 ads->ldap.port = LDAP_PORT;
227 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
228 DEBUG(1,("ads_try_connect: unable to convert %s "
237 /* Store our site name. */
238 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
243 /**********************************************************************
244 Try to find an AD dc using our internal name resolution routines
245 Try the realm first and then then workgroup name if netbios is not
247 **********************************************************************/
249 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
253 struct ip_service *ip_list;
255 bool got_realm = False;
256 bool use_own_domain = False;
258 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
260 /* if the realm and workgroup are both empty, assume they are ours */
263 c_realm = ads->server.realm;
265 if ( !c_realm || !*c_realm ) {
266 /* special case where no realm and no workgroup means our own */
267 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
268 use_own_domain = True;
269 c_realm = lp_realm();
273 if (c_realm && *c_realm)
276 /* we need to try once with the realm name and fallback to the
277 netbios domain name if we fail (if netbios has not been disabled */
279 if ( !got_realm && !lp_disable_netbios() ) {
280 c_realm = ads->server.workgroup;
281 if (!c_realm || !*c_realm) {
282 if ( use_own_domain )
283 c_realm = lp_workgroup();
286 if ( !c_realm || !*c_realm ) {
287 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
288 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
294 sitename = sitename_fetch(realm);
298 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
299 (got_realm ? "realm" : "domain"), realm));
301 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
302 if (!NT_STATUS_IS_OK(status)) {
303 /* fall back to netbios if we can */
304 if ( got_realm && !lp_disable_netbios() ) {
313 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
314 for ( i=0; i<count; i++ ) {
315 char server[INET6_ADDRSTRLEN];
317 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
319 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
323 /* realm in this case is a workgroup name. We need
324 to ignore any IP addresses in the negative connection
325 cache that match ip addresses returned in the ad realm
326 case. It sucks that I have to reproduce the logic above... */
327 c_realm = ads->server.realm;
328 if ( !c_realm || !*c_realm ) {
329 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
330 c_realm = lp_realm();
333 if (c_realm && *c_realm &&
334 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
335 /* Ensure we add the workgroup name for this
336 IP address as negative too. */
337 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
342 if ( ads_try_connect(ads, server) ) {
348 /* keep track of failures */
349 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
354 /* In case we failed to contact one of our closest DC on our site we
355 * need to try to find another DC, retry with a site-less SRV DNS query
359 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
360 "trying to find another DC\n", sitename));
362 namecache_delete(realm, 0x1C);
366 return NT_STATUS_NO_LOGON_SERVERS;
371 * Connect to the LDAP server
372 * @param ads Pointer to an existing ADS_STRUCT
373 * @return status of connection
375 ADS_STATUS ads_connect(ADS_STRUCT *ads)
377 int version = LDAP_VERSION3;
380 char addr[INET6_ADDRSTRLEN];
382 ZERO_STRUCT(ads->ldap);
383 ads->ldap.last_attempt = time(NULL);
384 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
386 /* try with a user specified server */
388 if (ads->server.ldap_server &&
389 ads_try_connect(ads, ads->server.ldap_server)) {
393 ntstatus = ads_find_dc(ads);
394 if (NT_STATUS_IS_OK(ntstatus)) {
398 return ADS_ERROR_NT(ntstatus);
402 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
403 DEBUG(3,("Connected to LDAP server %s\n", addr));
405 if (!ads->auth.user_name) {
406 /* Must use the userPrincipalName value here or sAMAccountName
407 and not servicePrincipalName; found by Guenther Deschner */
409 asprintf(&ads->auth.user_name, "%s$", global_myname() );
412 if (!ads->auth.realm) {
413 ads->auth.realm = SMB_STRDUP(ads->config.realm);
416 if (!ads->auth.kdc_server) {
417 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
418 ads->auth.kdc_server = SMB_STRDUP(addr);
422 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
423 to MIT kerberos to work (tridge) */
426 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
427 setenv(env, ads->auth.kdc_server, 1);
432 /* If the caller() requested no LDAP bind, then we are done */
434 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
438 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
439 if (!ads->ldap.mem_ctx) {
440 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
443 /* Otherwise setup the TCP LDAP session */
445 if ( (ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
446 LDAP_PORT, lp_ldap_timeout())) == NULL )
448 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
451 /* cache the successful connection for workgroup and realm */
452 if (ads_closest_dc(ads)) {
453 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
454 saf_store( ads->server.workgroup, addr);
455 saf_store( ads->server.realm, addr);
458 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
460 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
461 if (!ADS_ERR_OK(status)) {
465 /* fill in the current time and offsets */
467 status = ads_current_time( ads );
468 if ( !ADS_ERR_OK(status) ) {
472 /* Now do the bind */
474 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
475 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, NULL, NULL));
478 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
479 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
482 return ads_sasl_bind(ads);
486 * Disconnect the LDAP server
487 * @param ads Pointer to an existing ADS_STRUCT
489 void ads_disconnect(ADS_STRUCT *ads)
492 ldap_unbind(ads->ldap.ld);
495 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
496 ads->ldap.wrap_ops->disconnect(ads);
498 if (ads->ldap.mem_ctx) {
499 talloc_free(ads->ldap.mem_ctx);
501 ZERO_STRUCT(ads->ldap);
505 Duplicate a struct berval into talloc'ed memory
507 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
509 struct berval *value;
511 if (!in_val) return NULL;
513 value = TALLOC_ZERO_P(ctx, struct berval);
516 if (in_val->bv_len == 0) return value;
518 value->bv_len = in_val->bv_len;
519 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
525 Make a values list out of an array of (struct berval *)
527 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
528 const struct berval **in_vals)
530 struct berval **values;
533 if (!in_vals) return NULL;
534 for (i=0; in_vals[i]; i++)
536 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
537 if (!values) return NULL;
539 for (i=0; in_vals[i]; i++) {
540 values[i] = dup_berval(ctx, in_vals[i]);
546 UTF8-encode a values list out of an array of (char *)
548 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
553 if (!in_vals) return NULL;
554 for (i=0; in_vals[i]; i++)
556 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
557 if (!values) return NULL;
559 for (i=0; in_vals[i]; i++) {
560 push_utf8_talloc(ctx, &values[i], in_vals[i]);
566 Pull a (char *) array out of a UTF8-encoded values list
568 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
573 if (!in_vals) return NULL;
574 for (i=0; in_vals[i]; i++)
576 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
577 if (!values) return NULL;
579 for (i=0; in_vals[i]; i++) {
580 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
586 * Do a search with paged results. cookie must be null on the first
587 * call, and then returned on each subsequent call. It will be null
588 * again when the entire search is complete
589 * @param ads connection to ads server
590 * @param bind_path Base dn for the search
591 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
592 * @param expr Search expression - specified in local charset
593 * @param attrs Attributes to retrieve - specified in utf8 or ascii
594 * @param res ** which will contain results - free res* with ads_msgfree()
595 * @param count Number of entries retrieved on this page
596 * @param cookie The paged results cookie to be returned on subsequent calls
597 * @return status of search
599 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
600 const char *bind_path,
601 int scope, const char *expr,
602 const char **attrs, void *args,
604 int *count, struct berval **cookie)
607 char *utf8_expr, *utf8_path, **search_attrs;
608 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
609 BerElement *cookie_be = NULL;
610 struct berval *cookie_bv= NULL;
611 BerElement *ext_be = NULL;
612 struct berval *ext_bv= NULL;
615 ads_control *external_control = (ads_control *) args;
619 if (!(ctx = talloc_init("ads_do_paged_search_args")))
620 return ADS_ERROR(LDAP_NO_MEMORY);
622 /* 0 means the conversion worked but the result was empty
623 so we only fail if it's -1. In any case, it always
624 at least nulls out the dest */
625 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
626 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
631 if (!attrs || !(*attrs))
634 /* This would be the utf8-encoded version...*/
635 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
636 if (!(str_list_copy(&search_attrs, attrs))) {
642 /* Paged results only available on ldap v3 or later */
643 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
644 if (version < LDAP_VERSION3) {
645 rc = LDAP_NOT_SUPPORTED;
649 cookie_be = ber_alloc_t(LBER_USE_DER);
651 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
652 ber_bvfree(*cookie); /* don't need it from last time */
655 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
657 ber_flatten(cookie_be, &cookie_bv);
658 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
659 PagedResults.ldctl_iscritical = (char) 1;
660 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
661 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
663 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
664 NoReferrals.ldctl_iscritical = (char) 0;
665 NoReferrals.ldctl_value.bv_len = 0;
666 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
668 if (external_control &&
669 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
670 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
672 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
673 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
675 /* win2k does not accept a ldctl_value beeing passed in */
677 if (external_control->val != 0) {
679 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
684 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
688 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
693 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
694 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
697 ExternalCtrl.ldctl_value.bv_len = 0;
698 ExternalCtrl.ldctl_value.bv_val = NULL;
701 controls[0] = &NoReferrals;
702 controls[1] = &PagedResults;
703 controls[2] = &ExternalCtrl;
707 controls[0] = &NoReferrals;
708 controls[1] = &PagedResults;
712 /* we need to disable referrals as the openldap libs don't
713 handle them and paged results at the same time. Using them
714 together results in the result record containing the server
715 page control being removed from the result list (tridge/jmcd)
717 leaving this in despite the control that says don't generate
718 referrals, in case the server doesn't support it (jmcd)
720 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
722 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
723 search_attrs, 0, controls,
725 (LDAPMessage **)res);
727 ber_free(cookie_be, 1);
728 ber_bvfree(cookie_bv);
731 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
732 ldap_err2string(rc)));
736 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
737 NULL, &rcontrols, 0);
743 for (i=0; rcontrols[i]; i++) {
744 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
745 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
746 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
748 /* the berval is the cookie, but must be freed when
750 if (cookie_bv->bv_len) /* still more to do */
751 *cookie=ber_bvdup(cookie_bv);
754 ber_bvfree(cookie_bv);
755 ber_free(cookie_be, 1);
759 ldap_controls_free(rcontrols);
772 /* if/when we decide to utf8-encode attrs, take out this next line */
773 str_list_free(&search_attrs);
775 return ADS_ERROR(rc);
778 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
779 int scope, const char *expr,
780 const char **attrs, LDAPMessage **res,
781 int *count, struct berval **cookie)
783 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
788 * Get all results for a search. This uses ads_do_paged_search() to return
789 * all entries in a large search.
790 * @param ads connection to ads server
791 * @param bind_path Base dn for the search
792 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
793 * @param expr Search expression
794 * @param attrs Attributes to retrieve
795 * @param res ** which will contain results - free res* with ads_msgfree()
796 * @return status of search
798 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
799 int scope, const char *expr,
800 const char **attrs, void *args,
803 struct berval *cookie = NULL;
808 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
811 if (!ADS_ERR_OK(status))
814 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
816 LDAPMessage *res2 = NULL;
818 LDAPMessage *msg, *next;
820 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
821 attrs, args, &res2, &count, &cookie);
823 if (!ADS_ERR_OK(status2)) break;
825 /* this relies on the way that ldap_add_result_entry() works internally. I hope
826 that this works on all ldap libs, but I have only tested with openldap */
827 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
828 next = ads_next_entry(ads, msg);
829 ldap_add_result_entry((LDAPMessage **)res, msg);
831 /* note that we do not free res2, as the memory is now
832 part of the main returned list */
835 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
836 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
842 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
843 int scope, const char *expr,
844 const char **attrs, LDAPMessage **res)
846 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
849 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
850 int scope, const char *expr,
851 const char **attrs, uint32 sd_flags,
856 args.control = ADS_SD_FLAGS_OID;
858 args.critical = True;
860 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
865 * Run a function on all results for a search. Uses ads_do_paged_search() and
866 * runs the function as each page is returned, using ads_process_results()
867 * @param ads connection to ads server
868 * @param bind_path Base dn for the search
869 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
870 * @param expr Search expression - specified in local charset
871 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
872 * @param fn Function which takes attr name, values list, and data_area
873 * @param data_area Pointer which is passed to function on each call
874 * @return status of search
876 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
877 int scope, const char *expr, const char **attrs,
878 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
881 struct berval *cookie = NULL;
886 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
889 if (!ADS_ERR_OK(status)) return status;
891 ads_process_results(ads, res, fn, data_area);
892 ads_msgfree(ads, res);
895 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
896 &res, &count, &cookie);
898 if (!ADS_ERR_OK(status)) break;
900 ads_process_results(ads, res, fn, data_area);
901 ads_msgfree(ads, res);
908 * Do a search with a timeout.
909 * @param ads connection to ads server
910 * @param bind_path Base dn for the search
911 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
912 * @param expr Search expression
913 * @param attrs Attributes to retrieve
914 * @param res ** which will contain results - free res* with ads_msgfree()
915 * @return status of search
917 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
919 const char **attrs, LDAPMessage **res)
922 char *utf8_expr, *utf8_path, **search_attrs = NULL;
926 if (!(ctx = talloc_init("ads_do_search"))) {
927 DEBUG(1,("ads_do_search: talloc_init() failed!"));
928 return ADS_ERROR(LDAP_NO_MEMORY);
931 /* 0 means the conversion worked but the result was empty
932 so we only fail if it's negative. In any case, it always
933 at least nulls out the dest */
934 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
935 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
936 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
941 if (!attrs || !(*attrs))
944 /* This would be the utf8-encoded version...*/
945 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
946 if (!(str_list_copy(&search_attrs, attrs)))
948 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
954 /* see the note in ads_do_paged_search - we *must* disable referrals */
955 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
957 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
958 search_attrs, 0, NULL, NULL,
960 (LDAPMessage **)res);
962 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
963 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
969 /* if/when we decide to utf8-encode attrs, take out this next line */
970 str_list_free(&search_attrs);
971 return ADS_ERROR(rc);
974 * Do a general ADS search
975 * @param ads connection to ads server
976 * @param res ** which will contain results - free res* with ads_msgfree()
977 * @param expr Search expression
978 * @param attrs Attributes to retrieve
979 * @return status of search
981 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
982 const char *expr, const char **attrs)
984 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
989 * Do a search on a specific DistinguishedName
990 * @param ads connection to ads server
991 * @param res ** which will contain results - free res* with ads_msgfree()
992 * @param dn DistinguishName to search
993 * @param attrs Attributes to retrieve
994 * @return status of search
996 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
997 const char *dn, const char **attrs)
999 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1004 * Free up memory from a ads_search
1005 * @param ads connection to ads server
1006 * @param msg Search results to free
1008 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1015 * Free up memory from various ads requests
1016 * @param ads connection to ads server
1017 * @param mem Area to free
1019 void ads_memfree(ADS_STRUCT *ads, void *mem)
1025 * Get a dn from search results
1026 * @param ads connection to ads server
1027 * @param msg Search result
1030 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1032 char *utf8_dn, *unix_dn;
1034 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1037 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1041 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1042 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1046 ldap_memfree(utf8_dn);
1051 * Get the parent from a dn
1052 * @param dn the dn to return the parent from
1053 * @return parent dn string
1055 char *ads_parent_dn(const char *dn)
1063 p = strchr(dn, ',');
1073 * Find a machine account given a hostname
1074 * @param ads connection to ads server
1075 * @param res ** which will contain results - free res* with ads_msgfree()
1076 * @param host Hostname to search for
1077 * @return status of search
1079 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1080 const char *machine)
1084 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1088 /* the easiest way to find a machine account anywhere in the tree
1089 is to look for hostname$ */
1090 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1091 DEBUG(1, ("asprintf failed!\n"));
1092 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1095 status = ads_search(ads, res, expr, attrs);
1101 * Initialize a list of mods to be used in a modify request
1102 * @param ctx An initialized TALLOC_CTX
1103 * @return allocated ADS_MODLIST
1105 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1107 #define ADS_MODLIST_ALLOC_SIZE 10
1110 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1111 /* -1 is safety to make sure we don't go over the end.
1112 need to reset it to NULL before doing ldap modify */
1113 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1115 return (ADS_MODLIST)mods;
1120 add an attribute to the list, with values list already constructed
1122 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1123 int mod_op, const char *name,
1124 const void *_invals)
1126 const void **invals = (const void **)_invals;
1128 LDAPMod **modlist = (LDAPMod **) *mods;
1129 struct berval **ber_values = NULL;
1130 char **char_values = NULL;
1133 mod_op = LDAP_MOD_DELETE;
1135 if (mod_op & LDAP_MOD_BVALUES)
1136 ber_values = ads_dup_values(ctx,
1137 (const struct berval **)invals);
1139 char_values = ads_push_strvals(ctx,
1140 (const char **) invals);
1143 /* find the first empty slot */
1144 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1146 if (modlist[curmod] == (LDAPMod *) -1) {
1147 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1148 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1149 return ADS_ERROR(LDAP_NO_MEMORY);
1150 memset(&modlist[curmod], 0,
1151 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1152 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1153 *mods = (ADS_MODLIST)modlist;
1156 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1157 return ADS_ERROR(LDAP_NO_MEMORY);
1158 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1159 if (mod_op & LDAP_MOD_BVALUES) {
1160 modlist[curmod]->mod_bvalues = ber_values;
1161 } else if (mod_op & LDAP_MOD_DELETE) {
1162 modlist[curmod]->mod_values = NULL;
1164 modlist[curmod]->mod_values = char_values;
1167 modlist[curmod]->mod_op = mod_op;
1168 return ADS_ERROR(LDAP_SUCCESS);
1172 * Add a single string value to a mod list
1173 * @param ctx An initialized TALLOC_CTX
1174 * @param mods An initialized ADS_MODLIST
1175 * @param name The attribute name to add
1176 * @param val The value to add - NULL means DELETE
1177 * @return ADS STATUS indicating success of add
1179 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1180 const char *name, const char *val)
1182 const char *values[2];
1188 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1189 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1193 * Add an array of string values to a mod list
1194 * @param ctx An initialized TALLOC_CTX
1195 * @param mods An initialized ADS_MODLIST
1196 * @param name The attribute name to add
1197 * @param vals The array of string values to add - NULL means DELETE
1198 * @return ADS STATUS indicating success of add
1200 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1201 const char *name, const char **vals)
1204 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1205 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1206 name, (const void **) vals);
1211 * Add a single ber-encoded value to a mod list
1212 * @param ctx An initialized TALLOC_CTX
1213 * @param mods An initialized ADS_MODLIST
1214 * @param name The attribute name to add
1215 * @param val The value to add - NULL means DELETE
1216 * @return ADS STATUS indicating success of add
1218 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1219 const char *name, const struct berval *val)
1221 const struct berval *values[2];
1226 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1227 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1228 name, (const void **) values);
1233 * Perform an ldap modify
1234 * @param ads connection to ads server
1235 * @param mod_dn DistinguishedName to modify
1236 * @param mods list of modifications to perform
1237 * @return status of modify
1239 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1242 char *utf8_dn = NULL;
1244 this control is needed to modify that contains a currently
1245 non-existent attribute (but allowable for the object) to run
1247 LDAPControl PermitModify = {
1248 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1251 LDAPControl *controls[2];
1253 controls[0] = &PermitModify;
1256 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1257 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1260 /* find the end of the list, marked by NULL or -1 */
1261 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1262 /* make sure the end of the list is NULL */
1264 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1265 (LDAPMod **) mods, controls, NULL);
1267 return ADS_ERROR(ret);
1271 * Perform an ldap add
1272 * @param ads connection to ads server
1273 * @param new_dn DistinguishedName to add
1274 * @param mods list of attributes and values for DN
1275 * @return status of add
1277 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1280 char *utf8_dn = NULL;
1282 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1283 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1284 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1287 /* find the end of the list, marked by NULL or -1 */
1288 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1289 /* make sure the end of the list is NULL */
1292 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1294 return ADS_ERROR(ret);
1298 * Delete a DistinguishedName
1299 * @param ads connection to ads server
1300 * @param new_dn DistinguishedName to delete
1301 * @return status of delete
1303 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1306 char *utf8_dn = NULL;
1307 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1308 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1309 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1312 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1314 return ADS_ERROR(ret);
1318 * Build an org unit string
1319 * if org unit is Computers or blank then assume a container, otherwise
1320 * assume a / separated list of organisational units.
1321 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1322 * @param ads connection to ads server
1323 * @param org_unit Organizational unit
1324 * @return org unit string - caller must free
1326 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1330 if (!org_unit || !*org_unit) {
1332 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1334 /* samba4 might not yet respond to a wellknownobject-query */
1335 return ret ? ret : SMB_STRDUP("cn=Computers");
1338 if (strequal(org_unit, "Computers")) {
1339 return SMB_STRDUP("cn=Computers");
1342 /* jmcd: removed "\\" from the separation chars, because it is
1343 needed as an escape for chars like '#' which are valid in an
1345 return ads_build_path(org_unit, "/", "ou=", 1);
1349 * Get a org unit string for a well-known GUID
1350 * @param ads connection to ads server
1351 * @param wknguid Well known GUID
1352 * @return org unit string - caller must free
1354 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1357 LDAPMessage *res = NULL;
1358 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1359 **bind_dn_exp = NULL;
1360 const char *attrs[] = {"distinguishedName", NULL};
1361 int new_ln, wkn_ln, bind_ln, i;
1363 if (wknguid == NULL) {
1367 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1368 DEBUG(1, ("asprintf failed!\n"));
1372 status = ads_search_dn(ads, &res, base, attrs);
1373 if (!ADS_ERR_OK(status)) {
1374 DEBUG(1,("Failed while searching for: %s\n", base));
1378 if (ads_count_replies(ads, res) != 1) {
1382 /* substitute the bind-path from the well-known-guid-search result */
1383 wkn_dn = ads_get_dn(ads, res);
1388 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1393 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1398 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1400 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1403 new_ln = wkn_ln - bind_ln;
1405 ret = SMB_STRDUP(wkn_dn_exp[0]);
1410 for (i=1; i < new_ln; i++) {
1413 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1419 ret = SMB_STRDUP(s);
1428 ads_msgfree(ads, res);
1429 ads_memfree(ads, wkn_dn);
1431 ldap_value_free(wkn_dn_exp);
1434 ldap_value_free(bind_dn_exp);
1441 * Adds (appends) an item to an attribute array, rather then
1442 * replacing the whole list
1443 * @param ctx An initialized TALLOC_CTX
1444 * @param mods An initialized ADS_MODLIST
1445 * @param name name of the ldap attribute to append to
1446 * @param vals an array of values to add
1447 * @return status of addition
1450 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1451 const char *name, const char **vals)
1453 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1454 (const void *) vals);
1458 * Determines the computer account's current KVNO via an LDAP lookup
1459 * @param ads An initialized ADS_STRUCT
1460 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1461 * @return the kvno for the computer account, or -1 in case of a failure.
1464 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1466 LDAPMessage *res = NULL;
1467 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1469 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1470 char *dn_string = NULL;
1471 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1473 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1474 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1477 ret = ads_search(ads, &res, filter, attrs);
1479 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1480 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1481 ads_msgfree(ads, res);
1485 dn_string = ads_get_dn(ads, res);
1487 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1488 ads_msgfree(ads, res);
1491 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1492 ads_memfree(ads, dn_string);
1494 /* ---------------------------------------------------------
1495 * 0 is returned as a default KVNO from this point on...
1496 * This is done because Windows 2000 does not support key
1497 * version numbers. Chances are that a failure in the next
1498 * step is simply due to Windows 2000 being used for a
1499 * domain controller. */
1502 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1503 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1504 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1505 ads_msgfree(ads, res);
1510 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1511 ads_msgfree(ads, res);
1516 * This clears out all registered spn's for a given hostname
1517 * @param ads An initilaized ADS_STRUCT
1518 * @param machine_name the NetBIOS name of the computer.
1519 * @return 0 upon success, non-zero otherwise.
1522 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1525 LDAPMessage *res = NULL;
1527 const char *servicePrincipalName[1] = {NULL};
1528 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1529 char *dn_string = NULL;
1531 ret = ads_find_machine_acct(ads, &res, machine_name);
1532 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1533 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1534 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1535 ads_msgfree(ads, res);
1536 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1539 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1540 ctx = talloc_init("ads_clear_service_principal_names");
1542 ads_msgfree(ads, res);
1543 return ADS_ERROR(LDAP_NO_MEMORY);
1546 if (!(mods = ads_init_mods(ctx))) {
1547 talloc_destroy(ctx);
1548 ads_msgfree(ads, res);
1549 return ADS_ERROR(LDAP_NO_MEMORY);
1551 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1552 if (!ADS_ERR_OK(ret)) {
1553 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1554 ads_msgfree(ads, res);
1555 talloc_destroy(ctx);
1558 dn_string = ads_get_dn(ads, res);
1560 talloc_destroy(ctx);
1561 ads_msgfree(ads, res);
1562 return ADS_ERROR(LDAP_NO_MEMORY);
1564 ret = ads_gen_mod(ads, dn_string, mods);
1565 ads_memfree(ads,dn_string);
1566 if (!ADS_ERR_OK(ret)) {
1567 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1569 ads_msgfree(ads, res);
1570 talloc_destroy(ctx);
1574 ads_msgfree(ads, res);
1575 talloc_destroy(ctx);
1580 * This adds a service principal name to an existing computer account
1581 * (found by hostname) in AD.
1582 * @param ads An initialized ADS_STRUCT
1583 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1584 * @param my_fqdn The fully qualified DNS name of the machine
1585 * @param spn A string of the service principal to add, i.e. 'host'
1586 * @return 0 upon sucess, or non-zero if a failure occurs
1589 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1590 const char *my_fqdn, const char *spn)
1594 LDAPMessage *res = NULL;
1597 char *dn_string = NULL;
1598 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1600 ret = ads_find_machine_acct(ads, &res, machine_name);
1601 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1602 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1604 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1605 spn, machine_name, ads->config.realm));
1606 ads_msgfree(ads, res);
1607 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1610 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1611 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1612 ads_msgfree(ads, res);
1613 return ADS_ERROR(LDAP_NO_MEMORY);
1616 /* add short name spn */
1618 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1619 talloc_destroy(ctx);
1620 ads_msgfree(ads, res);
1621 return ADS_ERROR(LDAP_NO_MEMORY);
1624 strlower_m(&psp1[strlen(spn)]);
1625 servicePrincipalName[0] = psp1;
1627 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1628 psp1, machine_name));
1631 /* add fully qualified spn */
1633 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1634 ret = ADS_ERROR(LDAP_NO_MEMORY);
1638 strlower_m(&psp2[strlen(spn)]);
1639 servicePrincipalName[1] = psp2;
1641 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1642 psp2, machine_name));
1644 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1645 ret = ADS_ERROR(LDAP_NO_MEMORY);
1649 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1650 if (!ADS_ERR_OK(ret)) {
1651 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1655 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1656 ret = ADS_ERROR(LDAP_NO_MEMORY);
1660 ret = ads_gen_mod(ads, dn_string, mods);
1661 ads_memfree(ads,dn_string);
1662 if (!ADS_ERR_OK(ret)) {
1663 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1669 ads_msgfree(ads, res);
1674 * adds a machine account to the ADS server
1675 * @param ads An intialized ADS_STRUCT
1676 * @param machine_name - the NetBIOS machine name of this account.
1677 * @param account_type A number indicating the type of account to create
1678 * @param org_unit The LDAP path in which to place this account
1679 * @return 0 upon success, or non-zero otherwise
1682 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1683 const char *org_unit)
1686 char *samAccountName, *controlstr;
1689 char *machine_escaped = NULL;
1691 const char *objectClass[] = {"top", "person", "organizationalPerson",
1692 "user", "computer", NULL};
1693 LDAPMessage *res = NULL;
1694 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1695 UF_DONT_EXPIRE_PASSWD |\
1696 UF_ACCOUNTDISABLE );
1698 if (!(ctx = talloc_init("ads_add_machine_acct")))
1699 return ADS_ERROR(LDAP_NO_MEMORY);
1701 ret = ADS_ERROR(LDAP_NO_MEMORY);
1703 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1704 if (!machine_escaped) {
1708 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1709 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1711 if ( !new_dn || !samAccountName ) {
1715 #ifndef ENCTYPE_ARCFOUR_HMAC
1716 acct_control |= UF_USE_DES_KEY_ONLY;
1719 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1723 if (!(mods = ads_init_mods(ctx))) {
1727 ads_mod_str(ctx, &mods, "cn", machine_name);
1728 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1729 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1730 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1732 ret = ads_gen_add(ads, new_dn, mods);
1735 SAFE_FREE(machine_escaped);
1736 ads_msgfree(ads, res);
1737 talloc_destroy(ctx);
1743 * move a machine account to another OU on the ADS server
1744 * @param ads - An intialized ADS_STRUCT
1745 * @param machine_name - the NetBIOS machine name of this account.
1746 * @param org_unit - The LDAP path in which to place this account
1747 * @param moved - whether we moved the machine account (optional)
1748 * @return 0 upon success, or non-zero otherwise
1751 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1752 const char *org_unit, bool *moved)
1756 LDAPMessage *res = NULL;
1757 char *filter = NULL;
1758 char *computer_dn = NULL;
1760 char *computer_rdn = NULL;
1761 bool need_move = False;
1763 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1764 rc = ADS_ERROR(LDAP_NO_MEMORY);
1768 /* Find pre-existing machine */
1769 rc = ads_search(ads, &res, filter, NULL);
1770 if (!ADS_ERR_OK(rc)) {
1774 computer_dn = ads_get_dn(ads, res);
1776 rc = ADS_ERROR(LDAP_NO_MEMORY);
1780 parent_dn = ads_parent_dn(computer_dn);
1781 if (strequal(parent_dn, org_unit)) {
1787 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1788 rc = ADS_ERROR(LDAP_NO_MEMORY);
1792 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1793 org_unit, 1, NULL, NULL);
1794 rc = ADS_ERROR(ldap_status);
1797 ads_msgfree(ads, res);
1799 SAFE_FREE(computer_dn);
1800 SAFE_FREE(computer_rdn);
1802 if (!ADS_ERR_OK(rc)) {
1814 dump a binary result from ldap
1816 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1819 for (i=0; values[i]; i++) {
1820 printf("%s: ", field);
1821 for (j=0; j<values[i]->bv_len; j++) {
1822 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1828 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1831 for (i=0; values[i]; i++) {
1836 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1837 smb_uuid_unpack(guid, &tmp);
1838 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1843 dump a sid result from ldap
1845 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1848 for (i=0; values[i]; i++) {
1851 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1852 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1857 dump ntSecurityDescriptor
1859 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1861 TALLOC_CTX *frame = talloc_stackframe();
1862 struct security_descriptor *psd;
1865 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1866 values[0]->bv_len, &psd);
1867 if (!NT_STATUS_IS_OK(status)) {
1868 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1869 nt_errstr(status)));
1875 ads_disp_sd(ads, talloc_tos(), psd);
1882 dump a string result from ldap
1884 static void dump_string(const char *field, char **values)
1887 for (i=0; values[i]; i++) {
1888 printf("%s: %s\n", field, values[i]);
1893 dump a field from LDAP on stdout
1897 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1902 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1904 {"objectGUID", False, dump_guid},
1905 {"netbootGUID", False, dump_guid},
1906 {"nTSecurityDescriptor", False, dump_sd},
1907 {"dnsRecord", False, dump_binary},
1908 {"objectSid", False, dump_sid},
1909 {"tokenGroups", False, dump_sid},
1910 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1911 {"tokengroupsGlobalandUniversal", False, dump_sid},
1912 {"mS-DS-CreatorSID", False, dump_sid},
1913 {"msExchMailboxGuid", False, dump_guid},
1918 if (!field) { /* must be end of an entry */
1923 for (i=0; handlers[i].name; i++) {
1924 if (StrCaseCmp(handlers[i].name, field) == 0) {
1925 if (!values) /* first time, indicate string or not */
1926 return handlers[i].string;
1927 handlers[i].handler(ads, field, (struct berval **) values);
1931 if (!handlers[i].name) {
1932 if (!values) /* first time, indicate string conversion */
1934 dump_string(field, (char **)values);
1940 * Dump a result from LDAP on stdout
1941 * used for debugging
1942 * @param ads connection to ads server
1943 * @param res Results to dump
1946 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1948 ads_process_results(ads, res, ads_dump_field, NULL);
1952 * Walk through results, calling a function for each entry found.
1953 * The function receives a field name, a berval * array of values,
1954 * and a data area passed through from the start. The function is
1955 * called once with null for field and values at the end of each
1957 * @param ads connection to ads server
1958 * @param res Results to process
1959 * @param fn Function for processing each result
1960 * @param data_area user-defined area to pass to function
1962 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1963 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1969 if (!(ctx = talloc_init("ads_process_results")))
1972 for (msg = ads_first_entry(ads, res); msg;
1973 msg = ads_next_entry(ads, msg)) {
1977 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1978 (LDAPMessage *)msg,&b);
1980 utf8_field=ldap_next_attribute(ads->ldap.ld,
1981 (LDAPMessage *)msg,b)) {
1982 struct berval **ber_vals;
1983 char **str_vals, **utf8_vals;
1987 pull_utf8_talloc(ctx, &field, utf8_field);
1988 string = fn(ads, field, NULL, data_area);
1991 utf8_vals = ldap_get_values(ads->ldap.ld,
1992 (LDAPMessage *)msg, field);
1993 str_vals = ads_pull_strvals(ctx,
1994 (const char **) utf8_vals);
1995 fn(ads, field, (void **) str_vals, data_area);
1996 ldap_value_free(utf8_vals);
1998 ber_vals = ldap_get_values_len(ads->ldap.ld,
1999 (LDAPMessage *)msg, field);
2000 fn(ads, field, (void **) ber_vals, data_area);
2002 ldap_value_free_len(ber_vals);
2004 ldap_memfree(utf8_field);
2007 talloc_free_children(ctx);
2008 fn(ads, NULL, NULL, data_area); /* completed an entry */
2011 talloc_destroy(ctx);
2015 * count how many replies are in a LDAPMessage
2016 * @param ads connection to ads server
2017 * @param res Results to count
2018 * @return number of replies
2020 int ads_count_replies(ADS_STRUCT *ads, void *res)
2022 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2026 * pull the first entry from a ADS result
2027 * @param ads connection to ads server
2028 * @param res Results of search
2029 * @return first entry from result
2031 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2033 return ldap_first_entry(ads->ldap.ld, res);
2037 * pull the next entry from a ADS result
2038 * @param ads connection to ads server
2039 * @param res Results of search
2040 * @return next entry from result
2042 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2044 return ldap_next_entry(ads->ldap.ld, res);
2048 * pull a single string from a ADS result
2049 * @param ads connection to ads server
2050 * @param mem_ctx TALLOC_CTX to use for allocating result string
2051 * @param msg Results of search
2052 * @param field Attribute to retrieve
2053 * @return Result string in talloc context
2055 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2063 values = ldap_get_values(ads->ldap.ld, msg, field);
2068 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2070 if (rc != (size_t)-1)
2074 ldap_value_free(values);
2079 * pull an array of strings from a ADS result
2080 * @param ads connection to ads server
2081 * @param mem_ctx TALLOC_CTX to use for allocating result string
2082 * @param msg Results of search
2083 * @param field Attribute to retrieve
2084 * @return Result strings in talloc context
2086 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2087 LDAPMessage *msg, const char *field,
2094 values = ldap_get_values(ads->ldap.ld, msg, field);
2098 *num_values = ldap_count_values(values);
2100 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2102 ldap_value_free(values);
2106 for (i=0;i<*num_values;i++) {
2107 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2108 ldap_value_free(values);
2114 ldap_value_free(values);
2119 * pull an array of strings from a ADS result
2120 * (handle large multivalue attributes with range retrieval)
2121 * @param ads connection to ads server
2122 * @param mem_ctx TALLOC_CTX to use for allocating result string
2123 * @param msg Results of search
2124 * @param field Attribute to retrieve
2125 * @param current_strings strings returned by a previous call to this function
2126 * @param next_attribute The next query should ask for this attribute
2127 * @param num_values How many values did we get this time?
2128 * @param more_values Are there more values to get?
2129 * @return Result strings in talloc context
2131 char **ads_pull_strings_range(ADS_STRUCT *ads,
2132 TALLOC_CTX *mem_ctx,
2133 LDAPMessage *msg, const char *field,
2134 char **current_strings,
2135 const char **next_attribute,
2136 size_t *num_strings,
2140 char *expected_range_attrib, *range_attr;
2141 BerElement *ptr = NULL;
2144 size_t num_new_strings;
2145 unsigned long int range_start;
2146 unsigned long int range_end;
2148 /* we might have been given the whole lot anyway */
2149 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2150 *more_strings = False;
2154 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2156 /* look for Range result */
2157 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2159 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2160 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2161 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2169 /* nothing here - this field is just empty */
2170 *more_strings = False;
2174 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2175 &range_start, &range_end) == 2) {
2176 *more_strings = True;
2178 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2179 &range_start) == 1) {
2180 *more_strings = False;
2182 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2184 ldap_memfree(range_attr);
2185 *more_strings = False;
2190 if ((*num_strings) != range_start) {
2191 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2192 " - aborting range retreival\n",
2193 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2194 ldap_memfree(range_attr);
2195 *more_strings = False;
2199 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2201 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2202 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2203 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2204 range_attr, (unsigned long int)range_end - range_start + 1,
2205 (unsigned long int)num_new_strings));
2206 ldap_memfree(range_attr);
2207 *more_strings = False;
2211 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2212 *num_strings + num_new_strings);
2214 if (strings == NULL) {
2215 ldap_memfree(range_attr);
2216 *more_strings = False;
2220 if (new_strings && num_new_strings) {
2221 memcpy(&strings[*num_strings], new_strings,
2222 sizeof(*new_strings) * num_new_strings);
2225 (*num_strings) += num_new_strings;
2227 if (*more_strings) {
2228 *next_attribute = talloc_asprintf(mem_ctx,
2233 if (!*next_attribute) {
2234 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2235 ldap_memfree(range_attr);
2236 *more_strings = False;
2241 ldap_memfree(range_attr);
2247 * pull a single uint32 from a ADS result
2248 * @param ads connection to ads server
2249 * @param msg Results of search
2250 * @param field Attribute to retrieve
2251 * @param v Pointer to int to store result
2252 * @return boolean inidicating success
2254 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2259 values = ldap_get_values(ads->ldap.ld, msg, field);
2263 ldap_value_free(values);
2267 *v = atoi(values[0]);
2268 ldap_value_free(values);
2273 * pull a single objectGUID from an ADS result
2274 * @param ads connection to ADS server
2275 * @param msg results of search
2276 * @param guid 37-byte area to receive text guid
2277 * @return boolean indicating success
2279 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2282 UUID_FLAT flat_guid;
2284 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2289 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2290 smb_uuid_unpack(flat_guid, guid);
2291 ldap_value_free(values);
2294 ldap_value_free(values);
2301 * pull a single DOM_SID from a ADS result
2302 * @param ads connection to ads server
2303 * @param msg Results of search
2304 * @param field Attribute to retrieve
2305 * @param sid Pointer to sid to store result
2306 * @return boolean inidicating success
2308 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2311 struct berval **values;
2314 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2320 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2322 ldap_value_free_len(values);
2327 * pull an array of DOM_SIDs from a ADS result
2328 * @param ads connection to ads server
2329 * @param mem_ctx TALLOC_CTX for allocating sid array
2330 * @param msg Results of search
2331 * @param field Attribute to retrieve
2332 * @param sids pointer to sid array to allocate
2333 * @return the count of SIDs pulled
2335 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2336 LDAPMessage *msg, const char *field, DOM_SID **sids)
2338 struct berval **values;
2342 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2347 for (i=0; values[i]; i++)
2351 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2353 ldap_value_free_len(values);
2361 for (i=0; values[i]; i++) {
2362 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2364 DEBUG(10, ("pulling SID: %s\n",
2365 sid_string_dbg(&(*sids)[count])));
2370 ldap_value_free_len(values);
2375 * pull a SEC_DESC from a ADS result
2376 * @param ads connection to ads server
2377 * @param mem_ctx TALLOC_CTX for allocating sid array
2378 * @param msg Results of search
2379 * @param field Attribute to retrieve
2380 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2381 * @return boolean inidicating success
2383 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2384 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2386 struct berval **values;
2389 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2391 if (!values) return false;
2395 status = unmarshall_sec_desc(mem_ctx,
2396 (uint8 *)values[0]->bv_val,
2397 values[0]->bv_len, sd);
2398 if (!NT_STATUS_IS_OK(status)) {
2399 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2400 nt_errstr(status)));
2405 ldap_value_free_len(values);
2410 * in order to support usernames longer than 21 characters we need to
2411 * use both the sAMAccountName and the userPrincipalName attributes
2412 * It seems that not all users have the userPrincipalName attribute set
2414 * @param ads connection to ads server
2415 * @param mem_ctx TALLOC_CTX for allocating sid array
2416 * @param msg Results of search
2417 * @return the username
2419 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2425 /* lookup_name() only works on the sAMAccountName to
2426 returning the username portion of userPrincipalName
2427 breaks winbindd_getpwnam() */
2429 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2430 if (ret && (p = strchr_m(ret, '@'))) {
2435 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2440 * find the update serial number - this is the core of the ldap cache
2441 * @param ads connection to ads server
2442 * @param ads connection to ADS server
2443 * @param usn Pointer to retrieved update serial number
2444 * @return status of search
2446 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2448 const char *attrs[] = {"highestCommittedUSN", NULL};
2452 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2453 if (!ADS_ERR_OK(status))
2456 if (ads_count_replies(ads, res) != 1) {
2457 ads_msgfree(ads, res);
2458 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2461 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2462 ads_msgfree(ads, res);
2463 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2466 ads_msgfree(ads, res);
2470 /* parse a ADS timestring - typical string is
2471 '20020917091222.0Z0' which means 09:12.22 17th September
2473 static time_t ads_parse_time(const char *str)
2479 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2480 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2481 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2490 /********************************************************************
2491 ********************************************************************/
2493 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2495 const char *attrs[] = {"currentTime", NULL};
2500 ADS_STRUCT *ads_s = ads;
2502 if (!(ctx = talloc_init("ads_current_time"))) {
2503 return ADS_ERROR(LDAP_NO_MEMORY);
2506 /* establish a new ldap tcp session if necessary */
2508 if ( !ads->ldap.ld ) {
2509 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2510 ads->server.ldap_server )) == NULL )
2514 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2515 status = ads_connect( ads_s );
2516 if ( !ADS_ERR_OK(status))
2520 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2521 if (!ADS_ERR_OK(status)) {
2525 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2527 ads_msgfree(ads_s, res);
2528 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2532 /* but save the time and offset in the original ADS_STRUCT */
2534 ads->config.current_time = ads_parse_time(timestr);
2536 if (ads->config.current_time != 0) {
2537 ads->auth.time_offset = ads->config.current_time - time(NULL);
2538 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2541 ads_msgfree(ads, res);
2543 status = ADS_SUCCESS;
2546 /* free any temporary ads connections */
2547 if ( ads_s != ads ) {
2548 ads_destroy( &ads_s );
2550 talloc_destroy(ctx);
2555 /********************************************************************
2556 ********************************************************************/
2558 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2560 const char *attrs[] = {"domainFunctionality", NULL};
2563 ADS_STRUCT *ads_s = ads;
2565 *val = DS_DOMAIN_FUNCTION_2000;
2567 /* establish a new ldap tcp session if necessary */
2569 if ( !ads->ldap.ld ) {
2570 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2571 ads->server.ldap_server )) == NULL )
2575 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2576 status = ads_connect( ads_s );
2577 if ( !ADS_ERR_OK(status))
2581 /* If the attribute does not exist assume it is a Windows 2000
2582 functional domain */
2584 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2585 if (!ADS_ERR_OK(status)) {
2586 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2587 status = ADS_SUCCESS;
2592 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2593 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2595 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2598 ads_msgfree(ads, res);
2601 /* free any temporary ads connections */
2602 if ( ads_s != ads ) {
2603 ads_destroy( &ads_s );
2610 * find the domain sid for our domain
2611 * @param ads connection to ads server
2612 * @param sid Pointer to domain sid
2613 * @return status of search
2615 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2617 const char *attrs[] = {"objectSid", NULL};
2621 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2623 if (!ADS_ERR_OK(rc)) return rc;
2624 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2625 ads_msgfree(ads, res);
2626 return ADS_ERROR_SYSTEM(ENOENT);
2628 ads_msgfree(ads, res);
2634 * find our site name
2635 * @param ads connection to ads server
2636 * @param mem_ctx Pointer to talloc context
2637 * @param site_name Pointer to the sitename
2638 * @return status of search
2640 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2644 const char *dn, *service_name;
2645 const char *attrs[] = { "dsServiceName", NULL };
2647 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2648 if (!ADS_ERR_OK(status)) {
2652 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2653 if (service_name == NULL) {
2654 ads_msgfree(ads, res);
2655 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2658 ads_msgfree(ads, res);
2660 /* go up three levels */
2661 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2663 return ADS_ERROR(LDAP_NO_MEMORY);
2666 *site_name = talloc_strdup(mem_ctx, dn);
2667 if (*site_name == NULL) {
2668 return ADS_ERROR(LDAP_NO_MEMORY);
2673 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2678 * find the site dn where a machine resides
2679 * @param ads connection to ads server
2680 * @param mem_ctx Pointer to talloc context
2681 * @param computer_name name of the machine
2682 * @param site_name Pointer to the sitename
2683 * @return status of search
2685 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2689 const char *parent, *filter;
2690 char *config_context = NULL;
2693 /* shortcut a query */
2694 if (strequal(computer_name, ads->config.ldap_server_name)) {
2695 return ads_site_dn(ads, mem_ctx, site_dn);
2698 status = ads_config_path(ads, mem_ctx, &config_context);
2699 if (!ADS_ERR_OK(status)) {
2703 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2704 if (filter == NULL) {
2705 return ADS_ERROR(LDAP_NO_MEMORY);
2708 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2709 filter, NULL, &res);
2710 if (!ADS_ERR_OK(status)) {
2714 if (ads_count_replies(ads, res) != 1) {
2715 ads_msgfree(ads, res);
2716 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2719 dn = ads_get_dn(ads, res);
2721 ads_msgfree(ads, res);
2722 return ADS_ERROR(LDAP_NO_MEMORY);
2725 /* go up three levels */
2726 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2727 if (parent == NULL) {
2728 ads_msgfree(ads, res);
2729 ads_memfree(ads, dn);
2730 return ADS_ERROR(LDAP_NO_MEMORY);
2733 *site_dn = talloc_strdup(mem_ctx, parent);
2734 if (*site_dn == NULL) {
2735 ads_msgfree(ads, res);
2736 ads_memfree(ads, dn);
2737 return ADS_ERROR(LDAP_NO_MEMORY);
2740 ads_memfree(ads, dn);
2741 ads_msgfree(ads, res);
2747 * get the upn suffixes for a domain
2748 * @param ads connection to ads server
2749 * @param mem_ctx Pointer to talloc context
2750 * @param suffixes Pointer to an array of suffixes
2751 * @param num_suffixes Pointer to the number of suffixes
2752 * @return status of search
2754 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2759 char *config_context = NULL;
2760 const char *attrs[] = { "uPNSuffixes", NULL };
2762 status = ads_config_path(ads, mem_ctx, &config_context);
2763 if (!ADS_ERR_OK(status)) {
2767 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2769 return ADS_ERROR(LDAP_NO_MEMORY);
2772 status = ads_search_dn(ads, &res, base, attrs);
2773 if (!ADS_ERR_OK(status)) {
2777 if (ads_count_replies(ads, res) != 1) {
2778 ads_msgfree(ads, res);
2779 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2782 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2783 if ((*suffixes) == NULL) {
2784 ads_msgfree(ads, res);
2785 return ADS_ERROR(LDAP_NO_MEMORY);
2788 ads_msgfree(ads, res);
2794 * get the joinable ous for a domain
2795 * @param ads connection to ads server
2796 * @param mem_ctx Pointer to talloc context
2797 * @param ous Pointer to an array of ous
2798 * @param num_ous Pointer to the number of ous
2799 * @return status of search
2801 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2802 TALLOC_CTX *mem_ctx,
2807 LDAPMessage *res = NULL;
2808 LDAPMessage *msg = NULL;
2809 const char *attrs[] = { "dn", NULL };
2812 status = ads_search(ads, &res,
2813 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2815 if (!ADS_ERR_OK(status)) {
2819 count = ads_count_replies(ads, res);
2821 ads_msgfree(ads, res);
2822 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2825 for (msg = ads_first_entry(ads, res); msg;
2826 msg = ads_next_entry(ads, msg)) {
2830 dn = ads_get_dn(ads, msg);
2832 ads_msgfree(ads, res);
2833 return ADS_ERROR(LDAP_NO_MEMORY);
2836 if (!add_string_to_array(mem_ctx, dn,
2837 (const char ***)ous,
2839 ads_memfree(ads, dn);
2840 ads_msgfree(ads, res);
2841 return ADS_ERROR(LDAP_NO_MEMORY);
2844 ads_memfree(ads, dn);
2847 ads_msgfree(ads, res);
2854 * pull a DOM_SID from an extended dn string
2855 * @param mem_ctx TALLOC_CTX
2856 * @param extended_dn string
2857 * @param flags string type of extended_dn
2858 * @param sid pointer to a DOM_SID
2859 * @return boolean inidicating success
2861 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2862 const char *extended_dn,
2863 enum ads_extended_dn_flags flags,
2872 /* otherwise extended_dn gets stripped off */
2873 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2877 * ADS_EXTENDED_DN_HEX_STRING:
2878 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2880 * ADS_EXTENDED_DN_STRING (only with w2k3):
2881 <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
2884 p = strchr(dn, ';');
2889 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2893 p += strlen(";<SID=");
2902 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2906 case ADS_EXTENDED_DN_STRING:
2907 if (!string_to_sid(sid, p)) {
2911 case ADS_EXTENDED_DN_HEX_STRING: {
2915 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2920 if (!sid_parse(buf, buf_len, sid)) {
2921 DEBUG(10,("failed to parse sid\n"));
2927 DEBUG(10,("unknown extended dn format\n"));
2935 * pull an array of DOM_SIDs from a ADS result
2936 * @param ads connection to ads server
2937 * @param mem_ctx TALLOC_CTX for allocating sid array
2938 * @param msg Results of search
2939 * @param field Attribute to retrieve
2940 * @param flags string type of extended_dn
2941 * @param sids pointer to sid array to allocate
2942 * @return the count of SIDs pulled
2944 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2945 TALLOC_CTX *mem_ctx,
2948 enum ads_extended_dn_flags flags,
2955 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2956 &dn_count)) == NULL) {
2960 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2962 TALLOC_FREE(dn_strings);
2966 for (i=0; i<dn_count; i++) {
2968 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2969 flags, &(*sids)[i])) {
2971 TALLOC_FREE(dn_strings);
2976 TALLOC_FREE(dn_strings);
2981 /********************************************************************
2982 ********************************************************************/
2984 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2986 LDAPMessage *res = NULL;
2991 status = ads_find_machine_acct(ads, &res, global_myname());
2992 if (!ADS_ERR_OK(status)) {
2993 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2998 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2999 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3003 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3004 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3008 ads_msgfree(ads, res);
3013 /********************************************************************
3014 ********************************************************************/
3016 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3018 LDAPMessage *res = NULL;
3023 status = ads_find_machine_acct(ads, &res, global_myname());
3024 if (!ADS_ERR_OK(status)) {
3025 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3030 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3031 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3035 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3036 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3040 ads_msgfree(ads, res);
3045 /********************************************************************
3046 ********************************************************************/
3048 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3050 LDAPMessage *res = NULL;
3055 status = ads_find_machine_acct(ads, &res, global_myname());
3056 if (!ADS_ERR_OK(status)) {
3057 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3062 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3063 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3067 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3068 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3072 ads_msgfree(ads, res);
3079 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3082 * Join a machine to a realm
3083 * Creates the machine account and sets the machine password
3084 * @param ads connection to ads server
3085 * @param machine name of host to add
3086 * @param org_unit Organizational unit to place machine in
3087 * @return status of join
3089 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3090 uint32 account_type, const char *org_unit)
3093 LDAPMessage *res = NULL;
3096 /* machine name must be lowercase */
3097 machine = SMB_STRDUP(machine_name);
3098 strlower_m(machine);
3101 status = ads_find_machine_acct(ads, (void **)&res, machine);
3102 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3103 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3104 status = ads_leave_realm(ads, machine);
3105 if (!ADS_ERR_OK(status)) {
3106 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3107 machine, ads->config.realm));
3112 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3113 if (!ADS_ERR_OK(status)) {
3114 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3119 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3120 if (!ADS_ERR_OK(status)) {
3121 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3127 ads_msgfree(ads, res);
3134 * Delete a machine from the realm
3135 * @param ads connection to ads server
3136 * @param hostname Machine to remove
3137 * @return status of delete
3139 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3144 char *hostnameDN, *host;
3146 LDAPControl ldap_control;
3147 LDAPControl * pldap_control[2] = {NULL, NULL};
3149 pldap_control[0] = &ldap_control;
3150 memset(&ldap_control, 0, sizeof(LDAPControl));
3151 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3153 /* hostname must be lowercase */
3154 host = SMB_STRDUP(hostname);
3157 status = ads_find_machine_acct(ads, &res, host);
3158 if (!ADS_ERR_OK(status)) {
3159 DEBUG(0, ("Host account for %s does not exist.\n", host));
3164 msg = ads_first_entry(ads, res);
3167 return ADS_ERROR_SYSTEM(ENOENT);
3170 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3172 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3174 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3176 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3179 if (rc != LDAP_SUCCESS) {
3180 const char *attrs[] = { "cn", NULL };
3181 LDAPMessage *msg_sub;
3183 /* we only search with scope ONE, we do not expect any further
3184 * objects to be created deeper */
3186 status = ads_do_search_retry(ads, hostnameDN,
3187 LDAP_SCOPE_ONELEVEL,
3188 "(objectclass=*)", attrs, &res);
3190 if (!ADS_ERR_OK(status)) {
3192 ads_memfree(ads, hostnameDN);
3196 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3197 msg_sub = ads_next_entry(ads, msg_sub)) {
3201 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3203 ads_memfree(ads, hostnameDN);
3204 return ADS_ERROR(LDAP_NO_MEMORY);
3207 status = ads_del_dn(ads, dn);
3208 if (!ADS_ERR_OK(status)) {
3209 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3211 ads_memfree(ads, dn);
3212 ads_memfree(ads, hostnameDN);
3216 ads_memfree(ads, dn);
3219 /* there should be no subordinate objects anymore */
3220 status = ads_do_search_retry(ads, hostnameDN,
3221 LDAP_SCOPE_ONELEVEL,
3222 "(objectclass=*)", attrs, &res);
3224 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3226 ads_memfree(ads, hostnameDN);
3230 /* delete hostnameDN now */
3231 status = ads_del_dn(ads, hostnameDN);
3232 if (!ADS_ERR_OK(status)) {
3234 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3235 ads_memfree(ads, hostnameDN);
3240 ads_memfree(ads, hostnameDN);
3242 status = ads_find_machine_acct(ads, &res, host);
3243 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3244 DEBUG(3, ("Failed to remove host account.\n"));
3254 * pull all token-sids from an LDAP dn
3255 * @param ads connection to ads server
3256 * @param mem_ctx TALLOC_CTX for allocating sid array
3257 * @param dn of LDAP object
3258 * @param user_sid pointer to DOM_SID (objectSid)
3259 * @param primary_group_sid pointer to DOM_SID (self composed)
3260 * @param sids pointer to sid array to allocate
3261 * @param num_sids counter of SIDs pulled
3262 * @return status of token query
3264 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3265 TALLOC_CTX *mem_ctx,
3268 DOM_SID *primary_group_sid,
3273 LDAPMessage *res = NULL;
3275 size_t tmp_num_sids;
3277 DOM_SID tmp_user_sid;
3278 DOM_SID tmp_primary_group_sid;
3280 const char *attrs[] = {
3287 status = ads_search_retry_dn(ads, &res, dn, attrs);
3288 if (!ADS_ERR_OK(status)) {
3292 count = ads_count_replies(ads, res);
3294 ads_msgfree(ads, res);
3295 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3298 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3299 ads_msgfree(ads, res);
3300 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3303 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3304 ads_msgfree(ads, res);
3305 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3309 /* hack to compose the primary group sid without knowing the
3315 sid_copy(&domsid, &tmp_user_sid);
3317 if (!sid_split_rid(&domsid, &dummy_rid)) {
3318 ads_msgfree(ads, res);
3319 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3322 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3323 ads_msgfree(ads, res);
3324 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3328 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3330 if (tmp_num_sids == 0 || !tmp_sids) {
3331 ads_msgfree(ads, res);
3332 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3336 *num_sids = tmp_num_sids;
3344 *user_sid = tmp_user_sid;
3347 if (primary_group_sid) {
3348 *primary_group_sid = tmp_primary_group_sid;
3351 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3353 ads_msgfree(ads, res);
3354 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3358 * Find a sAMAccoutName in LDAP
3359 * @param ads connection to ads server
3360 * @param mem_ctx TALLOC_CTX for allocating sid array
3361 * @param samaccountname to search
3362 * @param uac_ret uint32 pointer userAccountControl attribute value
3363 * @param dn_ret pointer to dn
3364 * @return status of token query
3366 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3367 TALLOC_CTX *mem_ctx,
3368 const char *samaccountname,
3370 const char **dn_ret)
3373 const char *attrs[] = { "userAccountControl", NULL };
3375 LDAPMessage *res = NULL;
3379 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3381 if (filter == NULL) {
3385 status = ads_do_search_all(ads, ads->config.bind_path,
3387 filter, attrs, &res);
3389 if (!ADS_ERR_OK(status)) {
3393 if (ads_count_replies(ads, res) != 1) {
3394 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3398 dn = ads_get_dn(ads, res);
3400 status = ADS_ERROR(LDAP_NO_MEMORY);
3404 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3405 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3414 *dn_ret = talloc_strdup(mem_ctx, dn);
3416 status = ADS_ERROR(LDAP_NO_MEMORY);
3421 ads_memfree(ads, dn);
3422 ads_msgfree(ads, res);
3428 * find our configuration path
3429 * @param ads connection to ads server
3430 * @param mem_ctx Pointer to talloc context
3431 * @param config_path Pointer to the config path
3432 * @return status of search
3434 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3435 TALLOC_CTX *mem_ctx,
3439 LDAPMessage *res = NULL;
3440 const char *config_context = NULL;
3441 const char *attrs[] = { "configurationNamingContext", NULL };
3443 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3444 "(objectclass=*)", attrs, &res);
3445 if (!ADS_ERR_OK(status)) {
3449 config_context = ads_pull_string(ads, mem_ctx, res,
3450 "configurationNamingContext");
3451 ads_msgfree(ads, res);
3452 if (!config_context) {
3453 return ADS_ERROR(LDAP_NO_MEMORY);
3457 *config_path = talloc_strdup(mem_ctx, config_context);
3458 if (!*config_path) {
3459 return ADS_ERROR(LDAP_NO_MEMORY);
3463 return ADS_ERROR(LDAP_SUCCESS);
3467 * find the displayName of an extended right
3468 * @param ads connection to ads server
3469 * @param config_path The config path
3470 * @param mem_ctx Pointer to talloc context
3471 * @param GUID struct of the rightsGUID
3472 * @return status of search
3474 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3475 const char *config_path,
3476 TALLOC_CTX *mem_ctx,
3477 const struct GUID *rights_guid)
3480 LDAPMessage *res = NULL;
3482 const char *attrs[] = { "displayName", NULL };
3483 const char *result = NULL;
3486 if (!ads || !mem_ctx || !rights_guid) {
3490 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3491 smb_uuid_string(mem_ctx, *rights_guid));
3496 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3501 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3503 if (!ADS_ERR_OK(rc)) {
3507 if (ads_count_replies(ads, res) != 1) {
3511 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3514 ads_msgfree(ads, res);