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);
225 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
227 ads->ldap_port = LDAP_PORT;
228 ads->ldap_ip = *interpret_addr2(srv);
231 /* Store our site name. */
232 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
237 /**********************************************************************
238 Try to find an AD dc using our internal name resolution routines
239 Try the realm first and then then workgroup name if netbios is not
241 **********************************************************************/
243 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
247 struct ip_service *ip_list;
249 BOOL got_realm = False;
250 BOOL use_own_domain = False;
252 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
254 /* if the realm and workgroup are both empty, assume they are ours */
257 c_realm = ads->server.realm;
259 if ( !c_realm || !*c_realm ) {
260 /* special case where no realm and no workgroup means our own */
261 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
262 use_own_domain = True;
263 c_realm = lp_realm();
267 if (c_realm && *c_realm)
270 /* we need to try once with the realm name and fallback to the
271 netbios domain name if we fail (if netbios has not been disabled */
273 if ( !got_realm && !lp_disable_netbios() ) {
274 c_realm = ads->server.workgroup;
275 if (!c_realm || !*c_realm) {
276 if ( use_own_domain )
277 c_realm = lp_workgroup();
280 if ( !c_realm || !*c_realm ) {
281 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
282 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
286 pstrcpy( realm, c_realm );
288 sitename = sitename_fetch(realm);
292 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
293 (got_realm ? "realm" : "domain"), realm));
295 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
296 if (!NT_STATUS_IS_OK(status)) {
297 /* fall back to netbios if we can */
298 if ( got_realm && !lp_disable_netbios() ) {
307 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
308 for ( i=0; i<count; i++ ) {
311 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
313 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
317 /* realm in this case is a workgroup name. We need
318 to ignore any IP addresses in the negative connection
319 cache that match ip addresses returned in the ad realm
320 case. It sucks that I have to reproduce the logic above... */
321 c_realm = ads->server.realm;
322 if ( !c_realm || !*c_realm ) {
323 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
324 c_realm = lp_realm();
327 if (c_realm && *c_realm &&
328 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
329 /* Ensure we add the workgroup name for this
330 IP address as negative too. */
331 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
336 if ( ads_try_connect(ads, server) ) {
342 /* keep track of failures */
343 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
348 /* In case we failed to contact one of our closest DC on our site we
349 * need to try to find another DC, retry with a site-less SRV DNS query
353 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
354 "trying to find another DC\n", sitename));
356 namecache_delete(realm, 0x1C);
360 return NT_STATUS_NO_LOGON_SERVERS;
365 * Connect to the LDAP server
366 * @param ads Pointer to an existing ADS_STRUCT
367 * @return status of connection
369 ADS_STATUS ads_connect(ADS_STRUCT *ads)
371 int version = LDAP_VERSION3;
375 ads->last_attempt = time(NULL);
378 /* try with a user specified server */
380 if (ads->server.ldap_server &&
381 ads_try_connect(ads, ads->server.ldap_server)) {
385 ntstatus = ads_find_dc(ads);
386 if (NT_STATUS_IS_OK(ntstatus)) {
390 return ADS_ERROR_NT(ntstatus);
393 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
395 if (!ads->auth.user_name) {
396 /* Must use the userPrincipalName value here or sAMAccountName
397 and not servicePrincipalName; found by Guenther Deschner */
399 asprintf(&ads->auth.user_name, "%s$", global_myname() );
402 if (!ads->auth.realm) {
403 ads->auth.realm = SMB_STRDUP(ads->config.realm);
406 if (!ads->auth.kdc_server) {
407 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
411 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
412 to MIT kerberos to work (tridge) */
415 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
416 setenv(env, ads->auth.kdc_server, 1);
421 /* If the caller() requested no LDAP bind, then we are done */
423 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
427 /* Otherwise setup the TCP LDAP session */
429 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
430 LDAP_PORT, lp_ldap_timeout())) == NULL )
432 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
435 /* cache the successful connection for workgroup and realm */
436 if (ads_closest_dc(ads)) {
437 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
438 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
441 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
443 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
444 if (!ADS_ERR_OK(status)) {
448 /* fill in the current time and offsets */
450 status = ads_current_time( ads );
451 if ( !ADS_ERR_OK(status) ) {
455 /* Now do the bind */
457 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
458 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
461 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
462 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
465 return ads_sasl_bind(ads);
469 Duplicate a struct berval into talloc'ed memory
471 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
473 struct berval *value;
475 if (!in_val) return NULL;
477 value = TALLOC_ZERO_P(ctx, struct berval);
480 if (in_val->bv_len == 0) return value;
482 value->bv_len = in_val->bv_len;
483 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
489 Make a values list out of an array of (struct berval *)
491 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
492 const struct berval **in_vals)
494 struct berval **values;
497 if (!in_vals) return NULL;
498 for (i=0; in_vals[i]; i++)
500 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
501 if (!values) return NULL;
503 for (i=0; in_vals[i]; i++) {
504 values[i] = dup_berval(ctx, in_vals[i]);
510 UTF8-encode a values list out of an array of (char *)
512 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
517 if (!in_vals) return NULL;
518 for (i=0; in_vals[i]; i++)
520 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
521 if (!values) return NULL;
523 for (i=0; in_vals[i]; i++) {
524 push_utf8_talloc(ctx, &values[i], in_vals[i]);
530 Pull a (char *) array out of a UTF8-encoded values list
532 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
537 if (!in_vals) return NULL;
538 for (i=0; in_vals[i]; i++)
540 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
541 if (!values) return NULL;
543 for (i=0; in_vals[i]; i++) {
544 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
550 * Do a search with paged results. cookie must be null on the first
551 * call, and then returned on each subsequent call. It will be null
552 * again when the entire search is complete
553 * @param ads connection to ads server
554 * @param bind_path Base dn for the search
555 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
556 * @param expr Search expression - specified in local charset
557 * @param attrs Attributes to retrieve - specified in utf8 or ascii
558 * @param res ** which will contain results - free res* with ads_msgfree()
559 * @param count Number of entries retrieved on this page
560 * @param cookie The paged results cookie to be returned on subsequent calls
561 * @return status of search
563 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
564 const char *bind_path,
565 int scope, const char *expr,
566 const char **attrs, void *args,
568 int *count, struct berval **cookie)
571 char *utf8_expr, *utf8_path, **search_attrs;
572 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
573 BerElement *cookie_be = NULL;
574 struct berval *cookie_bv= NULL;
575 BerElement *ext_be = NULL;
576 struct berval *ext_bv= NULL;
579 ads_control *external_control = (ads_control *) args;
583 if (!(ctx = talloc_init("ads_do_paged_search_args")))
584 return ADS_ERROR(LDAP_NO_MEMORY);
586 /* 0 means the conversion worked but the result was empty
587 so we only fail if it's -1. In any case, it always
588 at least nulls out the dest */
589 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
590 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
595 if (!attrs || !(*attrs))
598 /* This would be the utf8-encoded version...*/
599 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
600 if (!(str_list_copy(&search_attrs, attrs))) {
606 /* Paged results only available on ldap v3 or later */
607 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
608 if (version < LDAP_VERSION3) {
609 rc = LDAP_NOT_SUPPORTED;
613 cookie_be = ber_alloc_t(LBER_USE_DER);
615 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
616 ber_bvfree(*cookie); /* don't need it from last time */
619 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
621 ber_flatten(cookie_be, &cookie_bv);
622 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
623 PagedResults.ldctl_iscritical = (char) 1;
624 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
625 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
627 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
628 NoReferrals.ldctl_iscritical = (char) 0;
629 NoReferrals.ldctl_value.bv_len = 0;
630 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
632 if (external_control &&
633 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
634 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
636 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
637 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
639 /* win2k does not accept a ldctl_value beeing passed in */
641 if (external_control->val != 0) {
643 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
648 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
652 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
657 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
658 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
661 ExternalCtrl.ldctl_value.bv_len = 0;
662 ExternalCtrl.ldctl_value.bv_val = NULL;
665 controls[0] = &NoReferrals;
666 controls[1] = &PagedResults;
667 controls[2] = &ExternalCtrl;
671 controls[0] = &NoReferrals;
672 controls[1] = &PagedResults;
676 /* we need to disable referrals as the openldap libs don't
677 handle them and paged results at the same time. Using them
678 together results in the result record containing the server
679 page control being removed from the result list (tridge/jmcd)
681 leaving this in despite the control that says don't generate
682 referrals, in case the server doesn't support it (jmcd)
684 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
686 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
687 search_attrs, 0, controls,
689 (LDAPMessage **)res);
691 ber_free(cookie_be, 1);
692 ber_bvfree(cookie_bv);
695 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
696 ldap_err2string(rc)));
700 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
701 NULL, &rcontrols, 0);
707 for (i=0; rcontrols[i]; i++) {
708 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
709 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
710 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
712 /* the berval is the cookie, but must be freed when
714 if (cookie_bv->bv_len) /* still more to do */
715 *cookie=ber_bvdup(cookie_bv);
718 ber_bvfree(cookie_bv);
719 ber_free(cookie_be, 1);
723 ldap_controls_free(rcontrols);
736 /* if/when we decide to utf8-encode attrs, take out this next line */
737 str_list_free(&search_attrs);
739 return ADS_ERROR(rc);
742 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
743 int scope, const char *expr,
744 const char **attrs, LDAPMessage **res,
745 int *count, struct berval **cookie)
747 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
752 * Get all results for a search. This uses ads_do_paged_search() to return
753 * all entries in a large search.
754 * @param ads connection to ads server
755 * @param bind_path Base dn for the search
756 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
757 * @param expr Search expression
758 * @param attrs Attributes to retrieve
759 * @param res ** which will contain results - free res* with ads_msgfree()
760 * @return status of search
762 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
763 int scope, const char *expr,
764 const char **attrs, void *args,
767 struct berval *cookie = NULL;
772 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
775 if (!ADS_ERR_OK(status))
778 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
780 LDAPMessage *res2 = NULL;
782 LDAPMessage *msg, *next;
784 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
785 attrs, args, &res2, &count, &cookie);
787 if (!ADS_ERR_OK(status2)) break;
789 /* this relies on the way that ldap_add_result_entry() works internally. I hope
790 that this works on all ldap libs, but I have only tested with openldap */
791 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
792 next = ads_next_entry(ads, msg);
793 ldap_add_result_entry((LDAPMessage **)res, msg);
795 /* note that we do not free res2, as the memory is now
796 part of the main returned list */
799 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
800 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
806 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
807 int scope, const char *expr,
808 const char **attrs, LDAPMessage **res)
810 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
813 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
814 int scope, const char *expr,
815 const char **attrs, uint32 sd_flags,
820 args.control = ADS_SD_FLAGS_OID;
822 args.critical = True;
824 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
829 * Run a function on all results for a search. Uses ads_do_paged_search() and
830 * runs the function as each page is returned, using ads_process_results()
831 * @param ads connection to ads server
832 * @param bind_path Base dn for the search
833 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
834 * @param expr Search expression - specified in local charset
835 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
836 * @param fn Function which takes attr name, values list, and data_area
837 * @param data_area Pointer which is passed to function on each call
838 * @return status of search
840 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
841 int scope, const char *expr, const char **attrs,
842 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
845 struct berval *cookie = NULL;
850 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
853 if (!ADS_ERR_OK(status)) return status;
855 ads_process_results(ads, res, fn, data_area);
856 ads_msgfree(ads, res);
859 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
860 &res, &count, &cookie);
862 if (!ADS_ERR_OK(status)) break;
864 ads_process_results(ads, res, fn, data_area);
865 ads_msgfree(ads, res);
872 * Do a search with a timeout.
873 * @param ads connection to ads server
874 * @param bind_path Base dn for the search
875 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
876 * @param expr Search expression
877 * @param attrs Attributes to retrieve
878 * @param res ** which will contain results - free res* with ads_msgfree()
879 * @return status of search
881 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
883 const char **attrs, LDAPMessage **res)
886 char *utf8_expr, *utf8_path, **search_attrs = NULL;
890 if (!(ctx = talloc_init("ads_do_search"))) {
891 DEBUG(1,("ads_do_search: talloc_init() failed!"));
892 return ADS_ERROR(LDAP_NO_MEMORY);
895 /* 0 means the conversion worked but the result was empty
896 so we only fail if it's negative. In any case, it always
897 at least nulls out the dest */
898 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
899 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
900 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
905 if (!attrs || !(*attrs))
908 /* This would be the utf8-encoded version...*/
909 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
910 if (!(str_list_copy(&search_attrs, attrs)))
912 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
918 /* see the note in ads_do_paged_search - we *must* disable referrals */
919 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
921 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
922 search_attrs, 0, NULL, NULL,
924 (LDAPMessage **)res);
926 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
927 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
933 /* if/when we decide to utf8-encode attrs, take out this next line */
934 str_list_free(&search_attrs);
935 return ADS_ERROR(rc);
938 * Do a general ADS search
939 * @param ads connection to ads server
940 * @param res ** which will contain results - free res* with ads_msgfree()
941 * @param expr Search expression
942 * @param attrs Attributes to retrieve
943 * @return status of search
945 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
946 const char *expr, const char **attrs)
948 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
953 * Do a search on a specific DistinguishedName
954 * @param ads connection to ads server
955 * @param res ** which will contain results - free res* with ads_msgfree()
956 * @param dn DistinguishName to search
957 * @param attrs Attributes to retrieve
958 * @return status of search
960 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
961 const char *dn, const char **attrs)
963 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
968 * Free up memory from a ads_search
969 * @param ads connection to ads server
970 * @param msg Search results to free
972 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
979 * Free up memory from various ads requests
980 * @param ads connection to ads server
981 * @param mem Area to free
983 void ads_memfree(ADS_STRUCT *ads, void *mem)
989 * Get a dn from search results
990 * @param ads connection to ads server
991 * @param msg Search result
994 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
996 char *utf8_dn, *unix_dn;
998 utf8_dn = ldap_get_dn(ads->ld, msg);
1001 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1005 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1006 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1010 ldap_memfree(utf8_dn);
1015 * Get the parent from a dn
1016 * @param dn the dn to return the parent from
1017 * @return parent dn string
1019 char *ads_parent_dn(const char *dn)
1027 p = strchr(dn, ',');
1037 * Find a machine account given a hostname
1038 * @param ads connection to ads server
1039 * @param res ** which will contain results - free res* with ads_msgfree()
1040 * @param host Hostname to search for
1041 * @return status of search
1043 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1044 const char *machine)
1048 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1052 /* the easiest way to find a machine account anywhere in the tree
1053 is to look for hostname$ */
1054 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1055 DEBUG(1, ("asprintf failed!\n"));
1056 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1059 status = ads_search(ads, res, expr, attrs);
1065 * Initialize a list of mods to be used in a modify request
1066 * @param ctx An initialized TALLOC_CTX
1067 * @return allocated ADS_MODLIST
1069 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1071 #define ADS_MODLIST_ALLOC_SIZE 10
1074 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1075 /* -1 is safety to make sure we don't go over the end.
1076 need to reset it to NULL before doing ldap modify */
1077 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1079 return (ADS_MODLIST)mods;
1084 add an attribute to the list, with values list already constructed
1086 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1087 int mod_op, const char *name,
1088 const void *_invals)
1090 const void **invals = (const void **)_invals;
1092 LDAPMod **modlist = (LDAPMod **) *mods;
1093 struct berval **ber_values = NULL;
1094 char **char_values = NULL;
1097 mod_op = LDAP_MOD_DELETE;
1099 if (mod_op & LDAP_MOD_BVALUES)
1100 ber_values = ads_dup_values(ctx,
1101 (const struct berval **)invals);
1103 char_values = ads_push_strvals(ctx,
1104 (const char **) invals);
1107 /* find the first empty slot */
1108 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1110 if (modlist[curmod] == (LDAPMod *) -1) {
1111 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1112 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1113 return ADS_ERROR(LDAP_NO_MEMORY);
1114 memset(&modlist[curmod], 0,
1115 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1116 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1117 *mods = (ADS_MODLIST)modlist;
1120 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1121 return ADS_ERROR(LDAP_NO_MEMORY);
1122 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1123 if (mod_op & LDAP_MOD_BVALUES) {
1124 modlist[curmod]->mod_bvalues = ber_values;
1125 } else if (mod_op & LDAP_MOD_DELETE) {
1126 modlist[curmod]->mod_values = NULL;
1128 modlist[curmod]->mod_values = char_values;
1131 modlist[curmod]->mod_op = mod_op;
1132 return ADS_ERROR(LDAP_SUCCESS);
1136 * Add a single string value to a mod list
1137 * @param ctx An initialized TALLOC_CTX
1138 * @param mods An initialized ADS_MODLIST
1139 * @param name The attribute name to add
1140 * @param val The value to add - NULL means DELETE
1141 * @return ADS STATUS indicating success of add
1143 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1144 const char *name, const char *val)
1146 const char *values[2];
1152 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1153 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1157 * Add an array of string values to a mod list
1158 * @param ctx An initialized TALLOC_CTX
1159 * @param mods An initialized ADS_MODLIST
1160 * @param name The attribute name to add
1161 * @param vals The array of string values to add - NULL means DELETE
1162 * @return ADS STATUS indicating success of add
1164 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1165 const char *name, const char **vals)
1168 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1169 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1170 name, (const void **) vals);
1175 * Add a single ber-encoded value to a mod list
1176 * @param ctx An initialized TALLOC_CTX
1177 * @param mods An initialized ADS_MODLIST
1178 * @param name The attribute name to add
1179 * @param val The value to add - NULL means DELETE
1180 * @return ADS STATUS indicating success of add
1182 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1183 const char *name, const struct berval *val)
1185 const struct berval *values[2];
1190 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1191 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1192 name, (const void **) values);
1197 * Perform an ldap modify
1198 * @param ads connection to ads server
1199 * @param mod_dn DistinguishedName to modify
1200 * @param mods list of modifications to perform
1201 * @return status of modify
1203 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1206 char *utf8_dn = NULL;
1208 this control is needed to modify that contains a currently
1209 non-existent attribute (but allowable for the object) to run
1211 LDAPControl PermitModify = {
1212 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1215 LDAPControl *controls[2];
1217 controls[0] = &PermitModify;
1220 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1221 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1224 /* find the end of the list, marked by NULL or -1 */
1225 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1226 /* make sure the end of the list is NULL */
1228 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1229 (LDAPMod **) mods, controls, NULL);
1231 return ADS_ERROR(ret);
1235 * Perform an ldap add
1236 * @param ads connection to ads server
1237 * @param new_dn DistinguishedName to add
1238 * @param mods list of attributes and values for DN
1239 * @return status of add
1241 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1244 char *utf8_dn = NULL;
1246 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1247 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1248 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1251 /* find the end of the list, marked by NULL or -1 */
1252 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1253 /* make sure the end of the list is NULL */
1256 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1258 return ADS_ERROR(ret);
1262 * Delete a DistinguishedName
1263 * @param ads connection to ads server
1264 * @param new_dn DistinguishedName to delete
1265 * @return status of delete
1267 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1270 char *utf8_dn = NULL;
1271 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1272 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1273 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1276 ret = ldap_delete_s(ads->ld, utf8_dn);
1278 return ADS_ERROR(ret);
1282 * Build an org unit string
1283 * if org unit is Computers or blank then assume a container, otherwise
1284 * assume a / separated list of organisational units.
1285 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1286 * @param ads connection to ads server
1287 * @param org_unit Organizational unit
1288 * @return org unit string - caller must free
1290 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1294 if (!org_unit || !*org_unit) {
1296 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1298 /* samba4 might not yet respond to a wellknownobject-query */
1299 return ret ? ret : SMB_STRDUP("cn=Computers");
1302 if (strequal(org_unit, "Computers")) {
1303 return SMB_STRDUP("cn=Computers");
1306 /* jmcd: removed "\\" from the separation chars, because it is
1307 needed as an escape for chars like '#' which are valid in an
1309 return ads_build_path(org_unit, "/", "ou=", 1);
1313 * Get a org unit string for a well-known GUID
1314 * @param ads connection to ads server
1315 * @param wknguid Well known GUID
1316 * @return org unit string - caller must free
1318 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1321 LDAPMessage *res = NULL;
1322 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1323 **bind_dn_exp = NULL;
1324 const char *attrs[] = {"distinguishedName", NULL};
1325 int new_ln, wkn_ln, bind_ln, i;
1327 if (wknguid == NULL) {
1331 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1332 DEBUG(1, ("asprintf failed!\n"));
1336 status = ads_search_dn(ads, &res, base, attrs);
1337 if (!ADS_ERR_OK(status)) {
1338 DEBUG(1,("Failed while searching for: %s\n", base));
1342 if (ads_count_replies(ads, res) != 1) {
1346 /* substitute the bind-path from the well-known-guid-search result */
1347 wkn_dn = ads_get_dn(ads, res);
1352 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1357 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1362 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1364 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1367 new_ln = wkn_ln - bind_ln;
1369 ret = SMB_STRDUP(wkn_dn_exp[0]);
1374 for (i=1; i < new_ln; i++) {
1377 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1383 ret = SMB_STRDUP(s);
1392 ads_msgfree(ads, res);
1393 ads_memfree(ads, wkn_dn);
1395 ldap_value_free(wkn_dn_exp);
1398 ldap_value_free(bind_dn_exp);
1405 * Adds (appends) an item to an attribute array, rather then
1406 * replacing the whole list
1407 * @param ctx An initialized TALLOC_CTX
1408 * @param mods An initialized ADS_MODLIST
1409 * @param name name of the ldap attribute to append to
1410 * @param vals an array of values to add
1411 * @return status of addition
1414 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1415 const char *name, const char **vals)
1417 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1418 (const void *) vals);
1422 * Determines the computer account's current KVNO via an LDAP lookup
1423 * @param ads An initialized ADS_STRUCT
1424 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1425 * @return the kvno for the computer account, or -1 in case of a failure.
1428 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1430 LDAPMessage *res = NULL;
1431 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1433 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1434 char *dn_string = NULL;
1435 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1437 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1438 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1441 ret = ads_search(ads, &res, filter, attrs);
1443 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1444 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1445 ads_msgfree(ads, res);
1449 dn_string = ads_get_dn(ads, res);
1451 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1452 ads_msgfree(ads, res);
1455 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1456 ads_memfree(ads, dn_string);
1458 /* ---------------------------------------------------------
1459 * 0 is returned as a default KVNO from this point on...
1460 * This is done because Windows 2000 does not support key
1461 * version numbers. Chances are that a failure in the next
1462 * step is simply due to Windows 2000 being used for a
1463 * domain controller. */
1466 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1467 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1468 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1469 ads_msgfree(ads, res);
1474 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1475 ads_msgfree(ads, res);
1480 * This clears out all registered spn's for a given hostname
1481 * @param ads An initilaized ADS_STRUCT
1482 * @param machine_name the NetBIOS name of the computer.
1483 * @return 0 upon success, non-zero otherwise.
1486 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1489 LDAPMessage *res = NULL;
1491 const char *servicePrincipalName[1] = {NULL};
1492 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1493 char *dn_string = NULL;
1495 ret = ads_find_machine_acct(ads, &res, machine_name);
1496 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1497 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1498 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1499 ads_msgfree(ads, res);
1500 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1503 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1504 ctx = talloc_init("ads_clear_service_principal_names");
1506 ads_msgfree(ads, res);
1507 return ADS_ERROR(LDAP_NO_MEMORY);
1510 if (!(mods = ads_init_mods(ctx))) {
1511 talloc_destroy(ctx);
1512 ads_msgfree(ads, res);
1513 return ADS_ERROR(LDAP_NO_MEMORY);
1515 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1516 if (!ADS_ERR_OK(ret)) {
1517 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1518 ads_msgfree(ads, res);
1519 talloc_destroy(ctx);
1522 dn_string = ads_get_dn(ads, res);
1524 talloc_destroy(ctx);
1525 ads_msgfree(ads, res);
1526 return ADS_ERROR(LDAP_NO_MEMORY);
1528 ret = ads_gen_mod(ads, dn_string, mods);
1529 ads_memfree(ads,dn_string);
1530 if (!ADS_ERR_OK(ret)) {
1531 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1533 ads_msgfree(ads, res);
1534 talloc_destroy(ctx);
1538 ads_msgfree(ads, res);
1539 talloc_destroy(ctx);
1544 * This adds a service principal name to an existing computer account
1545 * (found by hostname) in AD.
1546 * @param ads An initialized ADS_STRUCT
1547 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1548 * @param my_fqdn The fully qualified DNS name of the machine
1549 * @param spn A string of the service principal to add, i.e. 'host'
1550 * @return 0 upon sucess, or non-zero if a failure occurs
1553 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1554 const char *my_fqdn, const char *spn)
1558 LDAPMessage *res = NULL;
1561 char *dn_string = NULL;
1562 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1564 ret = ads_find_machine_acct(ads, &res, machine_name);
1565 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1566 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1568 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1569 spn, machine_name, ads->config.realm));
1570 ads_msgfree(ads, res);
1571 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1574 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1575 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1576 ads_msgfree(ads, res);
1577 return ADS_ERROR(LDAP_NO_MEMORY);
1580 /* add short name spn */
1582 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1583 talloc_destroy(ctx);
1584 ads_msgfree(ads, res);
1585 return ADS_ERROR(LDAP_NO_MEMORY);
1588 strlower_m(&psp1[strlen(spn)]);
1589 servicePrincipalName[0] = psp1;
1591 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1592 psp1, machine_name));
1595 /* add fully qualified spn */
1597 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1598 ret = ADS_ERROR(LDAP_NO_MEMORY);
1602 strlower_m(&psp2[strlen(spn)]);
1603 servicePrincipalName[1] = psp2;
1605 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1606 psp2, machine_name));
1608 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1609 ret = ADS_ERROR(LDAP_NO_MEMORY);
1613 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1614 if (!ADS_ERR_OK(ret)) {
1615 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1619 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1620 ret = ADS_ERROR(LDAP_NO_MEMORY);
1624 ret = ads_gen_mod(ads, dn_string, mods);
1625 ads_memfree(ads,dn_string);
1626 if (!ADS_ERR_OK(ret)) {
1627 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1633 ads_msgfree(ads, res);
1638 * adds a machine account to the ADS server
1639 * @param ads An intialized ADS_STRUCT
1640 * @param machine_name - the NetBIOS machine name of this account.
1641 * @param account_type A number indicating the type of account to create
1642 * @param org_unit The LDAP path in which to place this account
1643 * @return 0 upon success, or non-zero otherwise
1646 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1647 const char *org_unit)
1650 char *samAccountName, *controlstr;
1653 char *machine_escaped = NULL;
1655 const char *objectClass[] = {"top", "person", "organizationalPerson",
1656 "user", "computer", NULL};
1657 LDAPMessage *res = NULL;
1658 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1659 UF_DONT_EXPIRE_PASSWD |\
1660 UF_ACCOUNTDISABLE );
1662 if (!(ctx = talloc_init("ads_add_machine_acct")))
1663 return ADS_ERROR(LDAP_NO_MEMORY);
1665 ret = ADS_ERROR(LDAP_NO_MEMORY);
1667 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1668 if (!machine_escaped) {
1672 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1673 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1675 if ( !new_dn || !samAccountName ) {
1679 #ifndef ENCTYPE_ARCFOUR_HMAC
1680 acct_control |= UF_USE_DES_KEY_ONLY;
1683 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1687 if (!(mods = ads_init_mods(ctx))) {
1691 ads_mod_str(ctx, &mods, "cn", machine_name);
1692 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1693 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1694 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1696 ret = ads_gen_add(ads, new_dn, mods);
1699 SAFE_FREE(machine_escaped);
1700 ads_msgfree(ads, res);
1701 talloc_destroy(ctx);
1707 * move a machine account to another OU on the ADS server
1708 * @param ads - An intialized ADS_STRUCT
1709 * @param machine_name - the NetBIOS machine name of this account.
1710 * @param org_unit - The LDAP path in which to place this account
1711 * @param moved - whether we moved the machine account (optional)
1712 * @return 0 upon success, or non-zero otherwise
1715 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1716 const char *org_unit, BOOL *moved)
1720 LDAPMessage *res = NULL;
1721 char *filter = NULL;
1722 char *computer_dn = NULL;
1724 char *computer_rdn = NULL;
1725 BOOL need_move = False;
1727 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1728 rc = ADS_ERROR(LDAP_NO_MEMORY);
1732 /* Find pre-existing machine */
1733 rc = ads_search(ads, &res, filter, NULL);
1734 if (!ADS_ERR_OK(rc)) {
1738 computer_dn = ads_get_dn(ads, res);
1740 rc = ADS_ERROR(LDAP_NO_MEMORY);
1744 parent_dn = ads_parent_dn(computer_dn);
1745 if (strequal(parent_dn, org_unit)) {
1751 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1752 rc = ADS_ERROR(LDAP_NO_MEMORY);
1756 ldap_status = ldap_rename_s(ads->ld, computer_dn, computer_rdn,
1757 org_unit, 1, NULL, NULL);
1758 rc = ADS_ERROR(ldap_status);
1761 ads_msgfree(ads, res);
1763 SAFE_FREE(computer_dn);
1764 SAFE_FREE(computer_rdn);
1766 if (!ADS_ERR_OK(rc)) {
1778 dump a binary result from ldap
1780 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1783 for (i=0; values[i]; i++) {
1784 printf("%s: ", field);
1785 for (j=0; j<values[i]->bv_len; j++) {
1786 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1792 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1796 for (i=0; values[i]; i++) {
1797 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1798 printf("%s: %s\n", field,
1799 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1804 dump a sid result from ldap
1806 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1809 for (i=0; values[i]; i++) {
1811 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1812 printf("%s: %s\n", field, sid_string_static(&sid));
1817 dump ntSecurityDescriptor
1819 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1824 TALLOC_CTX *ctx = 0;
1826 if (!(ctx = talloc_init("sec_io_desc")))
1830 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1831 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1832 prs_set_offset(&ps,0);
1835 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1837 talloc_destroy(ctx);
1841 ads_disp_sd(ads, ctx, psd);
1845 talloc_destroy(ctx);
1849 dump a string result from ldap
1851 static void dump_string(const char *field, char **values)
1854 for (i=0; values[i]; i++) {
1855 printf("%s: %s\n", field, values[i]);
1860 dump a field from LDAP on stdout
1864 static BOOL ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1869 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1871 {"objectGUID", False, dump_guid},
1872 {"netbootGUID", False, dump_guid},
1873 {"nTSecurityDescriptor", False, dump_sd},
1874 {"dnsRecord", False, dump_binary},
1875 {"objectSid", False, dump_sid},
1876 {"tokenGroups", False, dump_sid},
1877 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1878 {"tokengroupsGlobalandUniversal", False, dump_sid},
1879 {"mS-DS-CreatorSID", False, dump_sid},
1884 if (!field) { /* must be end of an entry */
1889 for (i=0; handlers[i].name; i++) {
1890 if (StrCaseCmp(handlers[i].name, field) == 0) {
1891 if (!values) /* first time, indicate string or not */
1892 return handlers[i].string;
1893 handlers[i].handler(ads, field, (struct berval **) values);
1897 if (!handlers[i].name) {
1898 if (!values) /* first time, indicate string conversion */
1900 dump_string(field, (char **)values);
1906 * Dump a result from LDAP on stdout
1907 * used for debugging
1908 * @param ads connection to ads server
1909 * @param res Results to dump
1912 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1914 ads_process_results(ads, res, ads_dump_field, NULL);
1918 * Walk through results, calling a function for each entry found.
1919 * The function receives a field name, a berval * array of values,
1920 * and a data area passed through from the start. The function is
1921 * called once with null for field and values at the end of each
1923 * @param ads connection to ads server
1924 * @param res Results to process
1925 * @param fn Function for processing each result
1926 * @param data_area user-defined area to pass to function
1928 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1929 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
1935 if (!(ctx = talloc_init("ads_process_results")))
1938 for (msg = ads_first_entry(ads, res); msg;
1939 msg = ads_next_entry(ads, msg)) {
1943 for (utf8_field=ldap_first_attribute(ads->ld,
1944 (LDAPMessage *)msg,&b);
1946 utf8_field=ldap_next_attribute(ads->ld,
1947 (LDAPMessage *)msg,b)) {
1948 struct berval **ber_vals;
1949 char **str_vals, **utf8_vals;
1953 pull_utf8_talloc(ctx, &field, utf8_field);
1954 string = fn(ads, field, NULL, data_area);
1957 utf8_vals = ldap_get_values(ads->ld,
1958 (LDAPMessage *)msg, field);
1959 str_vals = ads_pull_strvals(ctx,
1960 (const char **) utf8_vals);
1961 fn(ads, field, (void **) str_vals, data_area);
1962 ldap_value_free(utf8_vals);
1964 ber_vals = ldap_get_values_len(ads->ld,
1965 (LDAPMessage *)msg, field);
1966 fn(ads, field, (void **) ber_vals, data_area);
1968 ldap_value_free_len(ber_vals);
1970 ldap_memfree(utf8_field);
1973 talloc_free_children(ctx);
1974 fn(ads, NULL, NULL, data_area); /* completed an entry */
1977 talloc_destroy(ctx);
1981 * count how many replies are in a LDAPMessage
1982 * @param ads connection to ads server
1983 * @param res Results to count
1984 * @return number of replies
1986 int ads_count_replies(ADS_STRUCT *ads, void *res)
1988 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1992 * pull the first entry from a ADS result
1993 * @param ads connection to ads server
1994 * @param res Results of search
1995 * @return first entry from result
1997 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1999 return ldap_first_entry(ads->ld, res);
2003 * pull the next entry from a ADS result
2004 * @param ads connection to ads server
2005 * @param res Results of search
2006 * @return next entry from result
2008 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2010 return ldap_next_entry(ads->ld, res);
2014 * pull a single string from a ADS result
2015 * @param ads connection to ads server
2016 * @param mem_ctx TALLOC_CTX to use for allocating result string
2017 * @param msg Results of search
2018 * @param field Attribute to retrieve
2019 * @return Result string in talloc context
2021 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2029 values = ldap_get_values(ads->ld, msg, field);
2034 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2036 if (rc != (size_t)-1)
2040 ldap_value_free(values);
2045 * pull an array of strings from a ADS result
2046 * @param ads connection to ads server
2047 * @param mem_ctx TALLOC_CTX to use for allocating result string
2048 * @param msg Results of search
2049 * @param field Attribute to retrieve
2050 * @return Result strings in talloc context
2052 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2053 LDAPMessage *msg, const char *field,
2060 values = ldap_get_values(ads->ld, msg, field);
2064 *num_values = ldap_count_values(values);
2066 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2068 ldap_value_free(values);
2072 for (i=0;i<*num_values;i++) {
2073 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2074 ldap_value_free(values);
2080 ldap_value_free(values);
2085 * pull an array of strings from a ADS result
2086 * (handle large multivalue attributes with range retrieval)
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 * @param current_strings strings returned by a previous call to this function
2092 * @param next_attribute The next query should ask for this attribute
2093 * @param num_values How many values did we get this time?
2094 * @param more_values Are there more values to get?
2095 * @return Result strings in talloc context
2097 char **ads_pull_strings_range(ADS_STRUCT *ads,
2098 TALLOC_CTX *mem_ctx,
2099 LDAPMessage *msg, const char *field,
2100 char **current_strings,
2101 const char **next_attribute,
2102 size_t *num_strings,
2106 char *expected_range_attrib, *range_attr;
2107 BerElement *ptr = NULL;
2110 size_t num_new_strings;
2111 unsigned long int range_start;
2112 unsigned long int range_end;
2114 /* we might have been given the whole lot anyway */
2115 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2116 *more_strings = False;
2120 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2122 /* look for Range result */
2123 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2125 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2126 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2127 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2135 /* nothing here - this field is just empty */
2136 *more_strings = False;
2140 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2141 &range_start, &range_end) == 2) {
2142 *more_strings = True;
2144 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2145 &range_start) == 1) {
2146 *more_strings = False;
2148 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2150 ldap_memfree(range_attr);
2151 *more_strings = False;
2156 if ((*num_strings) != range_start) {
2157 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2158 " - aborting range retreival\n",
2159 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2160 ldap_memfree(range_attr);
2161 *more_strings = False;
2165 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2167 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2168 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2169 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2170 range_attr, (unsigned long int)range_end - range_start + 1,
2171 (unsigned long int)num_new_strings));
2172 ldap_memfree(range_attr);
2173 *more_strings = False;
2177 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2178 *num_strings + num_new_strings);
2180 if (strings == NULL) {
2181 ldap_memfree(range_attr);
2182 *more_strings = False;
2186 if (new_strings && num_new_strings) {
2187 memcpy(&strings[*num_strings], new_strings,
2188 sizeof(*new_strings) * num_new_strings);
2191 (*num_strings) += num_new_strings;
2193 if (*more_strings) {
2194 *next_attribute = talloc_asprintf(mem_ctx,
2199 if (!*next_attribute) {
2200 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2201 ldap_memfree(range_attr);
2202 *more_strings = False;
2207 ldap_memfree(range_attr);
2213 * pull a single uint32 from a ADS result
2214 * @param ads connection to ads server
2215 * @param msg Results of search
2216 * @param field Attribute to retrieve
2217 * @param v Pointer to int to store result
2218 * @return boolean inidicating success
2220 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2225 values = ldap_get_values(ads->ld, msg, field);
2229 ldap_value_free(values);
2233 *v = atoi(values[0]);
2234 ldap_value_free(values);
2239 * pull a single objectGUID from an ADS result
2240 * @param ads connection to ADS server
2241 * @param msg results of search
2242 * @param guid 37-byte area to receive text guid
2243 * @return boolean indicating success
2245 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2248 UUID_FLAT flat_guid;
2250 values = ldap_get_values(ads->ld, msg, "objectGUID");
2255 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2256 smb_uuid_unpack(flat_guid, guid);
2257 ldap_value_free(values);
2260 ldap_value_free(values);
2267 * pull a single DOM_SID from a ADS result
2268 * @param ads connection to ads server
2269 * @param msg Results of search
2270 * @param field Attribute to retrieve
2271 * @param sid Pointer to sid to store result
2272 * @return boolean inidicating success
2274 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2277 struct berval **values;
2280 values = ldap_get_values_len(ads->ld, msg, field);
2286 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2288 ldap_value_free_len(values);
2293 * pull an array of DOM_SIDs from a ADS result
2294 * @param ads connection to ads server
2295 * @param mem_ctx TALLOC_CTX for allocating sid array
2296 * @param msg Results of search
2297 * @param field Attribute to retrieve
2298 * @param sids pointer to sid array to allocate
2299 * @return the count of SIDs pulled
2301 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2302 LDAPMessage *msg, const char *field, DOM_SID **sids)
2304 struct berval **values;
2308 values = ldap_get_values_len(ads->ld, msg, field);
2313 for (i=0; values[i]; i++)
2317 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2319 ldap_value_free_len(values);
2327 for (i=0; values[i]; i++) {
2328 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2331 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2336 ldap_value_free_len(values);
2341 * pull a SEC_DESC from a ADS result
2342 * @param ads connection to ads server
2343 * @param mem_ctx TALLOC_CTX for allocating sid array
2344 * @param msg Results of search
2345 * @param field Attribute to retrieve
2346 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2347 * @return boolean inidicating success
2349 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2350 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2352 struct berval **values;
2355 values = ldap_get_values_len(ads->ld, msg, field);
2357 if (!values) return False;
2361 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2362 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2363 prs_set_offset(&ps,0);
2365 ret = sec_io_desc("sd", sd, &ps, 1);
2369 ldap_value_free_len(values);
2374 * in order to support usernames longer than 21 characters we need to
2375 * use both the sAMAccountName and the userPrincipalName attributes
2376 * It seems that not all users have the userPrincipalName attribute set
2378 * @param ads connection to ads server
2379 * @param mem_ctx TALLOC_CTX for allocating sid array
2380 * @param msg Results of search
2381 * @return the username
2383 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2389 /* lookup_name() only works on the sAMAccountName to
2390 returning the username portion of userPrincipalName
2391 breaks winbindd_getpwnam() */
2393 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2394 if (ret && (p = strchr_m(ret, '@'))) {
2399 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2404 * find the update serial number - this is the core of the ldap cache
2405 * @param ads connection to ads server
2406 * @param ads connection to ADS server
2407 * @param usn Pointer to retrieved update serial number
2408 * @return status of search
2410 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2412 const char *attrs[] = {"highestCommittedUSN", NULL};
2416 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2417 if (!ADS_ERR_OK(status))
2420 if (ads_count_replies(ads, res) != 1) {
2421 ads_msgfree(ads, res);
2422 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2425 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2426 ads_msgfree(ads, res);
2427 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2430 ads_msgfree(ads, res);
2434 /* parse a ADS timestring - typical string is
2435 '20020917091222.0Z0' which means 09:12.22 17th September
2437 static time_t ads_parse_time(const char *str)
2443 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2444 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2445 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2454 /********************************************************************
2455 ********************************************************************/
2457 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2459 const char *attrs[] = {"currentTime", NULL};
2464 ADS_STRUCT *ads_s = ads;
2466 if (!(ctx = talloc_init("ads_current_time"))) {
2467 return ADS_ERROR(LDAP_NO_MEMORY);
2470 /* establish a new ldap tcp session if necessary */
2473 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2474 ads->server.ldap_server )) == NULL )
2478 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2479 status = ads_connect( ads_s );
2480 if ( !ADS_ERR_OK(status))
2484 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2485 if (!ADS_ERR_OK(status)) {
2489 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2491 ads_msgfree(ads_s, res);
2492 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2496 /* but save the time and offset in the original ADS_STRUCT */
2498 ads->config.current_time = ads_parse_time(timestr);
2500 if (ads->config.current_time != 0) {
2501 ads->auth.time_offset = ads->config.current_time - time(NULL);
2502 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2505 ads_msgfree(ads, res);
2507 status = ADS_SUCCESS;
2510 /* free any temporary ads connections */
2511 if ( ads_s != ads ) {
2512 ads_destroy( &ads_s );
2514 talloc_destroy(ctx);
2519 /********************************************************************
2520 ********************************************************************/
2522 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2524 const char *attrs[] = {"domainFunctionality", NULL};
2527 ADS_STRUCT *ads_s = ads;
2529 *val = DS_DOMAIN_FUNCTION_2000;
2531 /* establish a new ldap tcp session if necessary */
2534 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2535 ads->server.ldap_server )) == NULL )
2539 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2540 status = ads_connect( ads_s );
2541 if ( !ADS_ERR_OK(status))
2545 /* If the attribute does not exist assume it is a Windows 2000
2546 functional domain */
2548 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2549 if (!ADS_ERR_OK(status)) {
2550 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2551 status = ADS_SUCCESS;
2556 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2557 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2559 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2562 ads_msgfree(ads, res);
2565 /* free any temporary ads connections */
2566 if ( ads_s != ads ) {
2567 ads_destroy( &ads_s );
2574 * find the domain sid for our domain
2575 * @param ads connection to ads server
2576 * @param sid Pointer to domain sid
2577 * @return status of search
2579 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2581 const char *attrs[] = {"objectSid", NULL};
2585 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2587 if (!ADS_ERR_OK(rc)) return rc;
2588 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2589 ads_msgfree(ads, res);
2590 return ADS_ERROR_SYSTEM(ENOENT);
2592 ads_msgfree(ads, res);
2598 * find our site name
2599 * @param ads connection to ads server
2600 * @param mem_ctx Pointer to talloc context
2601 * @param site_name Pointer to the sitename
2602 * @return status of search
2604 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2608 const char *dn, *service_name;
2609 const char *attrs[] = { "dsServiceName", NULL };
2611 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2612 if (!ADS_ERR_OK(status)) {
2616 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2617 if (service_name == NULL) {
2618 ads_msgfree(ads, res);
2619 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2622 ads_msgfree(ads, res);
2624 /* go up three levels */
2625 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2627 return ADS_ERROR(LDAP_NO_MEMORY);
2630 *site_name = talloc_strdup(mem_ctx, dn);
2631 if (*site_name == NULL) {
2632 return ADS_ERROR(LDAP_NO_MEMORY);
2637 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2642 * find the site dn where a machine resides
2643 * @param ads connection to ads server
2644 * @param mem_ctx Pointer to talloc context
2645 * @param computer_name name of the machine
2646 * @param site_name Pointer to the sitename
2647 * @return status of search
2649 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2653 const char *parent, *config_context, *filter;
2654 const char *attrs[] = { "configurationNamingContext", NULL };
2657 /* shortcut a query */
2658 if (strequal(computer_name, ads->config.ldap_server_name)) {
2659 return ads_site_dn(ads, mem_ctx, site_dn);
2662 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2663 if (!ADS_ERR_OK(status)) {
2667 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2668 if (config_context == NULL) {
2669 ads_msgfree(ads, res);
2670 return ADS_ERROR(LDAP_NO_MEMORY);
2673 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2674 if (filter == NULL) {
2675 ads_msgfree(ads, res);
2676 return ADS_ERROR(LDAP_NO_MEMORY);
2679 ads_msgfree(ads, res);
2681 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2682 if (!ADS_ERR_OK(status)) {
2686 if (ads_count_replies(ads, res) != 1) {
2687 ads_msgfree(ads, res);
2688 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2691 dn = ads_get_dn(ads, res);
2693 ads_msgfree(ads, res);
2694 return ADS_ERROR(LDAP_NO_MEMORY);
2697 /* go up three levels */
2698 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2699 if (parent == NULL) {
2700 ads_msgfree(ads, res);
2701 ads_memfree(ads, dn);
2702 return ADS_ERROR(LDAP_NO_MEMORY);
2705 *site_dn = talloc_strdup(mem_ctx, parent);
2706 if (*site_dn == NULL) {
2707 ads_msgfree(ads, res);
2708 ads_memfree(ads, dn);
2709 ADS_ERROR(LDAP_NO_MEMORY);
2712 ads_memfree(ads, dn);
2713 ads_msgfree(ads, res);
2719 * get the upn suffixes for a domain
2720 * @param ads connection to ads server
2721 * @param mem_ctx Pointer to talloc context
2722 * @param suffixes Pointer to an array of suffixes
2723 * @param num_suffixes Pointer to the number of suffixes
2724 * @return status of search
2726 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2730 const char *config_context, *base;
2731 const char *attrs[] = { "configurationNamingContext", NULL };
2732 const char *attrs2[] = { "uPNSuffixes", NULL };
2734 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2735 if (!ADS_ERR_OK(status)) {
2739 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2740 if (config_context == NULL) {
2741 ads_msgfree(ads, res);
2742 return ADS_ERROR(LDAP_NO_MEMORY);
2745 ads_msgfree(ads, res);
2747 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2749 return ADS_ERROR(LDAP_NO_MEMORY);
2752 status = ads_search_dn(ads, &res, base, attrs2);
2753 if (!ADS_ERR_OK(status)) {
2757 if (ads_count_replies(ads, res) != 1) {
2758 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2761 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2762 if ((*suffixes) == NULL) {
2763 ads_msgfree(ads, res);
2764 return ADS_ERROR(LDAP_NO_MEMORY);
2767 ads_msgfree(ads, res);
2773 * pull a DOM_SID from an extended dn string
2774 * @param mem_ctx TALLOC_CTX
2775 * @param extended_dn string
2776 * @param flags string type of extended_dn
2777 * @param sid pointer to a DOM_SID
2778 * @return boolean inidicating success
2780 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2781 const char *extended_dn,
2782 enum ads_extended_dn_flags flags,
2791 /* otherwise extended_dn gets stripped off */
2792 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2796 * ADS_EXTENDED_DN_HEX_STRING:
2797 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2799 * ADS_EXTENDED_DN_STRING (only with w2k3):
2800 <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
2803 p = strchr(dn, ';');
2808 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2812 p += strlen(";<SID=");
2821 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2825 case ADS_EXTENDED_DN_STRING:
2826 if (!string_to_sid(sid, p)) {
2830 case ADS_EXTENDED_DN_HEX_STRING: {
2834 buf_len = strhex_to_str(buf, strlen(p), p);
2839 if (!sid_parse(buf, buf_len, sid)) {
2840 DEBUG(10,("failed to parse sid\n"));
2846 DEBUG(10,("unknown extended dn format\n"));
2854 * pull an array of DOM_SIDs from a ADS result
2855 * @param ads connection to ads server
2856 * @param mem_ctx TALLOC_CTX for allocating sid array
2857 * @param msg Results of search
2858 * @param field Attribute to retrieve
2859 * @param flags string type of extended_dn
2860 * @param sids pointer to sid array to allocate
2861 * @return the count of SIDs pulled
2863 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2864 TALLOC_CTX *mem_ctx,
2867 enum ads_extended_dn_flags flags,
2874 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2875 &dn_count)) == NULL) {
2879 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2881 TALLOC_FREE(dn_strings);
2885 for (i=0; i<dn_count; i++) {
2887 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2888 flags, &(*sids)[i])) {
2890 TALLOC_FREE(dn_strings);
2895 TALLOC_FREE(dn_strings);
2900 /********************************************************************
2901 ********************************************************************/
2903 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2905 LDAPMessage *res = NULL;
2910 status = ads_find_machine_acct(ads, &res, global_myname());
2911 if (!ADS_ERR_OK(status)) {
2912 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2917 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2918 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2922 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2923 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2927 ads_msgfree(ads, res);
2932 /********************************************************************
2933 ********************************************************************/
2935 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2937 LDAPMessage *res = NULL;
2942 status = ads_find_machine_acct(ads, &res, global_myname());
2943 if (!ADS_ERR_OK(status)) {
2944 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2949 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2950 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2954 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2955 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2959 ads_msgfree(ads, res);
2964 /********************************************************************
2965 ********************************************************************/
2967 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2969 LDAPMessage *res = NULL;
2974 status = ads_find_machine_acct(ads, &res, global_myname());
2975 if (!ADS_ERR_OK(status)) {
2976 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2981 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2982 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2986 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2987 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2991 ads_msgfree(ads, res);
2998 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3001 * Join a machine to a realm
3002 * Creates the machine account and sets the machine password
3003 * @param ads connection to ads server
3004 * @param machine name of host to add
3005 * @param org_unit Organizational unit to place machine in
3006 * @return status of join
3008 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3009 uint32 account_type, const char *org_unit)
3012 LDAPMessage *res = NULL;
3015 /* machine name must be lowercase */
3016 machine = SMB_STRDUP(machine_name);
3017 strlower_m(machine);
3020 status = ads_find_machine_acct(ads, (void **)&res, machine);
3021 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3022 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3023 status = ads_leave_realm(ads, machine);
3024 if (!ADS_ERR_OK(status)) {
3025 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3026 machine, ads->config.realm));
3031 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3032 if (!ADS_ERR_OK(status)) {
3033 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3038 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3039 if (!ADS_ERR_OK(status)) {
3040 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3046 ads_msgfree(ads, res);
3053 * Delete a machine from the realm
3054 * @param ads connection to ads server
3055 * @param hostname Machine to remove
3056 * @return status of delete
3058 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3063 char *hostnameDN, *host;
3065 LDAPControl ldap_control;
3066 LDAPControl * pldap_control[2] = {NULL, NULL};
3068 pldap_control[0] = &ldap_control;
3069 memset(&ldap_control, 0, sizeof(LDAPControl));
3070 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3072 /* hostname must be lowercase */
3073 host = SMB_STRDUP(hostname);
3076 status = ads_find_machine_acct(ads, &res, host);
3077 if (!ADS_ERR_OK(status)) {
3078 DEBUG(0, ("Host account for %s does not exist.\n", host));
3083 msg = ads_first_entry(ads, res);
3086 return ADS_ERROR_SYSTEM(ENOENT);
3089 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3091 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
3093 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3095 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3098 if (rc != LDAP_SUCCESS) {
3099 const char *attrs[] = { "cn", NULL };
3100 LDAPMessage *msg_sub;
3102 /* we only search with scope ONE, we do not expect any further
3103 * objects to be created deeper */
3105 status = ads_do_search_retry(ads, hostnameDN,
3106 LDAP_SCOPE_ONELEVEL,
3107 "(objectclass=*)", attrs, &res);
3109 if (!ADS_ERR_OK(status)) {
3111 ads_memfree(ads, hostnameDN);
3115 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3116 msg_sub = ads_next_entry(ads, msg_sub)) {
3120 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3122 ads_memfree(ads, hostnameDN);
3123 return ADS_ERROR(LDAP_NO_MEMORY);
3126 status = ads_del_dn(ads, dn);
3127 if (!ADS_ERR_OK(status)) {
3128 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3130 ads_memfree(ads, dn);
3131 ads_memfree(ads, hostnameDN);
3135 ads_memfree(ads, dn);
3138 /* there should be no subordinate objects anymore */
3139 status = ads_do_search_retry(ads, hostnameDN,
3140 LDAP_SCOPE_ONELEVEL,
3141 "(objectclass=*)", attrs, &res);
3143 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3145 ads_memfree(ads, hostnameDN);
3149 /* delete hostnameDN now */
3150 status = ads_del_dn(ads, hostnameDN);
3151 if (!ADS_ERR_OK(status)) {
3153 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3154 ads_memfree(ads, hostnameDN);
3159 ads_memfree(ads, hostnameDN);
3161 status = ads_find_machine_acct(ads, &res, host);
3162 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3163 DEBUG(3, ("Failed to remove host account.\n"));
3173 * pull all token-sids from an LDAP dn
3174 * @param ads connection to ads server
3175 * @param mem_ctx TALLOC_CTX for allocating sid array
3176 * @param dn of LDAP object
3177 * @param user_sid pointer to DOM_SID (objectSid)
3178 * @param primary_group_sid pointer to DOM_SID (self composed)
3179 * @param sids pointer to sid array to allocate
3180 * @param num_sids counter of SIDs pulled
3181 * @return status of token query
3183 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3184 TALLOC_CTX *mem_ctx,
3187 DOM_SID *primary_group_sid,
3192 LDAPMessage *res = NULL;
3194 size_t tmp_num_sids;
3196 DOM_SID tmp_user_sid;
3197 DOM_SID tmp_primary_group_sid;
3199 const char *attrs[] = {
3206 status = ads_search_retry_dn(ads, &res, dn, attrs);
3207 if (!ADS_ERR_OK(status)) {
3211 count = ads_count_replies(ads, res);
3213 ads_msgfree(ads, res);
3214 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3217 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3218 ads_msgfree(ads, res);
3219 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3222 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3223 ads_msgfree(ads, res);
3224 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3228 /* hack to compose the primary group sid without knowing the
3234 sid_copy(&domsid, &tmp_user_sid);
3236 if (!sid_split_rid(&domsid, &dummy_rid)) {
3237 ads_msgfree(ads, res);
3238 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3241 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3242 ads_msgfree(ads, res);
3243 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3247 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3249 if (tmp_num_sids == 0 || !tmp_sids) {
3250 ads_msgfree(ads, res);
3251 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3255 *num_sids = tmp_num_sids;
3263 *user_sid = tmp_user_sid;
3266 if (primary_group_sid) {
3267 *primary_group_sid = tmp_primary_group_sid;
3270 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3272 ads_msgfree(ads, res);
3273 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3277 * Find a sAMAccoutName in LDAP
3278 * @param ads connection to ads server
3279 * @param mem_ctx TALLOC_CTX for allocating sid array
3280 * @param samaccountname to search
3281 * @param uac_ret uint32 pointer userAccountControl attribute value
3282 * @param dn_ret pointer to dn
3283 * @return status of token query
3285 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3286 TALLOC_CTX *mem_ctx,
3287 const char *samaccountname,
3289 const char **dn_ret)
3292 const char *attrs[] = { "userAccountControl", NULL };
3294 LDAPMessage *res = NULL;
3298 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3300 if (filter == NULL) {
3304 status = ads_do_search_all(ads, ads->config.bind_path,
3306 filter, attrs, &res);
3308 if (!ADS_ERR_OK(status)) {
3312 if (ads_count_replies(ads, res) != 1) {
3313 printf("no result\n");
3317 dn = ads_get_dn(ads, res);
3319 status = ADS_ERROR(LDAP_NO_MEMORY);
3323 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3324 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3333 *dn_ret = talloc_strdup(mem_ctx, dn);
3335 status = ADS_ERROR(LDAP_NO_MEMORY);
3340 ads_memfree(ads, dn);
3341 ads_msgfree(ads, res);
3347 * find our configuration path
3348 * @param ads connection to ads server
3349 * @param mem_ctx Pointer to talloc context
3350 * @param config_path Pointer to the config path
3351 * @return status of search
3353 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3354 TALLOC_CTX *mem_ctx,
3358 LDAPMessage *res = NULL;
3359 const char *config_context = NULL;
3360 const char *attrs[] = { "configurationNamingContext", NULL };
3362 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3363 "(objectclass=*)", attrs, &res);
3364 if (!ADS_ERR_OK(status)) {
3368 config_context = ads_pull_string(ads, mem_ctx, res,
3369 "configurationNamingContext");
3370 ads_msgfree(ads, res);
3371 if (!config_context) {
3372 return ADS_ERROR(LDAP_NO_MEMORY);
3376 *config_path = talloc_strdup(mem_ctx, config_context);
3377 if (!*config_path) {
3378 return ADS_ERROR(LDAP_NO_MEMORY);
3382 return ADS_ERROR(LDAP_SUCCESS);
3386 * find the displayName of an extended right
3387 * @param ads connection to ads server
3388 * @param config_path The config path
3389 * @param mem_ctx Pointer to talloc context
3390 * @param GUID struct of the rightsGUID
3391 * @return status of search
3393 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3394 const char *config_path,
3395 TALLOC_CTX *mem_ctx,
3396 const struct GUID *rights_guid)
3399 LDAPMessage *res = NULL;
3401 const char *attrs[] = { "displayName", NULL };
3402 const char *result = NULL;
3405 if (!ads || !mem_ctx || !rights_guid) {
3409 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3410 smb_uuid_string_static(*rights_guid));
3415 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3420 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3422 if (!ADS_ERR_OK(rc)) {
3426 if (ads_count_replies(ads, res) != 1) {
3430 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3433 ads_msgfree(ads, res);