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);
456 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
458 /* cache the successful connection for workgroup and realm */
459 if (ads_closest_dc(ads)) {
460 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
461 saf_store( ads->server.workgroup, addr);
462 saf_store( ads->server.realm, addr);
465 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
467 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
468 if (!ADS_ERR_OK(status)) {
472 /* fill in the current time and offsets */
474 status = ads_current_time( ads );
475 if ( !ADS_ERR_OK(status) ) {
479 /* Now do the bind */
481 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
482 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, NULL, NULL));
485 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
486 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
489 return ads_sasl_bind(ads);
493 * Disconnect the LDAP server
494 * @param ads Pointer to an existing ADS_STRUCT
496 void ads_disconnect(ADS_STRUCT *ads)
499 ldap_unbind(ads->ldap.ld);
502 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
503 ads->ldap.wrap_ops->disconnect(ads);
505 if (ads->ldap.mem_ctx) {
506 talloc_free(ads->ldap.mem_ctx);
508 ZERO_STRUCT(ads->ldap);
512 Duplicate a struct berval into talloc'ed memory
514 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
516 struct berval *value;
518 if (!in_val) return NULL;
520 value = TALLOC_ZERO_P(ctx, struct berval);
523 if (in_val->bv_len == 0) return value;
525 value->bv_len = in_val->bv_len;
526 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
532 Make a values list out of an array of (struct berval *)
534 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
535 const struct berval **in_vals)
537 struct berval **values;
540 if (!in_vals) return NULL;
541 for (i=0; in_vals[i]; i++)
543 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
544 if (!values) return NULL;
546 for (i=0; in_vals[i]; i++) {
547 values[i] = dup_berval(ctx, in_vals[i]);
553 UTF8-encode a values list out of an array of (char *)
555 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
560 if (!in_vals) return NULL;
561 for (i=0; in_vals[i]; i++)
563 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
564 if (!values) return NULL;
566 for (i=0; in_vals[i]; i++) {
567 push_utf8_talloc(ctx, &values[i], in_vals[i]);
573 Pull a (char *) array out of a UTF8-encoded values list
575 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
580 if (!in_vals) return NULL;
581 for (i=0; in_vals[i]; i++)
583 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
584 if (!values) return NULL;
586 for (i=0; in_vals[i]; i++) {
587 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
593 * Do a search with paged results. cookie must be null on the first
594 * call, and then returned on each subsequent call. It will be null
595 * again when the entire search is complete
596 * @param ads connection to ads server
597 * @param bind_path Base dn for the search
598 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
599 * @param expr Search expression - specified in local charset
600 * @param attrs Attributes to retrieve - specified in utf8 or ascii
601 * @param res ** which will contain results - free res* with ads_msgfree()
602 * @param count Number of entries retrieved on this page
603 * @param cookie The paged results cookie to be returned on subsequent calls
604 * @return status of search
606 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
607 const char *bind_path,
608 int scope, const char *expr,
609 const char **attrs, void *args,
611 int *count, struct berval **cookie)
614 char *utf8_expr, *utf8_path, **search_attrs;
615 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
616 BerElement *cookie_be = NULL;
617 struct berval *cookie_bv= NULL;
618 BerElement *ext_be = NULL;
619 struct berval *ext_bv= NULL;
622 ads_control *external_control = (ads_control *) args;
626 if (!(ctx = talloc_init("ads_do_paged_search_args")))
627 return ADS_ERROR(LDAP_NO_MEMORY);
629 /* 0 means the conversion worked but the result was empty
630 so we only fail if it's -1. In any case, it always
631 at least nulls out the dest */
632 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
633 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
638 if (!attrs || !(*attrs))
641 /* This would be the utf8-encoded version...*/
642 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
643 if (!(str_list_copy(&search_attrs, attrs))) {
649 /* Paged results only available on ldap v3 or later */
650 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
651 if (version < LDAP_VERSION3) {
652 rc = LDAP_NOT_SUPPORTED;
656 cookie_be = ber_alloc_t(LBER_USE_DER);
658 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
659 ber_bvfree(*cookie); /* don't need it from last time */
662 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
664 ber_flatten(cookie_be, &cookie_bv);
665 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
666 PagedResults.ldctl_iscritical = (char) 1;
667 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
668 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
670 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
671 NoReferrals.ldctl_iscritical = (char) 0;
672 NoReferrals.ldctl_value.bv_len = 0;
673 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
675 if (external_control &&
676 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
677 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
679 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
680 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
682 /* win2k does not accept a ldctl_value beeing passed in */
684 if (external_control->val != 0) {
686 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
691 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
695 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
700 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
701 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
704 ExternalCtrl.ldctl_value.bv_len = 0;
705 ExternalCtrl.ldctl_value.bv_val = NULL;
708 controls[0] = &NoReferrals;
709 controls[1] = &PagedResults;
710 controls[2] = &ExternalCtrl;
714 controls[0] = &NoReferrals;
715 controls[1] = &PagedResults;
719 /* we need to disable referrals as the openldap libs don't
720 handle them and paged results at the same time. Using them
721 together results in the result record containing the server
722 page control being removed from the result list (tridge/jmcd)
724 leaving this in despite the control that says don't generate
725 referrals, in case the server doesn't support it (jmcd)
727 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
729 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
730 search_attrs, 0, controls,
732 (LDAPMessage **)res);
734 ber_free(cookie_be, 1);
735 ber_bvfree(cookie_bv);
738 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
739 ldap_err2string(rc)));
743 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
744 NULL, &rcontrols, 0);
750 for (i=0; rcontrols[i]; i++) {
751 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
752 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
753 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
755 /* the berval is the cookie, but must be freed when
757 if (cookie_bv->bv_len) /* still more to do */
758 *cookie=ber_bvdup(cookie_bv);
761 ber_bvfree(cookie_bv);
762 ber_free(cookie_be, 1);
766 ldap_controls_free(rcontrols);
779 /* if/when we decide to utf8-encode attrs, take out this next line */
780 str_list_free(&search_attrs);
782 return ADS_ERROR(rc);
785 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
786 int scope, const char *expr,
787 const char **attrs, LDAPMessage **res,
788 int *count, struct berval **cookie)
790 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
795 * Get all results for a search. This uses ads_do_paged_search() to return
796 * all entries in a large search.
797 * @param ads connection to ads server
798 * @param bind_path Base dn for the search
799 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
800 * @param expr Search expression
801 * @param attrs Attributes to retrieve
802 * @param res ** which will contain results - free res* with ads_msgfree()
803 * @return status of search
805 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
806 int scope, const char *expr,
807 const char **attrs, void *args,
810 struct berval *cookie = NULL;
815 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
818 if (!ADS_ERR_OK(status))
821 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
823 LDAPMessage *res2 = NULL;
825 LDAPMessage *msg, *next;
827 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
828 attrs, args, &res2, &count, &cookie);
830 if (!ADS_ERR_OK(status2)) break;
832 /* this relies on the way that ldap_add_result_entry() works internally. I hope
833 that this works on all ldap libs, but I have only tested with openldap */
834 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
835 next = ads_next_entry(ads, msg);
836 ldap_add_result_entry((LDAPMessage **)res, msg);
838 /* note that we do not free res2, as the memory is now
839 part of the main returned list */
842 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
843 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
849 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
850 int scope, const char *expr,
851 const char **attrs, LDAPMessage **res)
853 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
856 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
857 int scope, const char *expr,
858 const char **attrs, uint32 sd_flags,
863 args.control = ADS_SD_FLAGS_OID;
865 args.critical = True;
867 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
872 * Run a function on all results for a search. Uses ads_do_paged_search() and
873 * runs the function as each page is returned, using ads_process_results()
874 * @param ads connection to ads server
875 * @param bind_path Base dn for the search
876 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
877 * @param expr Search expression - specified in local charset
878 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
879 * @param fn Function which takes attr name, values list, and data_area
880 * @param data_area Pointer which is passed to function on each call
881 * @return status of search
883 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
884 int scope, const char *expr, const char **attrs,
885 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
888 struct berval *cookie = NULL;
893 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
896 if (!ADS_ERR_OK(status)) return status;
898 ads_process_results(ads, res, fn, data_area);
899 ads_msgfree(ads, res);
902 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
903 &res, &count, &cookie);
905 if (!ADS_ERR_OK(status)) break;
907 ads_process_results(ads, res, fn, data_area);
908 ads_msgfree(ads, res);
915 * Do a search with a timeout.
916 * @param ads connection to ads server
917 * @param bind_path Base dn for the search
918 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
919 * @param expr Search expression
920 * @param attrs Attributes to retrieve
921 * @param res ** which will contain results - free res* with ads_msgfree()
922 * @return status of search
924 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
926 const char **attrs, LDAPMessage **res)
929 char *utf8_expr, *utf8_path, **search_attrs = NULL;
933 if (!(ctx = talloc_init("ads_do_search"))) {
934 DEBUG(1,("ads_do_search: talloc_init() failed!"));
935 return ADS_ERROR(LDAP_NO_MEMORY);
938 /* 0 means the conversion worked but the result was empty
939 so we only fail if it's negative. In any case, it always
940 at least nulls out the dest */
941 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
942 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
943 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
948 if (!attrs || !(*attrs))
951 /* This would be the utf8-encoded version...*/
952 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
953 if (!(str_list_copy(&search_attrs, attrs)))
955 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
961 /* see the note in ads_do_paged_search - we *must* disable referrals */
962 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
964 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
965 search_attrs, 0, NULL, NULL,
967 (LDAPMessage **)res);
969 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
970 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
976 /* if/when we decide to utf8-encode attrs, take out this next line */
977 str_list_free(&search_attrs);
978 return ADS_ERROR(rc);
981 * Do a general ADS search
982 * @param ads connection to ads server
983 * @param res ** which will contain results - free res* with ads_msgfree()
984 * @param expr Search expression
985 * @param attrs Attributes to retrieve
986 * @return status of search
988 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
989 const char *expr, const char **attrs)
991 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
996 * Do a search on a specific DistinguishedName
997 * @param ads connection to ads server
998 * @param res ** which will contain results - free res* with ads_msgfree()
999 * @param dn DistinguishName to search
1000 * @param attrs Attributes to retrieve
1001 * @return status of search
1003 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1004 const char *dn, const char **attrs)
1006 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1011 * Free up memory from a ads_search
1012 * @param ads connection to ads server
1013 * @param msg Search results to free
1015 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1022 * Free up memory from various ads requests
1023 * @param ads connection to ads server
1024 * @param mem Area to free
1026 void ads_memfree(ADS_STRUCT *ads, void *mem)
1032 * Get a dn from search results
1033 * @param ads connection to ads server
1034 * @param msg Search result
1037 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1039 char *utf8_dn, *unix_dn;
1041 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1044 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1048 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1049 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1053 ldap_memfree(utf8_dn);
1058 * Get the parent from a dn
1059 * @param dn the dn to return the parent from
1060 * @return parent dn string
1062 char *ads_parent_dn(const char *dn)
1070 p = strchr(dn, ',');
1080 * Find a machine account given a hostname
1081 * @param ads connection to ads server
1082 * @param res ** which will contain results - free res* with ads_msgfree()
1083 * @param host Hostname to search for
1084 * @return status of search
1086 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1087 const char *machine)
1091 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1095 /* the easiest way to find a machine account anywhere in the tree
1096 is to look for hostname$ */
1097 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1098 DEBUG(1, ("asprintf failed!\n"));
1099 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1102 status = ads_search(ads, res, expr, attrs);
1108 * Initialize a list of mods to be used in a modify request
1109 * @param ctx An initialized TALLOC_CTX
1110 * @return allocated ADS_MODLIST
1112 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1114 #define ADS_MODLIST_ALLOC_SIZE 10
1117 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1118 /* -1 is safety to make sure we don't go over the end.
1119 need to reset it to NULL before doing ldap modify */
1120 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1122 return (ADS_MODLIST)mods;
1127 add an attribute to the list, with values list already constructed
1129 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1130 int mod_op, const char *name,
1131 const void *_invals)
1133 const void **invals = (const void **)_invals;
1135 LDAPMod **modlist = (LDAPMod **) *mods;
1136 struct berval **ber_values = NULL;
1137 char **char_values = NULL;
1140 mod_op = LDAP_MOD_DELETE;
1142 if (mod_op & LDAP_MOD_BVALUES)
1143 ber_values = ads_dup_values(ctx,
1144 (const struct berval **)invals);
1146 char_values = ads_push_strvals(ctx,
1147 (const char **) invals);
1150 /* find the first empty slot */
1151 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1153 if (modlist[curmod] == (LDAPMod *) -1) {
1154 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1155 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1156 return ADS_ERROR(LDAP_NO_MEMORY);
1157 memset(&modlist[curmod], 0,
1158 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1159 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1160 *mods = (ADS_MODLIST)modlist;
1163 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1164 return ADS_ERROR(LDAP_NO_MEMORY);
1165 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1166 if (mod_op & LDAP_MOD_BVALUES) {
1167 modlist[curmod]->mod_bvalues = ber_values;
1168 } else if (mod_op & LDAP_MOD_DELETE) {
1169 modlist[curmod]->mod_values = NULL;
1171 modlist[curmod]->mod_values = char_values;
1174 modlist[curmod]->mod_op = mod_op;
1175 return ADS_ERROR(LDAP_SUCCESS);
1179 * Add a single string value to a mod list
1180 * @param ctx An initialized TALLOC_CTX
1181 * @param mods An initialized ADS_MODLIST
1182 * @param name The attribute name to add
1183 * @param val The value to add - NULL means DELETE
1184 * @return ADS STATUS indicating success of add
1186 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1187 const char *name, const char *val)
1189 const char *values[2];
1195 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1196 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1200 * Add an array of string values to a mod list
1201 * @param ctx An initialized TALLOC_CTX
1202 * @param mods An initialized ADS_MODLIST
1203 * @param name The attribute name to add
1204 * @param vals The array of string values to add - NULL means DELETE
1205 * @return ADS STATUS indicating success of add
1207 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1208 const char *name, const char **vals)
1211 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1212 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1213 name, (const void **) vals);
1218 * Add a single ber-encoded value to a mod list
1219 * @param ctx An initialized TALLOC_CTX
1220 * @param mods An initialized ADS_MODLIST
1221 * @param name The attribute name to add
1222 * @param val The value to add - NULL means DELETE
1223 * @return ADS STATUS indicating success of add
1225 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1226 const char *name, const struct berval *val)
1228 const struct berval *values[2];
1233 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1234 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1235 name, (const void **) values);
1240 * Perform an ldap modify
1241 * @param ads connection to ads server
1242 * @param mod_dn DistinguishedName to modify
1243 * @param mods list of modifications to perform
1244 * @return status of modify
1246 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1249 char *utf8_dn = NULL;
1251 this control is needed to modify that contains a currently
1252 non-existent attribute (but allowable for the object) to run
1254 LDAPControl PermitModify = {
1255 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1258 LDAPControl *controls[2];
1260 controls[0] = &PermitModify;
1263 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1264 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1267 /* find the end of the list, marked by NULL or -1 */
1268 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1269 /* make sure the end of the list is NULL */
1271 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1272 (LDAPMod **) mods, controls, NULL);
1274 return ADS_ERROR(ret);
1278 * Perform an ldap add
1279 * @param ads connection to ads server
1280 * @param new_dn DistinguishedName to add
1281 * @param mods list of attributes and values for DN
1282 * @return status of add
1284 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1287 char *utf8_dn = NULL;
1289 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1290 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1291 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1294 /* find the end of the list, marked by NULL or -1 */
1295 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1296 /* make sure the end of the list is NULL */
1299 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1301 return ADS_ERROR(ret);
1305 * Delete a DistinguishedName
1306 * @param ads connection to ads server
1307 * @param new_dn DistinguishedName to delete
1308 * @return status of delete
1310 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1313 char *utf8_dn = NULL;
1314 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1315 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1316 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1319 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1321 return ADS_ERROR(ret);
1325 * Build an org unit string
1326 * if org unit is Computers or blank then assume a container, otherwise
1327 * assume a / separated list of organisational units.
1328 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1329 * @param ads connection to ads server
1330 * @param org_unit Organizational unit
1331 * @return org unit string - caller must free
1333 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1337 if (!org_unit || !*org_unit) {
1339 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1341 /* samba4 might not yet respond to a wellknownobject-query */
1342 return ret ? ret : SMB_STRDUP("cn=Computers");
1345 if (strequal(org_unit, "Computers")) {
1346 return SMB_STRDUP("cn=Computers");
1349 /* jmcd: removed "\\" from the separation chars, because it is
1350 needed as an escape for chars like '#' which are valid in an
1352 return ads_build_path(org_unit, "/", "ou=", 1);
1356 * Get a org unit string for a well-known GUID
1357 * @param ads connection to ads server
1358 * @param wknguid Well known GUID
1359 * @return org unit string - caller must free
1361 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1364 LDAPMessage *res = NULL;
1365 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1366 **bind_dn_exp = NULL;
1367 const char *attrs[] = {"distinguishedName", NULL};
1368 int new_ln, wkn_ln, bind_ln, i;
1370 if (wknguid == NULL) {
1374 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1375 DEBUG(1, ("asprintf failed!\n"));
1379 status = ads_search_dn(ads, &res, base, attrs);
1380 if (!ADS_ERR_OK(status)) {
1381 DEBUG(1,("Failed while searching for: %s\n", base));
1385 if (ads_count_replies(ads, res) != 1) {
1389 /* substitute the bind-path from the well-known-guid-search result */
1390 wkn_dn = ads_get_dn(ads, res);
1395 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1400 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1405 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1407 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1410 new_ln = wkn_ln - bind_ln;
1412 ret = SMB_STRDUP(wkn_dn_exp[0]);
1417 for (i=1; i < new_ln; i++) {
1420 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1426 ret = SMB_STRDUP(s);
1435 ads_msgfree(ads, res);
1436 ads_memfree(ads, wkn_dn);
1438 ldap_value_free(wkn_dn_exp);
1441 ldap_value_free(bind_dn_exp);
1448 * Adds (appends) an item to an attribute array, rather then
1449 * replacing the whole list
1450 * @param ctx An initialized TALLOC_CTX
1451 * @param mods An initialized ADS_MODLIST
1452 * @param name name of the ldap attribute to append to
1453 * @param vals an array of values to add
1454 * @return status of addition
1457 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1458 const char *name, const char **vals)
1460 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1461 (const void *) vals);
1465 * Determines the computer account's current KVNO via an LDAP lookup
1466 * @param ads An initialized ADS_STRUCT
1467 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1468 * @return the kvno for the computer account, or -1 in case of a failure.
1471 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1473 LDAPMessage *res = NULL;
1474 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1476 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1477 char *dn_string = NULL;
1478 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1480 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1481 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1484 ret = ads_search(ads, &res, filter, attrs);
1486 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1487 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1488 ads_msgfree(ads, res);
1492 dn_string = ads_get_dn(ads, res);
1494 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1495 ads_msgfree(ads, res);
1498 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1499 ads_memfree(ads, dn_string);
1501 /* ---------------------------------------------------------
1502 * 0 is returned as a default KVNO from this point on...
1503 * This is done because Windows 2000 does not support key
1504 * version numbers. Chances are that a failure in the next
1505 * step is simply due to Windows 2000 being used for a
1506 * domain controller. */
1509 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1510 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1511 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1512 ads_msgfree(ads, res);
1517 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1518 ads_msgfree(ads, res);
1523 * This clears out all registered spn's for a given hostname
1524 * @param ads An initilaized ADS_STRUCT
1525 * @param machine_name the NetBIOS name of the computer.
1526 * @return 0 upon success, non-zero otherwise.
1529 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1532 LDAPMessage *res = NULL;
1534 const char *servicePrincipalName[1] = {NULL};
1535 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1536 char *dn_string = NULL;
1538 ret = ads_find_machine_acct(ads, &res, machine_name);
1539 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1540 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1541 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1542 ads_msgfree(ads, res);
1543 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1546 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1547 ctx = talloc_init("ads_clear_service_principal_names");
1549 ads_msgfree(ads, res);
1550 return ADS_ERROR(LDAP_NO_MEMORY);
1553 if (!(mods = ads_init_mods(ctx))) {
1554 talloc_destroy(ctx);
1555 ads_msgfree(ads, res);
1556 return ADS_ERROR(LDAP_NO_MEMORY);
1558 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1559 if (!ADS_ERR_OK(ret)) {
1560 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1561 ads_msgfree(ads, res);
1562 talloc_destroy(ctx);
1565 dn_string = ads_get_dn(ads, res);
1567 talloc_destroy(ctx);
1568 ads_msgfree(ads, res);
1569 return ADS_ERROR(LDAP_NO_MEMORY);
1571 ret = ads_gen_mod(ads, dn_string, mods);
1572 ads_memfree(ads,dn_string);
1573 if (!ADS_ERR_OK(ret)) {
1574 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1576 ads_msgfree(ads, res);
1577 talloc_destroy(ctx);
1581 ads_msgfree(ads, res);
1582 talloc_destroy(ctx);
1587 * This adds a service principal name to an existing computer account
1588 * (found by hostname) in AD.
1589 * @param ads An initialized ADS_STRUCT
1590 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1591 * @param my_fqdn The fully qualified DNS name of the machine
1592 * @param spn A string of the service principal to add, i.e. 'host'
1593 * @return 0 upon sucess, or non-zero if a failure occurs
1596 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1597 const char *my_fqdn, const char *spn)
1601 LDAPMessage *res = NULL;
1604 char *dn_string = NULL;
1605 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1607 ret = ads_find_machine_acct(ads, &res, machine_name);
1608 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1609 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1611 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1612 spn, machine_name, ads->config.realm));
1613 ads_msgfree(ads, res);
1614 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1617 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1618 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1619 ads_msgfree(ads, res);
1620 return ADS_ERROR(LDAP_NO_MEMORY);
1623 /* add short name spn */
1625 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1626 talloc_destroy(ctx);
1627 ads_msgfree(ads, res);
1628 return ADS_ERROR(LDAP_NO_MEMORY);
1631 strlower_m(&psp1[strlen(spn)]);
1632 servicePrincipalName[0] = psp1;
1634 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1635 psp1, machine_name));
1638 /* add fully qualified spn */
1640 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1641 ret = ADS_ERROR(LDAP_NO_MEMORY);
1645 strlower_m(&psp2[strlen(spn)]);
1646 servicePrincipalName[1] = psp2;
1648 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1649 psp2, machine_name));
1651 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1652 ret = ADS_ERROR(LDAP_NO_MEMORY);
1656 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1657 if (!ADS_ERR_OK(ret)) {
1658 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1662 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1663 ret = ADS_ERROR(LDAP_NO_MEMORY);
1667 ret = ads_gen_mod(ads, dn_string, mods);
1668 ads_memfree(ads,dn_string);
1669 if (!ADS_ERR_OK(ret)) {
1670 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1676 ads_msgfree(ads, res);
1681 * adds a machine account to the ADS server
1682 * @param ads An intialized ADS_STRUCT
1683 * @param machine_name - the NetBIOS machine name of this account.
1684 * @param account_type A number indicating the type of account to create
1685 * @param org_unit The LDAP path in which to place this account
1686 * @return 0 upon success, or non-zero otherwise
1689 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1690 const char *org_unit)
1693 char *samAccountName, *controlstr;
1696 char *machine_escaped = NULL;
1698 const char *objectClass[] = {"top", "person", "organizationalPerson",
1699 "user", "computer", NULL};
1700 LDAPMessage *res = NULL;
1701 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1702 UF_DONT_EXPIRE_PASSWD |\
1703 UF_ACCOUNTDISABLE );
1705 if (!(ctx = talloc_init("ads_add_machine_acct")))
1706 return ADS_ERROR(LDAP_NO_MEMORY);
1708 ret = ADS_ERROR(LDAP_NO_MEMORY);
1710 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1711 if (!machine_escaped) {
1715 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1716 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1718 if ( !new_dn || !samAccountName ) {
1722 #ifndef ENCTYPE_ARCFOUR_HMAC
1723 acct_control |= UF_USE_DES_KEY_ONLY;
1726 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1730 if (!(mods = ads_init_mods(ctx))) {
1734 ads_mod_str(ctx, &mods, "cn", machine_name);
1735 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1736 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1737 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1739 ret = ads_gen_add(ads, new_dn, mods);
1742 SAFE_FREE(machine_escaped);
1743 ads_msgfree(ads, res);
1744 talloc_destroy(ctx);
1750 * move a machine account to another OU on the ADS server
1751 * @param ads - An intialized ADS_STRUCT
1752 * @param machine_name - the NetBIOS machine name of this account.
1753 * @param org_unit - The LDAP path in which to place this account
1754 * @param moved - whether we moved the machine account (optional)
1755 * @return 0 upon success, or non-zero otherwise
1758 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1759 const char *org_unit, bool *moved)
1763 LDAPMessage *res = NULL;
1764 char *filter = NULL;
1765 char *computer_dn = NULL;
1767 char *computer_rdn = NULL;
1768 bool need_move = False;
1770 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1771 rc = ADS_ERROR(LDAP_NO_MEMORY);
1775 /* Find pre-existing machine */
1776 rc = ads_search(ads, &res, filter, NULL);
1777 if (!ADS_ERR_OK(rc)) {
1781 computer_dn = ads_get_dn(ads, res);
1783 rc = ADS_ERROR(LDAP_NO_MEMORY);
1787 parent_dn = ads_parent_dn(computer_dn);
1788 if (strequal(parent_dn, org_unit)) {
1794 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1795 rc = ADS_ERROR(LDAP_NO_MEMORY);
1799 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1800 org_unit, 1, NULL, NULL);
1801 rc = ADS_ERROR(ldap_status);
1804 ads_msgfree(ads, res);
1806 SAFE_FREE(computer_dn);
1807 SAFE_FREE(computer_rdn);
1809 if (!ADS_ERR_OK(rc)) {
1821 dump a binary result from ldap
1823 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1826 for (i=0; values[i]; i++) {
1827 printf("%s: ", field);
1828 for (j=0; j<values[i]->bv_len; j++) {
1829 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1835 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1838 for (i=0; values[i]; i++) {
1843 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1844 smb_uuid_unpack(guid, &tmp);
1845 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1850 dump a sid result from ldap
1852 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1855 for (i=0; values[i]; i++) {
1858 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1859 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1864 dump ntSecurityDescriptor
1866 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1868 TALLOC_CTX *frame = talloc_stackframe();
1869 struct security_descriptor *psd;
1872 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1873 values[0]->bv_len, &psd);
1874 if (!NT_STATUS_IS_OK(status)) {
1875 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1876 nt_errstr(status)));
1882 ads_disp_sd(ads, talloc_tos(), psd);
1889 dump a string result from ldap
1891 static void dump_string(const char *field, char **values)
1894 for (i=0; values[i]; i++) {
1895 printf("%s: %s\n", field, values[i]);
1900 dump a field from LDAP on stdout
1904 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1909 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1911 {"objectGUID", False, dump_guid},
1912 {"netbootGUID", False, dump_guid},
1913 {"nTSecurityDescriptor", False, dump_sd},
1914 {"dnsRecord", False, dump_binary},
1915 {"objectSid", False, dump_sid},
1916 {"tokenGroups", False, dump_sid},
1917 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1918 {"tokengroupsGlobalandUniversal", False, dump_sid},
1919 {"mS-DS-CreatorSID", False, dump_sid},
1920 {"msExchMailboxGuid", False, dump_guid},
1925 if (!field) { /* must be end of an entry */
1930 for (i=0; handlers[i].name; i++) {
1931 if (StrCaseCmp(handlers[i].name, field) == 0) {
1932 if (!values) /* first time, indicate string or not */
1933 return handlers[i].string;
1934 handlers[i].handler(ads, field, (struct berval **) values);
1938 if (!handlers[i].name) {
1939 if (!values) /* first time, indicate string conversion */
1941 dump_string(field, (char **)values);
1947 * Dump a result from LDAP on stdout
1948 * used for debugging
1949 * @param ads connection to ads server
1950 * @param res Results to dump
1953 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1955 ads_process_results(ads, res, ads_dump_field, NULL);
1959 * Walk through results, calling a function for each entry found.
1960 * The function receives a field name, a berval * array of values,
1961 * and a data area passed through from the start. The function is
1962 * called once with null for field and values at the end of each
1964 * @param ads connection to ads server
1965 * @param res Results to process
1966 * @param fn Function for processing each result
1967 * @param data_area user-defined area to pass to function
1969 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1970 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1976 if (!(ctx = talloc_init("ads_process_results")))
1979 for (msg = ads_first_entry(ads, res); msg;
1980 msg = ads_next_entry(ads, msg)) {
1984 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1985 (LDAPMessage *)msg,&b);
1987 utf8_field=ldap_next_attribute(ads->ldap.ld,
1988 (LDAPMessage *)msg,b)) {
1989 struct berval **ber_vals;
1990 char **str_vals, **utf8_vals;
1994 pull_utf8_talloc(ctx, &field, utf8_field);
1995 string = fn(ads, field, NULL, data_area);
1998 utf8_vals = ldap_get_values(ads->ldap.ld,
1999 (LDAPMessage *)msg, field);
2000 str_vals = ads_pull_strvals(ctx,
2001 (const char **) utf8_vals);
2002 fn(ads, field, (void **) str_vals, data_area);
2003 ldap_value_free(utf8_vals);
2005 ber_vals = ldap_get_values_len(ads->ldap.ld,
2006 (LDAPMessage *)msg, field);
2007 fn(ads, field, (void **) ber_vals, data_area);
2009 ldap_value_free_len(ber_vals);
2011 ldap_memfree(utf8_field);
2014 talloc_free_children(ctx);
2015 fn(ads, NULL, NULL, data_area); /* completed an entry */
2018 talloc_destroy(ctx);
2022 * count how many replies are in a LDAPMessage
2023 * @param ads connection to ads server
2024 * @param res Results to count
2025 * @return number of replies
2027 int ads_count_replies(ADS_STRUCT *ads, void *res)
2029 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2033 * pull the first entry from a ADS result
2034 * @param ads connection to ads server
2035 * @param res Results of search
2036 * @return first entry from result
2038 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2040 return ldap_first_entry(ads->ldap.ld, res);
2044 * pull the next entry from a ADS result
2045 * @param ads connection to ads server
2046 * @param res Results of search
2047 * @return next entry from result
2049 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2051 return ldap_next_entry(ads->ldap.ld, res);
2055 * pull a single string from a ADS result
2056 * @param ads connection to ads server
2057 * @param mem_ctx TALLOC_CTX to use for allocating result string
2058 * @param msg Results of search
2059 * @param field Attribute to retrieve
2060 * @return Result string in talloc context
2062 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2070 values = ldap_get_values(ads->ldap.ld, msg, field);
2075 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2077 if (rc != (size_t)-1)
2081 ldap_value_free(values);
2086 * pull an array of strings from a ADS result
2087 * @param ads connection to ads server
2088 * @param mem_ctx TALLOC_CTX to use for allocating result string
2089 * @param msg Results of search
2090 * @param field Attribute to retrieve
2091 * @return Result strings in talloc context
2093 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2094 LDAPMessage *msg, const char *field,
2101 values = ldap_get_values(ads->ldap.ld, msg, field);
2105 *num_values = ldap_count_values(values);
2107 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2109 ldap_value_free(values);
2113 for (i=0;i<*num_values;i++) {
2114 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2115 ldap_value_free(values);
2121 ldap_value_free(values);
2126 * pull an array of strings from a ADS result
2127 * (handle large multivalue attributes with range retrieval)
2128 * @param ads connection to ads server
2129 * @param mem_ctx TALLOC_CTX to use for allocating result string
2130 * @param msg Results of search
2131 * @param field Attribute to retrieve
2132 * @param current_strings strings returned by a previous call to this function
2133 * @param next_attribute The next query should ask for this attribute
2134 * @param num_values How many values did we get this time?
2135 * @param more_values Are there more values to get?
2136 * @return Result strings in talloc context
2138 char **ads_pull_strings_range(ADS_STRUCT *ads,
2139 TALLOC_CTX *mem_ctx,
2140 LDAPMessage *msg, const char *field,
2141 char **current_strings,
2142 const char **next_attribute,
2143 size_t *num_strings,
2147 char *expected_range_attrib, *range_attr;
2148 BerElement *ptr = NULL;
2151 size_t num_new_strings;
2152 unsigned long int range_start;
2153 unsigned long int range_end;
2155 /* we might have been given the whole lot anyway */
2156 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2157 *more_strings = False;
2161 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2163 /* look for Range result */
2164 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2166 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2167 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2168 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2176 /* nothing here - this field is just empty */
2177 *more_strings = False;
2181 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2182 &range_start, &range_end) == 2) {
2183 *more_strings = True;
2185 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2186 &range_start) == 1) {
2187 *more_strings = False;
2189 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2191 ldap_memfree(range_attr);
2192 *more_strings = False;
2197 if ((*num_strings) != range_start) {
2198 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2199 " - aborting range retreival\n",
2200 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2201 ldap_memfree(range_attr);
2202 *more_strings = False;
2206 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2208 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2209 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2210 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2211 range_attr, (unsigned long int)range_end - range_start + 1,
2212 (unsigned long int)num_new_strings));
2213 ldap_memfree(range_attr);
2214 *more_strings = False;
2218 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2219 *num_strings + num_new_strings);
2221 if (strings == NULL) {
2222 ldap_memfree(range_attr);
2223 *more_strings = False;
2227 if (new_strings && num_new_strings) {
2228 memcpy(&strings[*num_strings], new_strings,
2229 sizeof(*new_strings) * num_new_strings);
2232 (*num_strings) += num_new_strings;
2234 if (*more_strings) {
2235 *next_attribute = talloc_asprintf(mem_ctx,
2240 if (!*next_attribute) {
2241 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2242 ldap_memfree(range_attr);
2243 *more_strings = False;
2248 ldap_memfree(range_attr);
2254 * pull a single uint32 from a ADS result
2255 * @param ads connection to ads server
2256 * @param msg Results of search
2257 * @param field Attribute to retrieve
2258 * @param v Pointer to int to store result
2259 * @return boolean inidicating success
2261 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2266 values = ldap_get_values(ads->ldap.ld, msg, field);
2270 ldap_value_free(values);
2274 *v = atoi(values[0]);
2275 ldap_value_free(values);
2280 * pull a single objectGUID from an ADS result
2281 * @param ads connection to ADS server
2282 * @param msg results of search
2283 * @param guid 37-byte area to receive text guid
2284 * @return boolean indicating success
2286 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2289 UUID_FLAT flat_guid;
2291 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2296 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2297 smb_uuid_unpack(flat_guid, guid);
2298 ldap_value_free(values);
2301 ldap_value_free(values);
2308 * pull a single DOM_SID from a ADS result
2309 * @param ads connection to ads server
2310 * @param msg Results of search
2311 * @param field Attribute to retrieve
2312 * @param sid Pointer to sid to store result
2313 * @return boolean inidicating success
2315 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2318 struct berval **values;
2321 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2327 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2329 ldap_value_free_len(values);
2334 * pull an array of DOM_SIDs from a ADS result
2335 * @param ads connection to ads server
2336 * @param mem_ctx TALLOC_CTX for allocating sid array
2337 * @param msg Results of search
2338 * @param field Attribute to retrieve
2339 * @param sids pointer to sid array to allocate
2340 * @return the count of SIDs pulled
2342 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2343 LDAPMessage *msg, const char *field, DOM_SID **sids)
2345 struct berval **values;
2349 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2354 for (i=0; values[i]; i++)
2358 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2360 ldap_value_free_len(values);
2368 for (i=0; values[i]; i++) {
2369 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2371 DEBUG(10, ("pulling SID: %s\n",
2372 sid_string_dbg(&(*sids)[count])));
2377 ldap_value_free_len(values);
2382 * pull a SEC_DESC from a ADS result
2383 * @param ads connection to ads server
2384 * @param mem_ctx TALLOC_CTX for allocating sid array
2385 * @param msg Results of search
2386 * @param field Attribute to retrieve
2387 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2388 * @return boolean inidicating success
2390 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2391 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2393 struct berval **values;
2396 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2398 if (!values) return false;
2402 status = unmarshall_sec_desc(mem_ctx,
2403 (uint8 *)values[0]->bv_val,
2404 values[0]->bv_len, sd);
2405 if (!NT_STATUS_IS_OK(status)) {
2406 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2407 nt_errstr(status)));
2412 ldap_value_free_len(values);
2417 * in order to support usernames longer than 21 characters we need to
2418 * use both the sAMAccountName and the userPrincipalName attributes
2419 * It seems that not all users have the userPrincipalName attribute set
2421 * @param ads connection to ads server
2422 * @param mem_ctx TALLOC_CTX for allocating sid array
2423 * @param msg Results of search
2424 * @return the username
2426 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2432 /* lookup_name() only works on the sAMAccountName to
2433 returning the username portion of userPrincipalName
2434 breaks winbindd_getpwnam() */
2436 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2437 if (ret && (p = strchr_m(ret, '@'))) {
2442 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2447 * find the update serial number - this is the core of the ldap cache
2448 * @param ads connection to ads server
2449 * @param ads connection to ADS server
2450 * @param usn Pointer to retrieved update serial number
2451 * @return status of search
2453 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2455 const char *attrs[] = {"highestCommittedUSN", NULL};
2459 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2460 if (!ADS_ERR_OK(status))
2463 if (ads_count_replies(ads, res) != 1) {
2464 ads_msgfree(ads, res);
2465 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2468 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2469 ads_msgfree(ads, res);
2470 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2473 ads_msgfree(ads, res);
2477 /* parse a ADS timestring - typical string is
2478 '20020917091222.0Z0' which means 09:12.22 17th September
2480 static time_t ads_parse_time(const char *str)
2486 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2487 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2488 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2497 /********************************************************************
2498 ********************************************************************/
2500 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2502 const char *attrs[] = {"currentTime", NULL};
2507 ADS_STRUCT *ads_s = ads;
2509 if (!(ctx = talloc_init("ads_current_time"))) {
2510 return ADS_ERROR(LDAP_NO_MEMORY);
2513 /* establish a new ldap tcp session if necessary */
2515 if ( !ads->ldap.ld ) {
2516 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2517 ads->server.ldap_server )) == NULL )
2521 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2522 status = ads_connect( ads_s );
2523 if ( !ADS_ERR_OK(status))
2527 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2528 if (!ADS_ERR_OK(status)) {
2532 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2534 ads_msgfree(ads_s, res);
2535 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2539 /* but save the time and offset in the original ADS_STRUCT */
2541 ads->config.current_time = ads_parse_time(timestr);
2543 if (ads->config.current_time != 0) {
2544 ads->auth.time_offset = ads->config.current_time - time(NULL);
2545 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2548 ads_msgfree(ads, res);
2550 status = ADS_SUCCESS;
2553 /* free any temporary ads connections */
2554 if ( ads_s != ads ) {
2555 ads_destroy( &ads_s );
2557 talloc_destroy(ctx);
2562 /********************************************************************
2563 ********************************************************************/
2565 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2567 const char *attrs[] = {"domainFunctionality", NULL};
2570 ADS_STRUCT *ads_s = ads;
2572 *val = DS_DOMAIN_FUNCTION_2000;
2574 /* establish a new ldap tcp session if necessary */
2576 if ( !ads->ldap.ld ) {
2577 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2578 ads->server.ldap_server )) == NULL )
2582 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2583 status = ads_connect( ads_s );
2584 if ( !ADS_ERR_OK(status))
2588 /* If the attribute does not exist assume it is a Windows 2000
2589 functional domain */
2591 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2592 if (!ADS_ERR_OK(status)) {
2593 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2594 status = ADS_SUCCESS;
2599 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2600 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2602 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2605 ads_msgfree(ads, res);
2608 /* free any temporary ads connections */
2609 if ( ads_s != ads ) {
2610 ads_destroy( &ads_s );
2617 * find the domain sid for our domain
2618 * @param ads connection to ads server
2619 * @param sid Pointer to domain sid
2620 * @return status of search
2622 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2624 const char *attrs[] = {"objectSid", NULL};
2628 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2630 if (!ADS_ERR_OK(rc)) return rc;
2631 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2632 ads_msgfree(ads, res);
2633 return ADS_ERROR_SYSTEM(ENOENT);
2635 ads_msgfree(ads, res);
2641 * find our site name
2642 * @param ads connection to ads server
2643 * @param mem_ctx Pointer to talloc context
2644 * @param site_name Pointer to the sitename
2645 * @return status of search
2647 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2651 const char *dn, *service_name;
2652 const char *attrs[] = { "dsServiceName", NULL };
2654 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2655 if (!ADS_ERR_OK(status)) {
2659 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2660 if (service_name == NULL) {
2661 ads_msgfree(ads, res);
2662 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2665 ads_msgfree(ads, res);
2667 /* go up three levels */
2668 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2670 return ADS_ERROR(LDAP_NO_MEMORY);
2673 *site_name = talloc_strdup(mem_ctx, dn);
2674 if (*site_name == NULL) {
2675 return ADS_ERROR(LDAP_NO_MEMORY);
2680 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2685 * find the site dn where a machine resides
2686 * @param ads connection to ads server
2687 * @param mem_ctx Pointer to talloc context
2688 * @param computer_name name of the machine
2689 * @param site_name Pointer to the sitename
2690 * @return status of search
2692 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2696 const char *parent, *filter;
2697 char *config_context = NULL;
2700 /* shortcut a query */
2701 if (strequal(computer_name, ads->config.ldap_server_name)) {
2702 return ads_site_dn(ads, mem_ctx, site_dn);
2705 status = ads_config_path(ads, mem_ctx, &config_context);
2706 if (!ADS_ERR_OK(status)) {
2710 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2711 if (filter == NULL) {
2712 return ADS_ERROR(LDAP_NO_MEMORY);
2715 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2716 filter, NULL, &res);
2717 if (!ADS_ERR_OK(status)) {
2721 if (ads_count_replies(ads, res) != 1) {
2722 ads_msgfree(ads, res);
2723 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2726 dn = ads_get_dn(ads, res);
2728 ads_msgfree(ads, res);
2729 return ADS_ERROR(LDAP_NO_MEMORY);
2732 /* go up three levels */
2733 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2734 if (parent == NULL) {
2735 ads_msgfree(ads, res);
2736 ads_memfree(ads, dn);
2737 return ADS_ERROR(LDAP_NO_MEMORY);
2740 *site_dn = talloc_strdup(mem_ctx, parent);
2741 if (*site_dn == NULL) {
2742 ads_msgfree(ads, res);
2743 ads_memfree(ads, dn);
2744 return ADS_ERROR(LDAP_NO_MEMORY);
2747 ads_memfree(ads, dn);
2748 ads_msgfree(ads, res);
2754 * get the upn suffixes for a domain
2755 * @param ads connection to ads server
2756 * @param mem_ctx Pointer to talloc context
2757 * @param suffixes Pointer to an array of suffixes
2758 * @param num_suffixes Pointer to the number of suffixes
2759 * @return status of search
2761 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2766 char *config_context = NULL;
2767 const char *attrs[] = { "uPNSuffixes", NULL };
2769 status = ads_config_path(ads, mem_ctx, &config_context);
2770 if (!ADS_ERR_OK(status)) {
2774 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2776 return ADS_ERROR(LDAP_NO_MEMORY);
2779 status = ads_search_dn(ads, &res, base, attrs);
2780 if (!ADS_ERR_OK(status)) {
2784 if (ads_count_replies(ads, res) != 1) {
2785 ads_msgfree(ads, res);
2786 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2789 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2790 if ((*suffixes) == NULL) {
2791 ads_msgfree(ads, res);
2792 return ADS_ERROR(LDAP_NO_MEMORY);
2795 ads_msgfree(ads, res);
2801 * get the joinable ous for a domain
2802 * @param ads connection to ads server
2803 * @param mem_ctx Pointer to talloc context
2804 * @param ous Pointer to an array of ous
2805 * @param num_ous Pointer to the number of ous
2806 * @return status of search
2808 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2809 TALLOC_CTX *mem_ctx,
2814 LDAPMessage *res = NULL;
2815 LDAPMessage *msg = NULL;
2816 const char *attrs[] = { "dn", NULL };
2819 status = ads_search(ads, &res,
2820 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2822 if (!ADS_ERR_OK(status)) {
2826 count = ads_count_replies(ads, res);
2828 ads_msgfree(ads, res);
2829 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2832 for (msg = ads_first_entry(ads, res); msg;
2833 msg = ads_next_entry(ads, msg)) {
2837 dn = ads_get_dn(ads, msg);
2839 ads_msgfree(ads, res);
2840 return ADS_ERROR(LDAP_NO_MEMORY);
2843 if (!add_string_to_array(mem_ctx, dn,
2844 (const char ***)ous,
2846 ads_memfree(ads, dn);
2847 ads_msgfree(ads, res);
2848 return ADS_ERROR(LDAP_NO_MEMORY);
2851 ads_memfree(ads, dn);
2854 ads_msgfree(ads, res);
2861 * pull a DOM_SID from an extended dn string
2862 * @param mem_ctx TALLOC_CTX
2863 * @param extended_dn string
2864 * @param flags string type of extended_dn
2865 * @param sid pointer to a DOM_SID
2866 * @return boolean inidicating success
2868 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2869 const char *extended_dn,
2870 enum ads_extended_dn_flags flags,
2879 /* otherwise extended_dn gets stripped off */
2880 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2884 * ADS_EXTENDED_DN_HEX_STRING:
2885 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2887 * ADS_EXTENDED_DN_STRING (only with w2k3):
2888 <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
2891 p = strchr(dn, ';');
2896 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2900 p += strlen(";<SID=");
2909 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2913 case ADS_EXTENDED_DN_STRING:
2914 if (!string_to_sid(sid, p)) {
2918 case ADS_EXTENDED_DN_HEX_STRING: {
2922 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2927 if (!sid_parse(buf, buf_len, sid)) {
2928 DEBUG(10,("failed to parse sid\n"));
2934 DEBUG(10,("unknown extended dn format\n"));
2942 * pull an array of DOM_SIDs from a ADS result
2943 * @param ads connection to ads server
2944 * @param mem_ctx TALLOC_CTX for allocating sid array
2945 * @param msg Results of search
2946 * @param field Attribute to retrieve
2947 * @param flags string type of extended_dn
2948 * @param sids pointer to sid array to allocate
2949 * @return the count of SIDs pulled
2951 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2952 TALLOC_CTX *mem_ctx,
2955 enum ads_extended_dn_flags flags,
2962 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2963 &dn_count)) == NULL) {
2967 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2969 TALLOC_FREE(dn_strings);
2973 for (i=0; i<dn_count; i++) {
2975 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2976 flags, &(*sids)[i])) {
2978 TALLOC_FREE(dn_strings);
2983 TALLOC_FREE(dn_strings);
2988 /********************************************************************
2989 ********************************************************************/
2991 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2993 LDAPMessage *res = NULL;
2998 status = ads_find_machine_acct(ads, &res, global_myname());
2999 if (!ADS_ERR_OK(status)) {
3000 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3005 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3006 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3010 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3011 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3015 ads_msgfree(ads, res);
3020 /********************************************************************
3021 ********************************************************************/
3023 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3025 LDAPMessage *res = NULL;
3030 status = ads_find_machine_acct(ads, &res, global_myname());
3031 if (!ADS_ERR_OK(status)) {
3032 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3037 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3038 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3042 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3043 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3047 ads_msgfree(ads, res);
3052 /********************************************************************
3053 ********************************************************************/
3055 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3057 LDAPMessage *res = NULL;
3062 status = ads_find_machine_acct(ads, &res, global_myname());
3063 if (!ADS_ERR_OK(status)) {
3064 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3069 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3070 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3074 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3075 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3079 ads_msgfree(ads, res);
3086 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3089 * Join a machine to a realm
3090 * Creates the machine account and sets the machine password
3091 * @param ads connection to ads server
3092 * @param machine name of host to add
3093 * @param org_unit Organizational unit to place machine in
3094 * @return status of join
3096 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3097 uint32 account_type, const char *org_unit)
3100 LDAPMessage *res = NULL;
3103 /* machine name must be lowercase */
3104 machine = SMB_STRDUP(machine_name);
3105 strlower_m(machine);
3108 status = ads_find_machine_acct(ads, (void **)&res, machine);
3109 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3110 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3111 status = ads_leave_realm(ads, machine);
3112 if (!ADS_ERR_OK(status)) {
3113 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3114 machine, ads->config.realm));
3119 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3120 if (!ADS_ERR_OK(status)) {
3121 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3126 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3127 if (!ADS_ERR_OK(status)) {
3128 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3134 ads_msgfree(ads, res);
3141 * Delete a machine from the realm
3142 * @param ads connection to ads server
3143 * @param hostname Machine to remove
3144 * @return status of delete
3146 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3151 char *hostnameDN, *host;
3153 LDAPControl ldap_control;
3154 LDAPControl * pldap_control[2] = {NULL, NULL};
3156 pldap_control[0] = &ldap_control;
3157 memset(&ldap_control, 0, sizeof(LDAPControl));
3158 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3160 /* hostname must be lowercase */
3161 host = SMB_STRDUP(hostname);
3164 status = ads_find_machine_acct(ads, &res, host);
3165 if (!ADS_ERR_OK(status)) {
3166 DEBUG(0, ("Host account for %s does not exist.\n", host));
3171 msg = ads_first_entry(ads, res);
3174 return ADS_ERROR_SYSTEM(ENOENT);
3177 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3179 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3181 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3183 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3186 if (rc != LDAP_SUCCESS) {
3187 const char *attrs[] = { "cn", NULL };
3188 LDAPMessage *msg_sub;
3190 /* we only search with scope ONE, we do not expect any further
3191 * objects to be created deeper */
3193 status = ads_do_search_retry(ads, hostnameDN,
3194 LDAP_SCOPE_ONELEVEL,
3195 "(objectclass=*)", attrs, &res);
3197 if (!ADS_ERR_OK(status)) {
3199 ads_memfree(ads, hostnameDN);
3203 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3204 msg_sub = ads_next_entry(ads, msg_sub)) {
3208 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3210 ads_memfree(ads, hostnameDN);
3211 return ADS_ERROR(LDAP_NO_MEMORY);
3214 status = ads_del_dn(ads, dn);
3215 if (!ADS_ERR_OK(status)) {
3216 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3218 ads_memfree(ads, dn);
3219 ads_memfree(ads, hostnameDN);
3223 ads_memfree(ads, dn);
3226 /* there should be no subordinate objects anymore */
3227 status = ads_do_search_retry(ads, hostnameDN,
3228 LDAP_SCOPE_ONELEVEL,
3229 "(objectclass=*)", attrs, &res);
3231 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3233 ads_memfree(ads, hostnameDN);
3237 /* delete hostnameDN now */
3238 status = ads_del_dn(ads, hostnameDN);
3239 if (!ADS_ERR_OK(status)) {
3241 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3242 ads_memfree(ads, hostnameDN);
3247 ads_memfree(ads, hostnameDN);
3249 status = ads_find_machine_acct(ads, &res, host);
3250 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3251 DEBUG(3, ("Failed to remove host account.\n"));
3261 * pull all token-sids from an LDAP dn
3262 * @param ads connection to ads server
3263 * @param mem_ctx TALLOC_CTX for allocating sid array
3264 * @param dn of LDAP object
3265 * @param user_sid pointer to DOM_SID (objectSid)
3266 * @param primary_group_sid pointer to DOM_SID (self composed)
3267 * @param sids pointer to sid array to allocate
3268 * @param num_sids counter of SIDs pulled
3269 * @return status of token query
3271 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3272 TALLOC_CTX *mem_ctx,
3275 DOM_SID *primary_group_sid,
3280 LDAPMessage *res = NULL;
3282 size_t tmp_num_sids;
3284 DOM_SID tmp_user_sid;
3285 DOM_SID tmp_primary_group_sid;
3287 const char *attrs[] = {
3294 status = ads_search_retry_dn(ads, &res, dn, attrs);
3295 if (!ADS_ERR_OK(status)) {
3299 count = ads_count_replies(ads, res);
3301 ads_msgfree(ads, res);
3302 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3305 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3306 ads_msgfree(ads, res);
3307 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3310 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3311 ads_msgfree(ads, res);
3312 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3316 /* hack to compose the primary group sid without knowing the
3322 sid_copy(&domsid, &tmp_user_sid);
3324 if (!sid_split_rid(&domsid, &dummy_rid)) {
3325 ads_msgfree(ads, res);
3326 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3329 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3330 ads_msgfree(ads, res);
3331 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3335 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3337 if (tmp_num_sids == 0 || !tmp_sids) {
3338 ads_msgfree(ads, res);
3339 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3343 *num_sids = tmp_num_sids;
3351 *user_sid = tmp_user_sid;
3354 if (primary_group_sid) {
3355 *primary_group_sid = tmp_primary_group_sid;
3358 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3360 ads_msgfree(ads, res);
3361 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3365 * Find a sAMAccoutName in LDAP
3366 * @param ads connection to ads server
3367 * @param mem_ctx TALLOC_CTX for allocating sid array
3368 * @param samaccountname to search
3369 * @param uac_ret uint32 pointer userAccountControl attribute value
3370 * @param dn_ret pointer to dn
3371 * @return status of token query
3373 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3374 TALLOC_CTX *mem_ctx,
3375 const char *samaccountname,
3377 const char **dn_ret)
3380 const char *attrs[] = { "userAccountControl", NULL };
3382 LDAPMessage *res = NULL;
3386 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3388 if (filter == NULL) {
3392 status = ads_do_search_all(ads, ads->config.bind_path,
3394 filter, attrs, &res);
3396 if (!ADS_ERR_OK(status)) {
3400 if (ads_count_replies(ads, res) != 1) {
3401 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3405 dn = ads_get_dn(ads, res);
3407 status = ADS_ERROR(LDAP_NO_MEMORY);
3411 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3412 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3421 *dn_ret = talloc_strdup(mem_ctx, dn);
3423 status = ADS_ERROR(LDAP_NO_MEMORY);
3428 ads_memfree(ads, dn);
3429 ads_msgfree(ads, res);
3435 * find our configuration path
3436 * @param ads connection to ads server
3437 * @param mem_ctx Pointer to talloc context
3438 * @param config_path Pointer to the config path
3439 * @return status of search
3441 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3442 TALLOC_CTX *mem_ctx,
3446 LDAPMessage *res = NULL;
3447 const char *config_context = NULL;
3448 const char *attrs[] = { "configurationNamingContext", NULL };
3450 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3451 "(objectclass=*)", attrs, &res);
3452 if (!ADS_ERR_OK(status)) {
3456 config_context = ads_pull_string(ads, mem_ctx, res,
3457 "configurationNamingContext");
3458 ads_msgfree(ads, res);
3459 if (!config_context) {
3460 return ADS_ERROR(LDAP_NO_MEMORY);
3464 *config_path = talloc_strdup(mem_ctx, config_context);
3465 if (!*config_path) {
3466 return ADS_ERROR(LDAP_NO_MEMORY);
3470 return ADS_ERROR(LDAP_SUCCESS);
3474 * find the displayName of an extended right
3475 * @param ads connection to ads server
3476 * @param config_path The config path
3477 * @param mem_ctx Pointer to talloc context
3478 * @param GUID struct of the rightsGUID
3479 * @return status of search
3481 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3482 const char *config_path,
3483 TALLOC_CTX *mem_ctx,
3484 const struct GUID *rights_guid)
3487 LDAPMessage *res = NULL;
3489 const char *attrs[] = { "displayName", NULL };
3490 const char *result = NULL;
3493 if (!ads || !mem_ctx || !rights_guid) {
3497 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3498 smb_uuid_string(mem_ctx, *rights_guid));
3503 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3508 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3510 if (!ADS_ERR_OK(rc)) {
3514 if (ads_count_replies(ads, res) != 1) {
3518 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3521 ads_msgfree(ads, res);