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->ldap.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->ldap.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->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
443 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.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->ldap.ld, NULL, NULL));
461 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
462 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
465 return ads_sasl_bind(ads);
469 * Disconnect the LDAP server
470 * @param ads Pointer to an existing ADS_STRUCT
472 void ads_disconnect(ADS_STRUCT *ads)
475 ldap_unbind(ads->ldap.ld);
481 Duplicate a struct berval into talloc'ed memory
483 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
485 struct berval *value;
487 if (!in_val) return NULL;
489 value = TALLOC_ZERO_P(ctx, struct berval);
492 if (in_val->bv_len == 0) return value;
494 value->bv_len = in_val->bv_len;
495 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
501 Make a values list out of an array of (struct berval *)
503 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
504 const struct berval **in_vals)
506 struct berval **values;
509 if (!in_vals) return NULL;
510 for (i=0; in_vals[i]; i++)
512 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
513 if (!values) return NULL;
515 for (i=0; in_vals[i]; i++) {
516 values[i] = dup_berval(ctx, in_vals[i]);
522 UTF8-encode a values list out of an array of (char *)
524 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
529 if (!in_vals) return NULL;
530 for (i=0; in_vals[i]; i++)
532 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
533 if (!values) return NULL;
535 for (i=0; in_vals[i]; i++) {
536 push_utf8_talloc(ctx, &values[i], in_vals[i]);
542 Pull a (char *) array out of a UTF8-encoded values list
544 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
549 if (!in_vals) return NULL;
550 for (i=0; in_vals[i]; i++)
552 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
553 if (!values) return NULL;
555 for (i=0; in_vals[i]; i++) {
556 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
562 * Do a search with paged results. cookie must be null on the first
563 * call, and then returned on each subsequent call. It will be null
564 * again when the entire search is complete
565 * @param ads connection to ads server
566 * @param bind_path Base dn for the search
567 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
568 * @param expr Search expression - specified in local charset
569 * @param attrs Attributes to retrieve - specified in utf8 or ascii
570 * @param res ** which will contain results - free res* with ads_msgfree()
571 * @param count Number of entries retrieved on this page
572 * @param cookie The paged results cookie to be returned on subsequent calls
573 * @return status of search
575 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
576 const char *bind_path,
577 int scope, const char *expr,
578 const char **attrs, void *args,
580 int *count, struct berval **cookie)
583 char *utf8_expr, *utf8_path, **search_attrs;
584 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
585 BerElement *cookie_be = NULL;
586 struct berval *cookie_bv= NULL;
587 BerElement *ext_be = NULL;
588 struct berval *ext_bv= NULL;
591 ads_control *external_control = (ads_control *) args;
595 if (!(ctx = talloc_init("ads_do_paged_search_args")))
596 return ADS_ERROR(LDAP_NO_MEMORY);
598 /* 0 means the conversion worked but the result was empty
599 so we only fail if it's -1. In any case, it always
600 at least nulls out the dest */
601 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
602 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
607 if (!attrs || !(*attrs))
610 /* This would be the utf8-encoded version...*/
611 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
612 if (!(str_list_copy(&search_attrs, attrs))) {
618 /* Paged results only available on ldap v3 or later */
619 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
620 if (version < LDAP_VERSION3) {
621 rc = LDAP_NOT_SUPPORTED;
625 cookie_be = ber_alloc_t(LBER_USE_DER);
627 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
628 ber_bvfree(*cookie); /* don't need it from last time */
631 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
633 ber_flatten(cookie_be, &cookie_bv);
634 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
635 PagedResults.ldctl_iscritical = (char) 1;
636 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
637 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
639 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
640 NoReferrals.ldctl_iscritical = (char) 0;
641 NoReferrals.ldctl_value.bv_len = 0;
642 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
644 if (external_control &&
645 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
646 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
648 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
649 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
651 /* win2k does not accept a ldctl_value beeing passed in */
653 if (external_control->val != 0) {
655 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
660 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
664 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
669 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
670 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
673 ExternalCtrl.ldctl_value.bv_len = 0;
674 ExternalCtrl.ldctl_value.bv_val = NULL;
677 controls[0] = &NoReferrals;
678 controls[1] = &PagedResults;
679 controls[2] = &ExternalCtrl;
683 controls[0] = &NoReferrals;
684 controls[1] = &PagedResults;
688 /* we need to disable referrals as the openldap libs don't
689 handle them and paged results at the same time. Using them
690 together results in the result record containing the server
691 page control being removed from the result list (tridge/jmcd)
693 leaving this in despite the control that says don't generate
694 referrals, in case the server doesn't support it (jmcd)
696 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
698 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
699 search_attrs, 0, controls,
701 (LDAPMessage **)res);
703 ber_free(cookie_be, 1);
704 ber_bvfree(cookie_bv);
707 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
708 ldap_err2string(rc)));
712 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
713 NULL, &rcontrols, 0);
719 for (i=0; rcontrols[i]; i++) {
720 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
721 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
722 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
724 /* the berval is the cookie, but must be freed when
726 if (cookie_bv->bv_len) /* still more to do */
727 *cookie=ber_bvdup(cookie_bv);
730 ber_bvfree(cookie_bv);
731 ber_free(cookie_be, 1);
735 ldap_controls_free(rcontrols);
748 /* if/when we decide to utf8-encode attrs, take out this next line */
749 str_list_free(&search_attrs);
751 return ADS_ERROR(rc);
754 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
755 int scope, const char *expr,
756 const char **attrs, LDAPMessage **res,
757 int *count, struct berval **cookie)
759 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
764 * Get all results for a search. This uses ads_do_paged_search() to return
765 * all entries in a large search.
766 * @param ads connection to ads server
767 * @param bind_path Base dn for the search
768 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
769 * @param expr Search expression
770 * @param attrs Attributes to retrieve
771 * @param res ** which will contain results - free res* with ads_msgfree()
772 * @return status of search
774 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
775 int scope, const char *expr,
776 const char **attrs, void *args,
779 struct berval *cookie = NULL;
784 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
787 if (!ADS_ERR_OK(status))
790 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
792 LDAPMessage *res2 = NULL;
794 LDAPMessage *msg, *next;
796 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
797 attrs, args, &res2, &count, &cookie);
799 if (!ADS_ERR_OK(status2)) break;
801 /* this relies on the way that ldap_add_result_entry() works internally. I hope
802 that this works on all ldap libs, but I have only tested with openldap */
803 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
804 next = ads_next_entry(ads, msg);
805 ldap_add_result_entry((LDAPMessage **)res, msg);
807 /* note that we do not free res2, as the memory is now
808 part of the main returned list */
811 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
812 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
818 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
819 int scope, const char *expr,
820 const char **attrs, LDAPMessage **res)
822 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
825 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
826 int scope, const char *expr,
827 const char **attrs, uint32 sd_flags,
832 args.control = ADS_SD_FLAGS_OID;
834 args.critical = True;
836 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
841 * Run a function on all results for a search. Uses ads_do_paged_search() and
842 * runs the function as each page is returned, using ads_process_results()
843 * @param ads connection to ads server
844 * @param bind_path Base dn for the search
845 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
846 * @param expr Search expression - specified in local charset
847 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
848 * @param fn Function which takes attr name, values list, and data_area
849 * @param data_area Pointer which is passed to function on each call
850 * @return status of search
852 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
853 int scope, const char *expr, const char **attrs,
854 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
857 struct berval *cookie = NULL;
862 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
865 if (!ADS_ERR_OK(status)) return status;
867 ads_process_results(ads, res, fn, data_area);
868 ads_msgfree(ads, res);
871 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
872 &res, &count, &cookie);
874 if (!ADS_ERR_OK(status)) break;
876 ads_process_results(ads, res, fn, data_area);
877 ads_msgfree(ads, res);
884 * Do a search with a timeout.
885 * @param ads connection to ads server
886 * @param bind_path Base dn for the search
887 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
888 * @param expr Search expression
889 * @param attrs Attributes to retrieve
890 * @param res ** which will contain results - free res* with ads_msgfree()
891 * @return status of search
893 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
895 const char **attrs, LDAPMessage **res)
898 char *utf8_expr, *utf8_path, **search_attrs = NULL;
902 if (!(ctx = talloc_init("ads_do_search"))) {
903 DEBUG(1,("ads_do_search: talloc_init() failed!"));
904 return ADS_ERROR(LDAP_NO_MEMORY);
907 /* 0 means the conversion worked but the result was empty
908 so we only fail if it's negative. In any case, it always
909 at least nulls out the dest */
910 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
911 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
912 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
917 if (!attrs || !(*attrs))
920 /* This would be the utf8-encoded version...*/
921 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
922 if (!(str_list_copy(&search_attrs, attrs)))
924 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
930 /* see the note in ads_do_paged_search - we *must* disable referrals */
931 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
933 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
934 search_attrs, 0, NULL, NULL,
936 (LDAPMessage **)res);
938 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
939 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
945 /* if/when we decide to utf8-encode attrs, take out this next line */
946 str_list_free(&search_attrs);
947 return ADS_ERROR(rc);
950 * Do a general ADS search
951 * @param ads connection to ads server
952 * @param res ** which will contain results - free res* with ads_msgfree()
953 * @param expr Search expression
954 * @param attrs Attributes to retrieve
955 * @return status of search
957 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
958 const char *expr, const char **attrs)
960 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
965 * Do a search on a specific DistinguishedName
966 * @param ads connection to ads server
967 * @param res ** which will contain results - free res* with ads_msgfree()
968 * @param dn DistinguishName to search
969 * @param attrs Attributes to retrieve
970 * @return status of search
972 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
973 const char *dn, const char **attrs)
975 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
980 * Free up memory from a ads_search
981 * @param ads connection to ads server
982 * @param msg Search results to free
984 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
991 * Free up memory from various ads requests
992 * @param ads connection to ads server
993 * @param mem Area to free
995 void ads_memfree(ADS_STRUCT *ads, void *mem)
1001 * Get a dn from search results
1002 * @param ads connection to ads server
1003 * @param msg Search result
1006 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1008 char *utf8_dn, *unix_dn;
1010 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1013 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1017 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1018 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1022 ldap_memfree(utf8_dn);
1027 * Get the parent from a dn
1028 * @param dn the dn to return the parent from
1029 * @return parent dn string
1031 char *ads_parent_dn(const char *dn)
1039 p = strchr(dn, ',');
1049 * Find a machine account given a hostname
1050 * @param ads connection to ads server
1051 * @param res ** which will contain results - free res* with ads_msgfree()
1052 * @param host Hostname to search for
1053 * @return status of search
1055 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1056 const char *machine)
1060 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1064 /* the easiest way to find a machine account anywhere in the tree
1065 is to look for hostname$ */
1066 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1067 DEBUG(1, ("asprintf failed!\n"));
1068 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1071 status = ads_search(ads, res, expr, attrs);
1077 * Initialize a list of mods to be used in a modify request
1078 * @param ctx An initialized TALLOC_CTX
1079 * @return allocated ADS_MODLIST
1081 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1083 #define ADS_MODLIST_ALLOC_SIZE 10
1086 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1087 /* -1 is safety to make sure we don't go over the end.
1088 need to reset it to NULL before doing ldap modify */
1089 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1091 return (ADS_MODLIST)mods;
1096 add an attribute to the list, with values list already constructed
1098 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1099 int mod_op, const char *name,
1100 const void *_invals)
1102 const void **invals = (const void **)_invals;
1104 LDAPMod **modlist = (LDAPMod **) *mods;
1105 struct berval **ber_values = NULL;
1106 char **char_values = NULL;
1109 mod_op = LDAP_MOD_DELETE;
1111 if (mod_op & LDAP_MOD_BVALUES)
1112 ber_values = ads_dup_values(ctx,
1113 (const struct berval **)invals);
1115 char_values = ads_push_strvals(ctx,
1116 (const char **) invals);
1119 /* find the first empty slot */
1120 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1122 if (modlist[curmod] == (LDAPMod *) -1) {
1123 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1124 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1125 return ADS_ERROR(LDAP_NO_MEMORY);
1126 memset(&modlist[curmod], 0,
1127 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1128 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1129 *mods = (ADS_MODLIST)modlist;
1132 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1133 return ADS_ERROR(LDAP_NO_MEMORY);
1134 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1135 if (mod_op & LDAP_MOD_BVALUES) {
1136 modlist[curmod]->mod_bvalues = ber_values;
1137 } else if (mod_op & LDAP_MOD_DELETE) {
1138 modlist[curmod]->mod_values = NULL;
1140 modlist[curmod]->mod_values = char_values;
1143 modlist[curmod]->mod_op = mod_op;
1144 return ADS_ERROR(LDAP_SUCCESS);
1148 * Add a single string value to a mod list
1149 * @param ctx An initialized TALLOC_CTX
1150 * @param mods An initialized ADS_MODLIST
1151 * @param name The attribute name to add
1152 * @param val The value to add - NULL means DELETE
1153 * @return ADS STATUS indicating success of add
1155 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1156 const char *name, const char *val)
1158 const char *values[2];
1164 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1165 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1169 * Add an array of string values to a mod list
1170 * @param ctx An initialized TALLOC_CTX
1171 * @param mods An initialized ADS_MODLIST
1172 * @param name The attribute name to add
1173 * @param vals The array of string values to add - NULL means DELETE
1174 * @return ADS STATUS indicating success of add
1176 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1177 const char *name, const char **vals)
1180 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1181 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1182 name, (const void **) vals);
1187 * Add a single ber-encoded value to a mod list
1188 * @param ctx An initialized TALLOC_CTX
1189 * @param mods An initialized ADS_MODLIST
1190 * @param name The attribute name to add
1191 * @param val The value to add - NULL means DELETE
1192 * @return ADS STATUS indicating success of add
1194 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1195 const char *name, const struct berval *val)
1197 const struct berval *values[2];
1202 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1203 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1204 name, (const void **) values);
1209 * Perform an ldap modify
1210 * @param ads connection to ads server
1211 * @param mod_dn DistinguishedName to modify
1212 * @param mods list of modifications to perform
1213 * @return status of modify
1215 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1218 char *utf8_dn = NULL;
1220 this control is needed to modify that contains a currently
1221 non-existent attribute (but allowable for the object) to run
1223 LDAPControl PermitModify = {
1224 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1227 LDAPControl *controls[2];
1229 controls[0] = &PermitModify;
1232 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1233 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1236 /* find the end of the list, marked by NULL or -1 */
1237 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1238 /* make sure the end of the list is NULL */
1240 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1241 (LDAPMod **) mods, controls, NULL);
1243 return ADS_ERROR(ret);
1247 * Perform an ldap add
1248 * @param ads connection to ads server
1249 * @param new_dn DistinguishedName to add
1250 * @param mods list of attributes and values for DN
1251 * @return status of add
1253 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1256 char *utf8_dn = NULL;
1258 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1259 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1260 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1263 /* find the end of the list, marked by NULL or -1 */
1264 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1265 /* make sure the end of the list is NULL */
1268 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1270 return ADS_ERROR(ret);
1274 * Delete a DistinguishedName
1275 * @param ads connection to ads server
1276 * @param new_dn DistinguishedName to delete
1277 * @return status of delete
1279 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1282 char *utf8_dn = NULL;
1283 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1284 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1285 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1288 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1290 return ADS_ERROR(ret);
1294 * Build an org unit string
1295 * if org unit is Computers or blank then assume a container, otherwise
1296 * assume a / separated list of organisational units.
1297 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1298 * @param ads connection to ads server
1299 * @param org_unit Organizational unit
1300 * @return org unit string - caller must free
1302 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1306 if (!org_unit || !*org_unit) {
1308 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1310 /* samba4 might not yet respond to a wellknownobject-query */
1311 return ret ? ret : SMB_STRDUP("cn=Computers");
1314 if (strequal(org_unit, "Computers")) {
1315 return SMB_STRDUP("cn=Computers");
1318 /* jmcd: removed "\\" from the separation chars, because it is
1319 needed as an escape for chars like '#' which are valid in an
1321 return ads_build_path(org_unit, "/", "ou=", 1);
1325 * Get a org unit string for a well-known GUID
1326 * @param ads connection to ads server
1327 * @param wknguid Well known GUID
1328 * @return org unit string - caller must free
1330 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1333 LDAPMessage *res = NULL;
1334 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1335 **bind_dn_exp = NULL;
1336 const char *attrs[] = {"distinguishedName", NULL};
1337 int new_ln, wkn_ln, bind_ln, i;
1339 if (wknguid == NULL) {
1343 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1344 DEBUG(1, ("asprintf failed!\n"));
1348 status = ads_search_dn(ads, &res, base, attrs);
1349 if (!ADS_ERR_OK(status)) {
1350 DEBUG(1,("Failed while searching for: %s\n", base));
1354 if (ads_count_replies(ads, res) != 1) {
1358 /* substitute the bind-path from the well-known-guid-search result */
1359 wkn_dn = ads_get_dn(ads, res);
1364 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1369 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1374 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1376 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1379 new_ln = wkn_ln - bind_ln;
1381 ret = SMB_STRDUP(wkn_dn_exp[0]);
1386 for (i=1; i < new_ln; i++) {
1389 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1395 ret = SMB_STRDUP(s);
1404 ads_msgfree(ads, res);
1405 ads_memfree(ads, wkn_dn);
1407 ldap_value_free(wkn_dn_exp);
1410 ldap_value_free(bind_dn_exp);
1417 * Adds (appends) an item to an attribute array, rather then
1418 * replacing the whole list
1419 * @param ctx An initialized TALLOC_CTX
1420 * @param mods An initialized ADS_MODLIST
1421 * @param name name of the ldap attribute to append to
1422 * @param vals an array of values to add
1423 * @return status of addition
1426 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1427 const char *name, const char **vals)
1429 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1430 (const void *) vals);
1434 * Determines the computer account's current KVNO via an LDAP lookup
1435 * @param ads An initialized ADS_STRUCT
1436 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1437 * @return the kvno for the computer account, or -1 in case of a failure.
1440 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1442 LDAPMessage *res = NULL;
1443 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1445 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1446 char *dn_string = NULL;
1447 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1449 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1450 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1453 ret = ads_search(ads, &res, filter, attrs);
1455 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1456 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1457 ads_msgfree(ads, res);
1461 dn_string = ads_get_dn(ads, res);
1463 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1464 ads_msgfree(ads, res);
1467 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1468 ads_memfree(ads, dn_string);
1470 /* ---------------------------------------------------------
1471 * 0 is returned as a default KVNO from this point on...
1472 * This is done because Windows 2000 does not support key
1473 * version numbers. Chances are that a failure in the next
1474 * step is simply due to Windows 2000 being used for a
1475 * domain controller. */
1478 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1479 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1480 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1481 ads_msgfree(ads, res);
1486 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1487 ads_msgfree(ads, res);
1492 * This clears out all registered spn's for a given hostname
1493 * @param ads An initilaized ADS_STRUCT
1494 * @param machine_name the NetBIOS name of the computer.
1495 * @return 0 upon success, non-zero otherwise.
1498 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1501 LDAPMessage *res = NULL;
1503 const char *servicePrincipalName[1] = {NULL};
1504 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1505 char *dn_string = NULL;
1507 ret = ads_find_machine_acct(ads, &res, machine_name);
1508 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1509 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1510 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1511 ads_msgfree(ads, res);
1512 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1515 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1516 ctx = talloc_init("ads_clear_service_principal_names");
1518 ads_msgfree(ads, res);
1519 return ADS_ERROR(LDAP_NO_MEMORY);
1522 if (!(mods = ads_init_mods(ctx))) {
1523 talloc_destroy(ctx);
1524 ads_msgfree(ads, res);
1525 return ADS_ERROR(LDAP_NO_MEMORY);
1527 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1528 if (!ADS_ERR_OK(ret)) {
1529 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1530 ads_msgfree(ads, res);
1531 talloc_destroy(ctx);
1534 dn_string = ads_get_dn(ads, res);
1536 talloc_destroy(ctx);
1537 ads_msgfree(ads, res);
1538 return ADS_ERROR(LDAP_NO_MEMORY);
1540 ret = ads_gen_mod(ads, dn_string, mods);
1541 ads_memfree(ads,dn_string);
1542 if (!ADS_ERR_OK(ret)) {
1543 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1545 ads_msgfree(ads, res);
1546 talloc_destroy(ctx);
1550 ads_msgfree(ads, res);
1551 talloc_destroy(ctx);
1556 * This adds a service principal name to an existing computer account
1557 * (found by hostname) in AD.
1558 * @param ads An initialized ADS_STRUCT
1559 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1560 * @param my_fqdn The fully qualified DNS name of the machine
1561 * @param spn A string of the service principal to add, i.e. 'host'
1562 * @return 0 upon sucess, or non-zero if a failure occurs
1565 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1566 const char *my_fqdn, const char *spn)
1570 LDAPMessage *res = NULL;
1573 char *dn_string = NULL;
1574 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1576 ret = ads_find_machine_acct(ads, &res, machine_name);
1577 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1578 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1580 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1581 spn, machine_name, ads->config.realm));
1582 ads_msgfree(ads, res);
1583 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1586 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1587 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1588 ads_msgfree(ads, res);
1589 return ADS_ERROR(LDAP_NO_MEMORY);
1592 /* add short name spn */
1594 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1595 talloc_destroy(ctx);
1596 ads_msgfree(ads, res);
1597 return ADS_ERROR(LDAP_NO_MEMORY);
1600 strlower_m(&psp1[strlen(spn)]);
1601 servicePrincipalName[0] = psp1;
1603 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1604 psp1, machine_name));
1607 /* add fully qualified spn */
1609 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1610 ret = ADS_ERROR(LDAP_NO_MEMORY);
1614 strlower_m(&psp2[strlen(spn)]);
1615 servicePrincipalName[1] = psp2;
1617 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1618 psp2, machine_name));
1620 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1621 ret = ADS_ERROR(LDAP_NO_MEMORY);
1625 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1626 if (!ADS_ERR_OK(ret)) {
1627 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1631 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1632 ret = ADS_ERROR(LDAP_NO_MEMORY);
1636 ret = ads_gen_mod(ads, dn_string, mods);
1637 ads_memfree(ads,dn_string);
1638 if (!ADS_ERR_OK(ret)) {
1639 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1645 ads_msgfree(ads, res);
1650 * adds a machine account to the ADS server
1651 * @param ads An intialized ADS_STRUCT
1652 * @param machine_name - the NetBIOS machine name of this account.
1653 * @param account_type A number indicating the type of account to create
1654 * @param org_unit The LDAP path in which to place this account
1655 * @return 0 upon success, or non-zero otherwise
1658 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1659 const char *org_unit)
1662 char *samAccountName, *controlstr;
1665 char *machine_escaped = NULL;
1667 const char *objectClass[] = {"top", "person", "organizationalPerson",
1668 "user", "computer", NULL};
1669 LDAPMessage *res = NULL;
1670 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1671 UF_DONT_EXPIRE_PASSWD |\
1672 UF_ACCOUNTDISABLE );
1674 if (!(ctx = talloc_init("ads_add_machine_acct")))
1675 return ADS_ERROR(LDAP_NO_MEMORY);
1677 ret = ADS_ERROR(LDAP_NO_MEMORY);
1679 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1680 if (!machine_escaped) {
1684 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1685 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1687 if ( !new_dn || !samAccountName ) {
1691 #ifndef ENCTYPE_ARCFOUR_HMAC
1692 acct_control |= UF_USE_DES_KEY_ONLY;
1695 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1699 if (!(mods = ads_init_mods(ctx))) {
1703 ads_mod_str(ctx, &mods, "cn", machine_name);
1704 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1705 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1706 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1708 ret = ads_gen_add(ads, new_dn, mods);
1711 SAFE_FREE(machine_escaped);
1712 ads_msgfree(ads, res);
1713 talloc_destroy(ctx);
1719 * move a machine account to another OU on the ADS server
1720 * @param ads - An intialized ADS_STRUCT
1721 * @param machine_name - the NetBIOS machine name of this account.
1722 * @param org_unit - The LDAP path in which to place this account
1723 * @param moved - whether we moved the machine account (optional)
1724 * @return 0 upon success, or non-zero otherwise
1727 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1728 const char *org_unit, BOOL *moved)
1732 LDAPMessage *res = NULL;
1733 char *filter = NULL;
1734 char *computer_dn = NULL;
1736 char *computer_rdn = NULL;
1737 BOOL need_move = False;
1739 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1740 rc = ADS_ERROR(LDAP_NO_MEMORY);
1744 /* Find pre-existing machine */
1745 rc = ads_search(ads, &res, filter, NULL);
1746 if (!ADS_ERR_OK(rc)) {
1750 computer_dn = ads_get_dn(ads, res);
1752 rc = ADS_ERROR(LDAP_NO_MEMORY);
1756 parent_dn = ads_parent_dn(computer_dn);
1757 if (strequal(parent_dn, org_unit)) {
1763 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1764 rc = ADS_ERROR(LDAP_NO_MEMORY);
1768 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1769 org_unit, 1, NULL, NULL);
1770 rc = ADS_ERROR(ldap_status);
1773 ads_msgfree(ads, res);
1775 SAFE_FREE(computer_dn);
1776 SAFE_FREE(computer_rdn);
1778 if (!ADS_ERR_OK(rc)) {
1790 dump a binary result from ldap
1792 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1795 for (i=0; values[i]; i++) {
1796 printf("%s: ", field);
1797 for (j=0; j<values[i]->bv_len; j++) {
1798 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1804 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1808 for (i=0; values[i]; i++) {
1809 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1810 printf("%s: %s\n", field,
1811 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1816 dump a sid result from ldap
1818 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1821 for (i=0; values[i]; i++) {
1823 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1824 printf("%s: %s\n", field, sid_string_static(&sid));
1829 dump ntSecurityDescriptor
1831 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1836 TALLOC_CTX *ctx = 0;
1838 if (!(ctx = talloc_init("sec_io_desc")))
1842 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1843 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1844 prs_set_offset(&ps,0);
1847 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1849 talloc_destroy(ctx);
1853 ads_disp_sd(ads, ctx, psd);
1857 talloc_destroy(ctx);
1861 dump a string result from ldap
1863 static void dump_string(const char *field, char **values)
1866 for (i=0; values[i]; i++) {
1867 printf("%s: %s\n", field, values[i]);
1872 dump a field from LDAP on stdout
1876 static BOOL ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1881 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1883 {"objectGUID", False, dump_guid},
1884 {"netbootGUID", False, dump_guid},
1885 {"nTSecurityDescriptor", False, dump_sd},
1886 {"dnsRecord", False, dump_binary},
1887 {"objectSid", False, dump_sid},
1888 {"tokenGroups", False, dump_sid},
1889 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1890 {"tokengroupsGlobalandUniversal", False, dump_sid},
1891 {"mS-DS-CreatorSID", False, dump_sid},
1896 if (!field) { /* must be end of an entry */
1901 for (i=0; handlers[i].name; i++) {
1902 if (StrCaseCmp(handlers[i].name, field) == 0) {
1903 if (!values) /* first time, indicate string or not */
1904 return handlers[i].string;
1905 handlers[i].handler(ads, field, (struct berval **) values);
1909 if (!handlers[i].name) {
1910 if (!values) /* first time, indicate string conversion */
1912 dump_string(field, (char **)values);
1918 * Dump a result from LDAP on stdout
1919 * used for debugging
1920 * @param ads connection to ads server
1921 * @param res Results to dump
1924 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1926 ads_process_results(ads, res, ads_dump_field, NULL);
1930 * Walk through results, calling a function for each entry found.
1931 * The function receives a field name, a berval * array of values,
1932 * and a data area passed through from the start. The function is
1933 * called once with null for field and values at the end of each
1935 * @param ads connection to ads server
1936 * @param res Results to process
1937 * @param fn Function for processing each result
1938 * @param data_area user-defined area to pass to function
1940 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1941 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
1947 if (!(ctx = talloc_init("ads_process_results")))
1950 for (msg = ads_first_entry(ads, res); msg;
1951 msg = ads_next_entry(ads, msg)) {
1955 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1956 (LDAPMessage *)msg,&b);
1958 utf8_field=ldap_next_attribute(ads->ldap.ld,
1959 (LDAPMessage *)msg,b)) {
1960 struct berval **ber_vals;
1961 char **str_vals, **utf8_vals;
1965 pull_utf8_talloc(ctx, &field, utf8_field);
1966 string = fn(ads, field, NULL, data_area);
1969 utf8_vals = ldap_get_values(ads->ldap.ld,
1970 (LDAPMessage *)msg, field);
1971 str_vals = ads_pull_strvals(ctx,
1972 (const char **) utf8_vals);
1973 fn(ads, field, (void **) str_vals, data_area);
1974 ldap_value_free(utf8_vals);
1976 ber_vals = ldap_get_values_len(ads->ldap.ld,
1977 (LDAPMessage *)msg, field);
1978 fn(ads, field, (void **) ber_vals, data_area);
1980 ldap_value_free_len(ber_vals);
1982 ldap_memfree(utf8_field);
1985 talloc_free_children(ctx);
1986 fn(ads, NULL, NULL, data_area); /* completed an entry */
1989 talloc_destroy(ctx);
1993 * count how many replies are in a LDAPMessage
1994 * @param ads connection to ads server
1995 * @param res Results to count
1996 * @return number of replies
1998 int ads_count_replies(ADS_STRUCT *ads, void *res)
2000 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2004 * pull the first entry from a ADS result
2005 * @param ads connection to ads server
2006 * @param res Results of search
2007 * @return first entry from result
2009 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2011 return ldap_first_entry(ads->ldap.ld, res);
2015 * pull the next entry from a ADS result
2016 * @param ads connection to ads server
2017 * @param res Results of search
2018 * @return next entry from result
2020 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2022 return ldap_next_entry(ads->ldap.ld, res);
2026 * pull a single string from a ADS result
2027 * @param ads connection to ads server
2028 * @param mem_ctx TALLOC_CTX to use for allocating result string
2029 * @param msg Results of search
2030 * @param field Attribute to retrieve
2031 * @return Result string in talloc context
2033 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2041 values = ldap_get_values(ads->ldap.ld, msg, field);
2046 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2048 if (rc != (size_t)-1)
2052 ldap_value_free(values);
2057 * pull an array of strings from a ADS result
2058 * @param ads connection to ads server
2059 * @param mem_ctx TALLOC_CTX to use for allocating result string
2060 * @param msg Results of search
2061 * @param field Attribute to retrieve
2062 * @return Result strings in talloc context
2064 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2065 LDAPMessage *msg, const char *field,
2072 values = ldap_get_values(ads->ldap.ld, msg, field);
2076 *num_values = ldap_count_values(values);
2078 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2080 ldap_value_free(values);
2084 for (i=0;i<*num_values;i++) {
2085 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2086 ldap_value_free(values);
2092 ldap_value_free(values);
2097 * pull an array of strings from a ADS result
2098 * (handle large multivalue attributes with range retrieval)
2099 * @param ads connection to ads server
2100 * @param mem_ctx TALLOC_CTX to use for allocating result string
2101 * @param msg Results of search
2102 * @param field Attribute to retrieve
2103 * @param current_strings strings returned by a previous call to this function
2104 * @param next_attribute The next query should ask for this attribute
2105 * @param num_values How many values did we get this time?
2106 * @param more_values Are there more values to get?
2107 * @return Result strings in talloc context
2109 char **ads_pull_strings_range(ADS_STRUCT *ads,
2110 TALLOC_CTX *mem_ctx,
2111 LDAPMessage *msg, const char *field,
2112 char **current_strings,
2113 const char **next_attribute,
2114 size_t *num_strings,
2118 char *expected_range_attrib, *range_attr;
2119 BerElement *ptr = NULL;
2122 size_t num_new_strings;
2123 unsigned long int range_start;
2124 unsigned long int range_end;
2126 /* we might have been given the whole lot anyway */
2127 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2128 *more_strings = False;
2132 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2134 /* look for Range result */
2135 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2137 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2138 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2139 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2147 /* nothing here - this field is just empty */
2148 *more_strings = False;
2152 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2153 &range_start, &range_end) == 2) {
2154 *more_strings = True;
2156 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2157 &range_start) == 1) {
2158 *more_strings = False;
2160 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2162 ldap_memfree(range_attr);
2163 *more_strings = False;
2168 if ((*num_strings) != range_start) {
2169 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2170 " - aborting range retreival\n",
2171 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2172 ldap_memfree(range_attr);
2173 *more_strings = False;
2177 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2179 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2180 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2181 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2182 range_attr, (unsigned long int)range_end - range_start + 1,
2183 (unsigned long int)num_new_strings));
2184 ldap_memfree(range_attr);
2185 *more_strings = False;
2189 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2190 *num_strings + num_new_strings);
2192 if (strings == NULL) {
2193 ldap_memfree(range_attr);
2194 *more_strings = False;
2198 if (new_strings && num_new_strings) {
2199 memcpy(&strings[*num_strings], new_strings,
2200 sizeof(*new_strings) * num_new_strings);
2203 (*num_strings) += num_new_strings;
2205 if (*more_strings) {
2206 *next_attribute = talloc_asprintf(mem_ctx,
2211 if (!*next_attribute) {
2212 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2213 ldap_memfree(range_attr);
2214 *more_strings = False;
2219 ldap_memfree(range_attr);
2225 * pull a single uint32 from a ADS result
2226 * @param ads connection to ads server
2227 * @param msg Results of search
2228 * @param field Attribute to retrieve
2229 * @param v Pointer to int to store result
2230 * @return boolean inidicating success
2232 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2237 values = ldap_get_values(ads->ldap.ld, msg, field);
2241 ldap_value_free(values);
2245 *v = atoi(values[0]);
2246 ldap_value_free(values);
2251 * pull a single objectGUID from an ADS result
2252 * @param ads connection to ADS server
2253 * @param msg results of search
2254 * @param guid 37-byte area to receive text guid
2255 * @return boolean indicating success
2257 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2260 UUID_FLAT flat_guid;
2262 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2267 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2268 smb_uuid_unpack(flat_guid, guid);
2269 ldap_value_free(values);
2272 ldap_value_free(values);
2279 * pull a single DOM_SID from a ADS result
2280 * @param ads connection to ads server
2281 * @param msg Results of search
2282 * @param field Attribute to retrieve
2283 * @param sid Pointer to sid to store result
2284 * @return boolean inidicating success
2286 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2289 struct berval **values;
2292 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2298 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2300 ldap_value_free_len(values);
2305 * pull an array of DOM_SIDs from a ADS result
2306 * @param ads connection to ads server
2307 * @param mem_ctx TALLOC_CTX for allocating sid array
2308 * @param msg Results of search
2309 * @param field Attribute to retrieve
2310 * @param sids pointer to sid array to allocate
2311 * @return the count of SIDs pulled
2313 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2314 LDAPMessage *msg, const char *field, DOM_SID **sids)
2316 struct berval **values;
2320 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2325 for (i=0; values[i]; i++)
2329 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2331 ldap_value_free_len(values);
2339 for (i=0; values[i]; i++) {
2340 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2343 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2348 ldap_value_free_len(values);
2353 * pull a SEC_DESC from a ADS result
2354 * @param ads connection to ads server
2355 * @param mem_ctx TALLOC_CTX for allocating sid array
2356 * @param msg Results of search
2357 * @param field Attribute to retrieve
2358 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2359 * @return boolean inidicating success
2361 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2362 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2364 struct berval **values;
2367 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2369 if (!values) return False;
2373 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2374 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2375 prs_set_offset(&ps,0);
2377 ret = sec_io_desc("sd", sd, &ps, 1);
2381 ldap_value_free_len(values);
2386 * in order to support usernames longer than 21 characters we need to
2387 * use both the sAMAccountName and the userPrincipalName attributes
2388 * It seems that not all users have the userPrincipalName attribute set
2390 * @param ads connection to ads server
2391 * @param mem_ctx TALLOC_CTX for allocating sid array
2392 * @param msg Results of search
2393 * @return the username
2395 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2401 /* lookup_name() only works on the sAMAccountName to
2402 returning the username portion of userPrincipalName
2403 breaks winbindd_getpwnam() */
2405 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2406 if (ret && (p = strchr_m(ret, '@'))) {
2411 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2416 * find the update serial number - this is the core of the ldap cache
2417 * @param ads connection to ads server
2418 * @param ads connection to ADS server
2419 * @param usn Pointer to retrieved update serial number
2420 * @return status of search
2422 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2424 const char *attrs[] = {"highestCommittedUSN", NULL};
2428 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2429 if (!ADS_ERR_OK(status))
2432 if (ads_count_replies(ads, res) != 1) {
2433 ads_msgfree(ads, res);
2434 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2437 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2438 ads_msgfree(ads, res);
2439 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2442 ads_msgfree(ads, res);
2446 /* parse a ADS timestring - typical string is
2447 '20020917091222.0Z0' which means 09:12.22 17th September
2449 static time_t ads_parse_time(const char *str)
2455 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2456 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2457 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2466 /********************************************************************
2467 ********************************************************************/
2469 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2471 const char *attrs[] = {"currentTime", NULL};
2476 ADS_STRUCT *ads_s = ads;
2478 if (!(ctx = talloc_init("ads_current_time"))) {
2479 return ADS_ERROR(LDAP_NO_MEMORY);
2482 /* establish a new ldap tcp session if necessary */
2484 if ( !ads->ldap.ld ) {
2485 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2486 ads->server.ldap_server )) == NULL )
2490 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2491 status = ads_connect( ads_s );
2492 if ( !ADS_ERR_OK(status))
2496 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2497 if (!ADS_ERR_OK(status)) {
2501 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2503 ads_msgfree(ads_s, res);
2504 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2508 /* but save the time and offset in the original ADS_STRUCT */
2510 ads->config.current_time = ads_parse_time(timestr);
2512 if (ads->config.current_time != 0) {
2513 ads->auth.time_offset = ads->config.current_time - time(NULL);
2514 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2517 ads_msgfree(ads, res);
2519 status = ADS_SUCCESS;
2522 /* free any temporary ads connections */
2523 if ( ads_s != ads ) {
2524 ads_destroy( &ads_s );
2526 talloc_destroy(ctx);
2531 /********************************************************************
2532 ********************************************************************/
2534 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2536 const char *attrs[] = {"domainFunctionality", NULL};
2539 ADS_STRUCT *ads_s = ads;
2541 *val = DS_DOMAIN_FUNCTION_2000;
2543 /* establish a new ldap tcp session if necessary */
2545 if ( !ads->ldap.ld ) {
2546 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2547 ads->server.ldap_server )) == NULL )
2551 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2552 status = ads_connect( ads_s );
2553 if ( !ADS_ERR_OK(status))
2557 /* If the attribute does not exist assume it is a Windows 2000
2558 functional domain */
2560 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2561 if (!ADS_ERR_OK(status)) {
2562 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2563 status = ADS_SUCCESS;
2568 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2569 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2571 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2574 ads_msgfree(ads, res);
2577 /* free any temporary ads connections */
2578 if ( ads_s != ads ) {
2579 ads_destroy( &ads_s );
2586 * find the domain sid for our domain
2587 * @param ads connection to ads server
2588 * @param sid Pointer to domain sid
2589 * @return status of search
2591 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2593 const char *attrs[] = {"objectSid", NULL};
2597 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2599 if (!ADS_ERR_OK(rc)) return rc;
2600 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2601 ads_msgfree(ads, res);
2602 return ADS_ERROR_SYSTEM(ENOENT);
2604 ads_msgfree(ads, res);
2610 * find our site name
2611 * @param ads connection to ads server
2612 * @param mem_ctx Pointer to talloc context
2613 * @param site_name Pointer to the sitename
2614 * @return status of search
2616 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2620 const char *dn, *service_name;
2621 const char *attrs[] = { "dsServiceName", NULL };
2623 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2624 if (!ADS_ERR_OK(status)) {
2628 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2629 if (service_name == NULL) {
2630 ads_msgfree(ads, res);
2631 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2634 ads_msgfree(ads, res);
2636 /* go up three levels */
2637 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2639 return ADS_ERROR(LDAP_NO_MEMORY);
2642 *site_name = talloc_strdup(mem_ctx, dn);
2643 if (*site_name == NULL) {
2644 return ADS_ERROR(LDAP_NO_MEMORY);
2649 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2654 * find the site dn where a machine resides
2655 * @param ads connection to ads server
2656 * @param mem_ctx Pointer to talloc context
2657 * @param computer_name name of the machine
2658 * @param site_name Pointer to the sitename
2659 * @return status of search
2661 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2665 const char *parent, *filter;
2666 char *config_context = NULL;
2669 /* shortcut a query */
2670 if (strequal(computer_name, ads->config.ldap_server_name)) {
2671 return ads_site_dn(ads, mem_ctx, site_dn);
2674 status = ads_config_path(ads, mem_ctx, &config_context);
2675 if (!ADS_ERR_OK(status)) {
2679 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2680 if (filter == NULL) {
2681 return ADS_ERROR(LDAP_NO_MEMORY);
2684 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2685 filter, NULL, &res);
2686 if (!ADS_ERR_OK(status)) {
2690 if (ads_count_replies(ads, res) != 1) {
2691 ads_msgfree(ads, res);
2692 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2695 dn = ads_get_dn(ads, res);
2697 ads_msgfree(ads, res);
2698 return ADS_ERROR(LDAP_NO_MEMORY);
2701 /* go up three levels */
2702 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2703 if (parent == NULL) {
2704 ads_msgfree(ads, res);
2705 ads_memfree(ads, dn);
2706 return ADS_ERROR(LDAP_NO_MEMORY);
2709 *site_dn = talloc_strdup(mem_ctx, parent);
2710 if (*site_dn == NULL) {
2711 ads_msgfree(ads, res);
2712 ads_memfree(ads, dn);
2713 ADS_ERROR(LDAP_NO_MEMORY);
2716 ads_memfree(ads, dn);
2717 ads_msgfree(ads, res);
2723 * get the upn suffixes for a domain
2724 * @param ads connection to ads server
2725 * @param mem_ctx Pointer to talloc context
2726 * @param suffixes Pointer to an array of suffixes
2727 * @param num_suffixes Pointer to the number of suffixes
2728 * @return status of search
2730 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2735 char *config_context = NULL;
2736 const char *attrs[] = { "uPNSuffixes", NULL };
2738 status = ads_config_path(ads, mem_ctx, &config_context);
2739 if (!ADS_ERR_OK(status)) {
2743 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2745 return ADS_ERROR(LDAP_NO_MEMORY);
2748 status = ads_search_dn(ads, &res, base, attrs);
2749 if (!ADS_ERR_OK(status)) {
2753 if (ads_count_replies(ads, res) != 1) {
2754 ads_msgfree(ads, res);
2755 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2758 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2759 if ((*suffixes) == NULL) {
2760 ads_msgfree(ads, res);
2761 return ADS_ERROR(LDAP_NO_MEMORY);
2764 ads_msgfree(ads, res);
2770 * pull a DOM_SID from an extended dn string
2771 * @param mem_ctx TALLOC_CTX
2772 * @param extended_dn string
2773 * @param flags string type of extended_dn
2774 * @param sid pointer to a DOM_SID
2775 * @return boolean inidicating success
2777 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2778 const char *extended_dn,
2779 enum ads_extended_dn_flags flags,
2788 /* otherwise extended_dn gets stripped off */
2789 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2793 * ADS_EXTENDED_DN_HEX_STRING:
2794 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2796 * ADS_EXTENDED_DN_STRING (only with w2k3):
2797 <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
2800 p = strchr(dn, ';');
2805 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2809 p += strlen(";<SID=");
2818 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2822 case ADS_EXTENDED_DN_STRING:
2823 if (!string_to_sid(sid, p)) {
2827 case ADS_EXTENDED_DN_HEX_STRING: {
2831 buf_len = strhex_to_str(buf, strlen(p), p);
2836 if (!sid_parse(buf, buf_len, sid)) {
2837 DEBUG(10,("failed to parse sid\n"));
2843 DEBUG(10,("unknown extended dn format\n"));
2851 * pull an array of DOM_SIDs from a ADS result
2852 * @param ads connection to ads server
2853 * @param mem_ctx TALLOC_CTX for allocating sid array
2854 * @param msg Results of search
2855 * @param field Attribute to retrieve
2856 * @param flags string type of extended_dn
2857 * @param sids pointer to sid array to allocate
2858 * @return the count of SIDs pulled
2860 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2861 TALLOC_CTX *mem_ctx,
2864 enum ads_extended_dn_flags flags,
2871 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2872 &dn_count)) == NULL) {
2876 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2878 TALLOC_FREE(dn_strings);
2882 for (i=0; i<dn_count; i++) {
2884 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2885 flags, &(*sids)[i])) {
2887 TALLOC_FREE(dn_strings);
2892 TALLOC_FREE(dn_strings);
2897 /********************************************************************
2898 ********************************************************************/
2900 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2902 LDAPMessage *res = NULL;
2907 status = ads_find_machine_acct(ads, &res, global_myname());
2908 if (!ADS_ERR_OK(status)) {
2909 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2914 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2915 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2919 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2920 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2924 ads_msgfree(ads, res);
2929 /********************************************************************
2930 ********************************************************************/
2932 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2934 LDAPMessage *res = NULL;
2939 status = ads_find_machine_acct(ads, &res, global_myname());
2940 if (!ADS_ERR_OK(status)) {
2941 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2946 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2947 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2951 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2952 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2956 ads_msgfree(ads, res);
2961 /********************************************************************
2962 ********************************************************************/
2964 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2966 LDAPMessage *res = NULL;
2971 status = ads_find_machine_acct(ads, &res, global_myname());
2972 if (!ADS_ERR_OK(status)) {
2973 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2978 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2979 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2983 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2984 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2988 ads_msgfree(ads, res);
2995 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2998 * Join a machine to a realm
2999 * Creates the machine account and sets the machine password
3000 * @param ads connection to ads server
3001 * @param machine name of host to add
3002 * @param org_unit Organizational unit to place machine in
3003 * @return status of join
3005 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3006 uint32 account_type, const char *org_unit)
3009 LDAPMessage *res = NULL;
3012 /* machine name must be lowercase */
3013 machine = SMB_STRDUP(machine_name);
3014 strlower_m(machine);
3017 status = ads_find_machine_acct(ads, (void **)&res, machine);
3018 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3019 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3020 status = ads_leave_realm(ads, machine);
3021 if (!ADS_ERR_OK(status)) {
3022 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3023 machine, ads->config.realm));
3028 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3029 if (!ADS_ERR_OK(status)) {
3030 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3035 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3036 if (!ADS_ERR_OK(status)) {
3037 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3043 ads_msgfree(ads, res);
3050 * Delete a machine from the realm
3051 * @param ads connection to ads server
3052 * @param hostname Machine to remove
3053 * @return status of delete
3055 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3060 char *hostnameDN, *host;
3062 LDAPControl ldap_control;
3063 LDAPControl * pldap_control[2] = {NULL, NULL};
3065 pldap_control[0] = &ldap_control;
3066 memset(&ldap_control, 0, sizeof(LDAPControl));
3067 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3069 /* hostname must be lowercase */
3070 host = SMB_STRDUP(hostname);
3073 status = ads_find_machine_acct(ads, &res, host);
3074 if (!ADS_ERR_OK(status)) {
3075 DEBUG(0, ("Host account for %s does not exist.\n", host));
3080 msg = ads_first_entry(ads, res);
3083 return ADS_ERROR_SYSTEM(ENOENT);
3086 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3088 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3090 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3092 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3095 if (rc != LDAP_SUCCESS) {
3096 const char *attrs[] = { "cn", NULL };
3097 LDAPMessage *msg_sub;
3099 /* we only search with scope ONE, we do not expect any further
3100 * objects to be created deeper */
3102 status = ads_do_search_retry(ads, hostnameDN,
3103 LDAP_SCOPE_ONELEVEL,
3104 "(objectclass=*)", attrs, &res);
3106 if (!ADS_ERR_OK(status)) {
3108 ads_memfree(ads, hostnameDN);
3112 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3113 msg_sub = ads_next_entry(ads, msg_sub)) {
3117 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3119 ads_memfree(ads, hostnameDN);
3120 return ADS_ERROR(LDAP_NO_MEMORY);
3123 status = ads_del_dn(ads, dn);
3124 if (!ADS_ERR_OK(status)) {
3125 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3127 ads_memfree(ads, dn);
3128 ads_memfree(ads, hostnameDN);
3132 ads_memfree(ads, dn);
3135 /* there should be no subordinate objects anymore */
3136 status = ads_do_search_retry(ads, hostnameDN,
3137 LDAP_SCOPE_ONELEVEL,
3138 "(objectclass=*)", attrs, &res);
3140 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3142 ads_memfree(ads, hostnameDN);
3146 /* delete hostnameDN now */
3147 status = ads_del_dn(ads, hostnameDN);
3148 if (!ADS_ERR_OK(status)) {
3150 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3151 ads_memfree(ads, hostnameDN);
3156 ads_memfree(ads, hostnameDN);
3158 status = ads_find_machine_acct(ads, &res, host);
3159 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3160 DEBUG(3, ("Failed to remove host account.\n"));
3170 * pull all token-sids from an LDAP dn
3171 * @param ads connection to ads server
3172 * @param mem_ctx TALLOC_CTX for allocating sid array
3173 * @param dn of LDAP object
3174 * @param user_sid pointer to DOM_SID (objectSid)
3175 * @param primary_group_sid pointer to DOM_SID (self composed)
3176 * @param sids pointer to sid array to allocate
3177 * @param num_sids counter of SIDs pulled
3178 * @return status of token query
3180 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3181 TALLOC_CTX *mem_ctx,
3184 DOM_SID *primary_group_sid,
3189 LDAPMessage *res = NULL;
3191 size_t tmp_num_sids;
3193 DOM_SID tmp_user_sid;
3194 DOM_SID tmp_primary_group_sid;
3196 const char *attrs[] = {
3203 status = ads_search_retry_dn(ads, &res, dn, attrs);
3204 if (!ADS_ERR_OK(status)) {
3208 count = ads_count_replies(ads, res);
3210 ads_msgfree(ads, res);
3211 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3214 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3215 ads_msgfree(ads, res);
3216 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3219 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3220 ads_msgfree(ads, res);
3221 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3225 /* hack to compose the primary group sid without knowing the
3231 sid_copy(&domsid, &tmp_user_sid);
3233 if (!sid_split_rid(&domsid, &dummy_rid)) {
3234 ads_msgfree(ads, res);
3235 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3238 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3239 ads_msgfree(ads, res);
3240 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3244 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3246 if (tmp_num_sids == 0 || !tmp_sids) {
3247 ads_msgfree(ads, res);
3248 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3252 *num_sids = tmp_num_sids;
3260 *user_sid = tmp_user_sid;
3263 if (primary_group_sid) {
3264 *primary_group_sid = tmp_primary_group_sid;
3267 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3269 ads_msgfree(ads, res);
3270 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3274 * Find a sAMAccoutName in LDAP
3275 * @param ads connection to ads server
3276 * @param mem_ctx TALLOC_CTX for allocating sid array
3277 * @param samaccountname to search
3278 * @param uac_ret uint32 pointer userAccountControl attribute value
3279 * @param dn_ret pointer to dn
3280 * @return status of token query
3282 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3283 TALLOC_CTX *mem_ctx,
3284 const char *samaccountname,
3286 const char **dn_ret)
3289 const char *attrs[] = { "userAccountControl", NULL };
3291 LDAPMessage *res = NULL;
3295 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3297 if (filter == NULL) {
3301 status = ads_do_search_all(ads, ads->config.bind_path,
3303 filter, attrs, &res);
3305 if (!ADS_ERR_OK(status)) {
3309 if (ads_count_replies(ads, res) != 1) {
3310 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3314 dn = ads_get_dn(ads, res);
3316 status = ADS_ERROR(LDAP_NO_MEMORY);
3320 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3321 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3330 *dn_ret = talloc_strdup(mem_ctx, dn);
3332 status = ADS_ERROR(LDAP_NO_MEMORY);
3337 ads_memfree(ads, dn);
3338 ads_msgfree(ads, res);
3344 * find our configuration path
3345 * @param ads connection to ads server
3346 * @param mem_ctx Pointer to talloc context
3347 * @param config_path Pointer to the config path
3348 * @return status of search
3350 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3351 TALLOC_CTX *mem_ctx,
3355 LDAPMessage *res = NULL;
3356 const char *config_context = NULL;
3357 const char *attrs[] = { "configurationNamingContext", NULL };
3359 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3360 "(objectclass=*)", attrs, &res);
3361 if (!ADS_ERR_OK(status)) {
3365 config_context = ads_pull_string(ads, mem_ctx, res,
3366 "configurationNamingContext");
3367 ads_msgfree(ads, res);
3368 if (!config_context) {
3369 return ADS_ERROR(LDAP_NO_MEMORY);
3373 *config_path = talloc_strdup(mem_ctx, config_context);
3374 if (!*config_path) {
3375 return ADS_ERROR(LDAP_NO_MEMORY);
3379 return ADS_ERROR(LDAP_SUCCESS);
3383 * find the displayName of an extended right
3384 * @param ads connection to ads server
3385 * @param config_path The config path
3386 * @param mem_ctx Pointer to talloc context
3387 * @param GUID struct of the rightsGUID
3388 * @return status of search
3390 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3391 const char *config_path,
3392 TALLOC_CTX *mem_ctx,
3393 const struct GUID *rights_guid)
3396 LDAPMessage *res = NULL;
3398 const char *attrs[] = { "displayName", NULL };
3399 const char *result = NULL;
3402 if (!ads || !mem_ctx || !rights_guid) {
3406 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3407 smb_uuid_string_static(*rights_guid));
3412 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3417 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3419 if (!ADS_ERR_OK(rc)) {
3423 if (ads_count_replies(ads, res) != 1) {
3427 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3430 ads_msgfree(ads, res);