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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server, port, strerror(errno)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
80 static int ldap_search_with_timeout(LDAP *ld,
81 LDAP_CONST char *base,
83 LDAP_CONST char *filter,
91 struct timeval timeout;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout.tv_sec = lp_ldap_timeout();
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
100 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105 attrsonly, sctrls, cctrls, &timeout,
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
113 return LDAP_TIMELIMIT_EXCEEDED;
118 /**********************************************
119 Do client and server sitename match ?
120 **********************************************/
122 BOOL ads_sitename_match(ADS_STRUCT *ads)
124 if (ads->config.server_site_name == NULL &&
125 ads->config.client_site_name == NULL ) {
126 DEBUG(10,("ads_sitename_match: both null\n"));
129 if (ads->config.server_site_name &&
130 ads->config.client_site_name &&
131 strequal(ads->config.server_site_name,
132 ads->config.client_site_name)) {
133 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
136 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
137 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
138 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
142 /**********************************************
143 Is this the closest DC ?
144 **********************************************/
146 BOOL ads_closest_dc(ADS_STRUCT *ads)
148 if (ads->config.flags & ADS_CLOSEST) {
149 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
153 /* not sure if this can ever happen */
154 if (ads_sitename_match(ads)) {
155 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
159 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
160 ads->config.ldap_server_name));
167 try a connection to a given ldap server, returning True and setting the servers IP
168 in the ads struct if successful
170 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
173 struct cldap_netlogon_reply cldap_reply;
175 if (!server || !*server) {
179 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
180 server, ads->server.realm));
182 /* this copes with inet_ntoa brokenness */
184 srv = SMB_STRDUP(server);
186 ZERO_STRUCT( cldap_reply );
188 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
189 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
194 /* Check the CLDAP reply flags */
196 if ( !(cldap_reply.flags & ADS_LDAP) ) {
197 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
203 /* Fill in the ads->config values */
205 SAFE_FREE(ads->config.realm);
206 SAFE_FREE(ads->config.bind_path);
207 SAFE_FREE(ads->config.ldap_server_name);
208 SAFE_FREE(ads->config.server_site_name);
209 SAFE_FREE(ads->config.client_site_name);
210 SAFE_FREE(ads->server.workgroup);
212 ads->config.flags = cldap_reply.flags;
213 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
214 strupper_m(cldap_reply.domain);
215 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
216 ads->config.bind_path = ads_build_dn(ads->config.realm);
217 if (*cldap_reply.server_site_name) {
218 ads->config.server_site_name =
219 SMB_STRDUP(cldap_reply.server_site_name);
221 if (*cldap_reply.client_site_name) {
222 ads->config.client_site_name =
223 SMB_STRDUP(cldap_reply.client_site_name);
226 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
228 ads->ldap_port = LDAP_PORT;
229 ads->ldap_ip = *interpret_addr2(srv);
232 /* Store our site name. */
233 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
238 /**********************************************************************
239 Try to find an AD dc using our internal name resolution routines
240 Try the realm first and then then workgroup name if netbios is not
242 **********************************************************************/
244 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
248 struct ip_service *ip_list;
250 BOOL got_realm = False;
251 BOOL use_own_domain = False;
253 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
255 /* if the realm and workgroup are both empty, assume they are ours */
258 c_realm = ads->server.realm;
260 if ( !c_realm || !*c_realm ) {
261 /* special case where no realm and no workgroup means our own */
262 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
263 use_own_domain = True;
264 c_realm = lp_realm();
268 if (c_realm && *c_realm)
271 /* we need to try once with the realm name and fallback to the
272 netbios domain name if we fail (if netbios has not been disabled */
274 if ( !got_realm && !lp_disable_netbios() ) {
275 c_realm = ads->server.workgroup;
276 if (!c_realm || !*c_realm) {
277 if ( use_own_domain )
278 c_realm = lp_workgroup();
281 if ( !c_realm || !*c_realm ) {
282 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
283 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
287 pstrcpy( realm, c_realm );
289 sitename = sitename_fetch(realm);
293 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
294 (got_realm ? "realm" : "domain"), realm));
296 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
297 if (!NT_STATUS_IS_OK(status)) {
298 /* fall back to netbios if we can */
299 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, ExtendedDn, *controls[4], **rcontrols;
573 BerElement *cookie_be = NULL;
574 struct berval *cookie_bv= NULL;
575 BerElement *extdn_be = NULL;
576 struct berval *extdn_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))) {
607 /* Paged results only available on ldap v3 or later */
608 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
609 if (version < LDAP_VERSION3) {
610 rc = LDAP_NOT_SUPPORTED;
614 cookie_be = ber_alloc_t(LBER_USE_DER);
616 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
617 ber_bvfree(*cookie); /* don't need it from last time */
620 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
622 ber_flatten(cookie_be, &cookie_bv);
623 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
624 PagedResults.ldctl_iscritical = (char) 1;
625 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
626 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
628 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
629 NoReferrals.ldctl_iscritical = (char) 0;
630 NoReferrals.ldctl_value.bv_len = 0;
631 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
633 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
635 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
636 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
638 /* win2k does not accept a ldctl_value beeing passed in */
640 if (external_control->val != 0) {
642 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
647 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
651 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
656 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
657 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
660 ExtendedDn.ldctl_value.bv_len = 0;
661 ExtendedDn.ldctl_value.bv_val = NULL;
664 controls[0] = &NoReferrals;
665 controls[1] = &PagedResults;
666 controls[2] = &ExtendedDn;
670 controls[0] = &NoReferrals;
671 controls[1] = &PagedResults;
675 /* we need to disable referrals as the openldap libs don't
676 handle them and paged results at the same time. Using them
677 together results in the result record containing the server
678 page control being removed from the result list (tridge/jmcd)
680 leaving this in despite the control that says don't generate
681 referrals, in case the server doesn't support it (jmcd)
683 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
685 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
686 search_attrs, 0, controls,
688 (LDAPMessage **)res);
690 ber_free(cookie_be, 1);
691 ber_bvfree(cookie_bv);
694 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
695 ldap_err2string(rc)));
699 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
700 NULL, &rcontrols, 0);
706 for (i=0; rcontrols[i]; i++) {
707 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
708 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
709 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
711 /* the berval is the cookie, but must be freed when
713 if (cookie_bv->bv_len) /* still more to do */
714 *cookie=ber_bvdup(cookie_bv);
717 ber_bvfree(cookie_bv);
718 ber_free(cookie_be, 1);
722 ldap_controls_free(rcontrols);
728 ber_free(extdn_be, 1);
732 ber_bvfree(extdn_bv);
735 /* if/when we decide to utf8-encode attrs, take out this next line */
736 str_list_free(&search_attrs);
738 return ADS_ERROR(rc);
741 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
742 int scope, const char *expr,
743 const char **attrs, LDAPMessage **res,
744 int *count, struct berval **cookie)
746 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
751 * Get all results for a search. This uses ads_do_paged_search() to return
752 * all entries in a large search.
753 * @param ads connection to ads server
754 * @param bind_path Base dn for the search
755 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
756 * @param expr Search expression
757 * @param attrs Attributes to retrieve
758 * @param res ** which will contain results - free res* with ads_msgfree()
759 * @return status of search
761 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
762 int scope, const char *expr,
763 const char **attrs, void *args,
766 struct berval *cookie = NULL;
771 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
774 if (!ADS_ERR_OK(status))
777 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
779 LDAPMessage *res2 = NULL;
781 LDAPMessage *msg, *next;
783 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
784 attrs, args, &res2, &count, &cookie);
786 if (!ADS_ERR_OK(status2)) break;
788 /* this relies on the way that ldap_add_result_entry() works internally. I hope
789 that this works on all ldap libs, but I have only tested with openldap */
790 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
791 next = ads_next_entry(ads, msg);
792 ldap_add_result_entry((LDAPMessage **)res, msg);
794 /* note that we do not free res2, as the memory is now
795 part of the main returned list */
798 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
799 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
805 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
806 int scope, const char *expr,
807 const char **attrs, LDAPMessage **res)
809 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
813 * Run a function on all results for a search. Uses ads_do_paged_search() and
814 * runs the function as each page is returned, using ads_process_results()
815 * @param ads connection to ads server
816 * @param bind_path Base dn for the search
817 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
818 * @param expr Search expression - specified in local charset
819 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
820 * @param fn Function which takes attr name, values list, and data_area
821 * @param data_area Pointer which is passed to function on each call
822 * @return status of search
824 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
825 int scope, const char *expr, const char **attrs,
826 BOOL(*fn)(char *, void **, void *),
829 struct berval *cookie = NULL;
834 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
837 if (!ADS_ERR_OK(status)) return status;
839 ads_process_results(ads, res, fn, data_area);
840 ads_msgfree(ads, res);
843 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
844 &res, &count, &cookie);
846 if (!ADS_ERR_OK(status)) break;
848 ads_process_results(ads, res, fn, data_area);
849 ads_msgfree(ads, res);
856 * Do a search with a timeout.
857 * @param ads connection to ads server
858 * @param bind_path Base dn for the search
859 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
860 * @param expr Search expression
861 * @param attrs Attributes to retrieve
862 * @param res ** which will contain results - free res* with ads_msgfree()
863 * @return status of search
865 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
867 const char **attrs, LDAPMessage **res)
870 char *utf8_expr, *utf8_path, **search_attrs = NULL;
874 if (!(ctx = talloc_init("ads_do_search"))) {
875 DEBUG(1,("ads_do_search: talloc_init() failed!"));
876 return ADS_ERROR(LDAP_NO_MEMORY);
879 /* 0 means the conversion worked but the result was empty
880 so we only fail if it's negative. In any case, it always
881 at least nulls out the dest */
882 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
883 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
884 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
889 if (!attrs || !(*attrs))
892 /* This would be the utf8-encoded version...*/
893 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
894 if (!(str_list_copy(&search_attrs, attrs)))
896 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
902 /* see the note in ads_do_paged_search - we *must* disable referrals */
903 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
905 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
906 search_attrs, 0, NULL, NULL,
908 (LDAPMessage **)res);
910 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
911 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
917 /* if/when we decide to utf8-encode attrs, take out this next line */
918 str_list_free(&search_attrs);
919 return ADS_ERROR(rc);
922 * Do a general ADS search
923 * @param ads connection to ads server
924 * @param res ** which will contain results - free res* with ads_msgfree()
925 * @param expr Search expression
926 * @param attrs Attributes to retrieve
927 * @return status of search
929 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
930 const char *expr, const char **attrs)
932 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
937 * Do a search on a specific DistinguishedName
938 * @param ads connection to ads server
939 * @param res ** which will contain results - free res* with ads_msgfree()
940 * @param dn DistinguishName to search
941 * @param attrs Attributes to retrieve
942 * @return status of search
944 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
945 const char *dn, const char **attrs)
947 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
952 * Free up memory from a ads_search
953 * @param ads connection to ads server
954 * @param msg Search results to free
956 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
963 * Free up memory from various ads requests
964 * @param ads connection to ads server
965 * @param mem Area to free
967 void ads_memfree(ADS_STRUCT *ads, void *mem)
973 * Get a dn from search results
974 * @param ads connection to ads server
975 * @param msg Search result
978 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
980 char *utf8_dn, *unix_dn;
982 utf8_dn = ldap_get_dn(ads->ld, msg);
985 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
989 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
990 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
994 ldap_memfree(utf8_dn);
999 * Get the parent from a dn
1000 * @param dn the dn to return the parent from
1001 * @return parent dn string
1003 char *ads_parent_dn(const char *dn)
1011 p = strchr(dn, ',');
1021 * Find a machine account given a hostname
1022 * @param ads connection to ads server
1023 * @param res ** which will contain results - free res* with ads_msgfree()
1024 * @param host Hostname to search for
1025 * @return status of search
1027 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1028 const char *machine)
1032 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1036 /* the easiest way to find a machine account anywhere in the tree
1037 is to look for hostname$ */
1038 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1039 DEBUG(1, ("asprintf failed!\n"));
1040 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1043 status = ads_search(ads, res, expr, attrs);
1049 * Initialize a list of mods to be used in a modify request
1050 * @param ctx An initialized TALLOC_CTX
1051 * @return allocated ADS_MODLIST
1053 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1055 #define ADS_MODLIST_ALLOC_SIZE 10
1058 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1059 /* -1 is safety to make sure we don't go over the end.
1060 need to reset it to NULL before doing ldap modify */
1061 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1063 return (ADS_MODLIST)mods;
1068 add an attribute to the list, with values list already constructed
1070 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1071 int mod_op, const char *name,
1072 const void *_invals)
1074 const void **invals = (const void **)_invals;
1076 LDAPMod **modlist = (LDAPMod **) *mods;
1077 struct berval **ber_values = NULL;
1078 char **char_values = NULL;
1081 mod_op = LDAP_MOD_DELETE;
1083 if (mod_op & LDAP_MOD_BVALUES)
1084 ber_values = ads_dup_values(ctx,
1085 (const struct berval **)invals);
1087 char_values = ads_push_strvals(ctx,
1088 (const char **) invals);
1091 /* find the first empty slot */
1092 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1094 if (modlist[curmod] == (LDAPMod *) -1) {
1095 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1096 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1097 return ADS_ERROR(LDAP_NO_MEMORY);
1098 memset(&modlist[curmod], 0,
1099 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1100 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1101 *mods = (ADS_MODLIST)modlist;
1104 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1105 return ADS_ERROR(LDAP_NO_MEMORY);
1106 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1107 if (mod_op & LDAP_MOD_BVALUES) {
1108 modlist[curmod]->mod_bvalues = ber_values;
1109 } else if (mod_op & LDAP_MOD_DELETE) {
1110 modlist[curmod]->mod_values = NULL;
1112 modlist[curmod]->mod_values = char_values;
1115 modlist[curmod]->mod_op = mod_op;
1116 return ADS_ERROR(LDAP_SUCCESS);
1120 * Add a single string value to a mod list
1121 * @param ctx An initialized TALLOC_CTX
1122 * @param mods An initialized ADS_MODLIST
1123 * @param name The attribute name to add
1124 * @param val The value to add - NULL means DELETE
1125 * @return ADS STATUS indicating success of add
1127 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1128 const char *name, const char *val)
1130 const char *values[2];
1136 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1137 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1141 * Add an array of string values to a mod list
1142 * @param ctx An initialized TALLOC_CTX
1143 * @param mods An initialized ADS_MODLIST
1144 * @param name The attribute name to add
1145 * @param vals The array of string values to add - NULL means DELETE
1146 * @return ADS STATUS indicating success of add
1148 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1149 const char *name, const char **vals)
1152 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1153 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1154 name, (const void **) vals);
1159 * Add a single ber-encoded value to a mod list
1160 * @param ctx An initialized TALLOC_CTX
1161 * @param mods An initialized ADS_MODLIST
1162 * @param name The attribute name to add
1163 * @param val The value to add - NULL means DELETE
1164 * @return ADS STATUS indicating success of add
1166 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1167 const char *name, const struct berval *val)
1169 const struct berval *values[2];
1174 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1175 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1176 name, (const void **) values);
1181 * Perform an ldap modify
1182 * @param ads connection to ads server
1183 * @param mod_dn DistinguishedName to modify
1184 * @param mods list of modifications to perform
1185 * @return status of modify
1187 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1190 char *utf8_dn = NULL;
1192 this control is needed to modify that contains a currently
1193 non-existent attribute (but allowable for the object) to run
1195 LDAPControl PermitModify = {
1196 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1199 LDAPControl *controls[2];
1201 controls[0] = &PermitModify;
1204 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1205 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1208 /* find the end of the list, marked by NULL or -1 */
1209 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1210 /* make sure the end of the list is NULL */
1212 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1213 (LDAPMod **) mods, controls, NULL);
1215 return ADS_ERROR(ret);
1219 * Perform an ldap add
1220 * @param ads connection to ads server
1221 * @param new_dn DistinguishedName to add
1222 * @param mods list of attributes and values for DN
1223 * @return status of add
1225 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1228 char *utf8_dn = NULL;
1230 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1231 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1232 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1235 /* find the end of the list, marked by NULL or -1 */
1236 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1237 /* make sure the end of the list is NULL */
1240 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1242 return ADS_ERROR(ret);
1246 * Delete a DistinguishedName
1247 * @param ads connection to ads server
1248 * @param new_dn DistinguishedName to delete
1249 * @return status of delete
1251 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1254 char *utf8_dn = NULL;
1255 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1256 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1257 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1260 ret = ldap_delete_s(ads->ld, utf8_dn);
1262 return ADS_ERROR(ret);
1266 * Build an org unit string
1267 * if org unit is Computers or blank then assume a container, otherwise
1268 * assume a / separated list of organisational units.
1269 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1270 * @param ads connection to ads server
1271 * @param org_unit Organizational unit
1272 * @return org unit string - caller must free
1274 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1278 if (!org_unit || !*org_unit) {
1280 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1282 /* samba4 might not yet respond to a wellknownobject-query */
1283 return ret ? ret : SMB_STRDUP("cn=Computers");
1286 if (strequal(org_unit, "Computers")) {
1287 return SMB_STRDUP("cn=Computers");
1290 /* jmcd: removed "\\" from the separation chars, because it is
1291 needed as an escape for chars like '#' which are valid in an
1293 return ads_build_path(org_unit, "/", "ou=", 1);
1297 * Get a org unit string for a well-known GUID
1298 * @param ads connection to ads server
1299 * @param wknguid Well known GUID
1300 * @return org unit string - caller must free
1302 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1305 LDAPMessage *res = NULL;
1306 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1307 **bind_dn_exp = NULL;
1308 const char *attrs[] = {"distinguishedName", NULL};
1309 int new_ln, wkn_ln, bind_ln, i;
1311 if (wknguid == NULL) {
1315 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1316 DEBUG(1, ("asprintf failed!\n"));
1320 status = ads_search_dn(ads, &res, base, attrs);
1321 if (!ADS_ERR_OK(status)) {
1322 DEBUG(1,("Failed while searching for: %s\n", base));
1326 if (ads_count_replies(ads, res) != 1) {
1330 /* substitute the bind-path from the well-known-guid-search result */
1331 wkn_dn = ads_get_dn(ads, res);
1336 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1341 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1346 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1348 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1351 new_ln = wkn_ln - bind_ln;
1353 ret = SMB_STRDUP(wkn_dn_exp[0]);
1358 for (i=1; i < new_ln; i++) {
1361 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1367 ret = SMB_STRDUP(s);
1376 ads_msgfree(ads, res);
1377 ads_memfree(ads, wkn_dn);
1379 ldap_value_free(wkn_dn_exp);
1382 ldap_value_free(bind_dn_exp);
1389 * Adds (appends) an item to an attribute array, rather then
1390 * replacing the whole list
1391 * @param ctx An initialized TALLOC_CTX
1392 * @param mods An initialized ADS_MODLIST
1393 * @param name name of the ldap attribute to append to
1394 * @param vals an array of values to add
1395 * @return status of addition
1398 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1399 const char *name, const char **vals)
1401 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1402 (const void *) vals);
1406 * Determines the computer account's current KVNO via an LDAP lookup
1407 * @param ads An initialized ADS_STRUCT
1408 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1409 * @return the kvno for the computer account, or -1 in case of a failure.
1412 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1414 LDAPMessage *res = NULL;
1415 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1417 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1418 char *dn_string = NULL;
1419 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1421 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1422 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1425 ret = ads_search(ads, &res, filter, attrs);
1427 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1428 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1429 ads_msgfree(ads, res);
1433 dn_string = ads_get_dn(ads, res);
1435 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1436 ads_msgfree(ads, res);
1439 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1440 ads_memfree(ads, dn_string);
1442 /* ---------------------------------------------------------
1443 * 0 is returned as a default KVNO from this point on...
1444 * This is done because Windows 2000 does not support key
1445 * version numbers. Chances are that a failure in the next
1446 * step is simply due to Windows 2000 being used for a
1447 * domain controller. */
1450 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1451 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1452 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1453 ads_msgfree(ads, res);
1458 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1459 ads_msgfree(ads, res);
1464 * This clears out all registered spn's for a given hostname
1465 * @param ads An initilaized ADS_STRUCT
1466 * @param machine_name the NetBIOS name of the computer.
1467 * @return 0 upon success, non-zero otherwise.
1470 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1473 LDAPMessage *res = NULL;
1475 const char *servicePrincipalName[1] = {NULL};
1476 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1477 char *dn_string = NULL;
1479 ret = ads_find_machine_acct(ads, &res, machine_name);
1480 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1481 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1482 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1483 ads_msgfree(ads, res);
1484 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1487 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1488 ctx = talloc_init("ads_clear_service_principal_names");
1490 ads_msgfree(ads, res);
1491 return ADS_ERROR(LDAP_NO_MEMORY);
1494 if (!(mods = ads_init_mods(ctx))) {
1495 talloc_destroy(ctx);
1496 ads_msgfree(ads, res);
1497 return ADS_ERROR(LDAP_NO_MEMORY);
1499 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1500 if (!ADS_ERR_OK(ret)) {
1501 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1502 ads_msgfree(ads, res);
1503 talloc_destroy(ctx);
1506 dn_string = ads_get_dn(ads, res);
1508 talloc_destroy(ctx);
1509 ads_msgfree(ads, res);
1510 return ADS_ERROR(LDAP_NO_MEMORY);
1512 ret = ads_gen_mod(ads, dn_string, mods);
1513 ads_memfree(ads,dn_string);
1514 if (!ADS_ERR_OK(ret)) {
1515 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1517 ads_msgfree(ads, res);
1518 talloc_destroy(ctx);
1522 ads_msgfree(ads, res);
1523 talloc_destroy(ctx);
1528 * This adds a service principal name to an existing computer account
1529 * (found by hostname) in AD.
1530 * @param ads An initialized ADS_STRUCT
1531 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1532 * @param my_fqdn The fully qualified DNS name of the machine
1533 * @param spn A string of the service principal to add, i.e. 'host'
1534 * @return 0 upon sucess, or non-zero if a failure occurs
1537 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1538 const char *my_fqdn, const char *spn)
1542 LDAPMessage *res = NULL;
1545 char *dn_string = NULL;
1546 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1548 ret = ads_find_machine_acct(ads, &res, machine_name);
1549 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1550 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1552 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1553 spn, machine_name, ads->config.realm));
1554 ads_msgfree(ads, res);
1555 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1558 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1559 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1560 ads_msgfree(ads, res);
1561 return ADS_ERROR(LDAP_NO_MEMORY);
1564 /* add short name spn */
1566 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1567 talloc_destroy(ctx);
1568 ads_msgfree(ads, res);
1569 return ADS_ERROR(LDAP_NO_MEMORY);
1572 strlower_m(&psp1[strlen(spn)]);
1573 servicePrincipalName[0] = psp1;
1575 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1576 psp1, machine_name));
1579 /* add fully qualified spn */
1581 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1582 ret = ADS_ERROR(LDAP_NO_MEMORY);
1586 strlower_m(&psp2[strlen(spn)]);
1587 servicePrincipalName[1] = psp2;
1589 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1590 psp2, machine_name));
1592 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1593 ret = ADS_ERROR(LDAP_NO_MEMORY);
1597 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1598 if (!ADS_ERR_OK(ret)) {
1599 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1603 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1604 ret = ADS_ERROR(LDAP_NO_MEMORY);
1608 ret = ads_gen_mod(ads, dn_string, mods);
1609 ads_memfree(ads,dn_string);
1610 if (!ADS_ERR_OK(ret)) {
1611 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1617 ads_msgfree(ads, res);
1622 * adds a machine account to the ADS server
1623 * @param ads An intialized ADS_STRUCT
1624 * @param machine_name - the NetBIOS machine name of this account.
1625 * @param account_type A number indicating the type of account to create
1626 * @param org_unit The LDAP path in which to place this account
1627 * @return 0 upon success, or non-zero otherwise
1630 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1631 const char *org_unit)
1634 char *samAccountName, *controlstr;
1638 const char *objectClass[] = {"top", "person", "organizationalPerson",
1639 "user", "computer", NULL};
1640 LDAPMessage *res = NULL;
1641 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1642 UF_DONT_EXPIRE_PASSWD |\
1643 UF_ACCOUNTDISABLE );
1645 if (!(ctx = talloc_init("ads_add_machine_acct")))
1646 return ADS_ERROR(LDAP_NO_MEMORY);
1648 ret = ADS_ERROR(LDAP_NO_MEMORY);
1650 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1651 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1653 if ( !new_dn || !samAccountName ) {
1657 #ifndef ENCTYPE_ARCFOUR_HMAC
1658 acct_control |= UF_USE_DES_KEY_ONLY;
1661 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1665 if (!(mods = ads_init_mods(ctx))) {
1669 ads_mod_str(ctx, &mods, "cn", machine_name);
1670 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1671 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1672 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1674 ret = ads_gen_add(ads, new_dn, mods);
1677 ads_msgfree(ads, res);
1678 talloc_destroy(ctx);
1684 dump a binary result from ldap
1686 static void dump_binary(const char *field, struct berval **values)
1689 for (i=0; values[i]; i++) {
1690 printf("%s: ", field);
1691 for (j=0; j<values[i]->bv_len; j++) {
1692 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1698 static void dump_guid(const char *field, struct berval **values)
1702 for (i=0; values[i]; i++) {
1703 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1704 printf("%s: %s\n", field,
1705 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1710 dump a sid result from ldap
1712 static void dump_sid(const char *field, struct berval **values)
1715 for (i=0; values[i]; i++) {
1717 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1718 printf("%s: %s\n", field, sid_string_static(&sid));
1723 dump ntSecurityDescriptor
1725 static void dump_sd(const char *filed, struct berval **values)
1730 TALLOC_CTX *ctx = 0;
1732 if (!(ctx = talloc_init("sec_io_desc")))
1736 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1737 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1738 prs_set_offset(&ps,0);
1741 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1743 talloc_destroy(ctx);
1746 if (psd) ads_disp_sd(psd);
1749 talloc_destroy(ctx);
1753 dump a string result from ldap
1755 static void dump_string(const char *field, char **values)
1758 for (i=0; values[i]; i++) {
1759 printf("%s: %s\n", field, values[i]);
1764 dump a field from LDAP on stdout
1768 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1773 void (*handler)(const char *, struct berval **);
1775 {"objectGUID", False, dump_guid},
1776 {"netbootGUID", False, dump_guid},
1777 {"nTSecurityDescriptor", False, dump_sd},
1778 {"dnsRecord", False, dump_binary},
1779 {"objectSid", False, dump_sid},
1780 {"tokenGroups", False, dump_sid},
1781 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1782 {"tokengroupsGlobalandUniversal", False, dump_sid},
1783 {"mS-DS-CreatorSID", False, dump_sid},
1788 if (!field) { /* must be end of an entry */
1793 for (i=0; handlers[i].name; i++) {
1794 if (StrCaseCmp(handlers[i].name, field) == 0) {
1795 if (!values) /* first time, indicate string or not */
1796 return handlers[i].string;
1797 handlers[i].handler(field, (struct berval **) values);
1801 if (!handlers[i].name) {
1802 if (!values) /* first time, indicate string conversion */
1804 dump_string(field, (char **)values);
1810 * Dump a result from LDAP on stdout
1811 * used for debugging
1812 * @param ads connection to ads server
1813 * @param res Results to dump
1816 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1818 ads_process_results(ads, res, ads_dump_field, NULL);
1822 * Walk through results, calling a function for each entry found.
1823 * The function receives a field name, a berval * array of values,
1824 * and a data area passed through from the start. The function is
1825 * called once with null for field and values at the end of each
1827 * @param ads connection to ads server
1828 * @param res Results to process
1829 * @param fn Function for processing each result
1830 * @param data_area user-defined area to pass to function
1832 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1833 BOOL(*fn)(char *, void **, void *),
1839 if (!(ctx = talloc_init("ads_process_results")))
1842 for (msg = ads_first_entry(ads, res); msg;
1843 msg = ads_next_entry(ads, msg)) {
1847 for (utf8_field=ldap_first_attribute(ads->ld,
1848 (LDAPMessage *)msg,&b);
1850 utf8_field=ldap_next_attribute(ads->ld,
1851 (LDAPMessage *)msg,b)) {
1852 struct berval **ber_vals;
1853 char **str_vals, **utf8_vals;
1857 pull_utf8_talloc(ctx, &field, utf8_field);
1858 string = fn(field, NULL, data_area);
1861 utf8_vals = ldap_get_values(ads->ld,
1862 (LDAPMessage *)msg, field);
1863 str_vals = ads_pull_strvals(ctx,
1864 (const char **) utf8_vals);
1865 fn(field, (void **) str_vals, data_area);
1866 ldap_value_free(utf8_vals);
1868 ber_vals = ldap_get_values_len(ads->ld,
1869 (LDAPMessage *)msg, field);
1870 fn(field, (void **) ber_vals, data_area);
1872 ldap_value_free_len(ber_vals);
1874 ldap_memfree(utf8_field);
1877 talloc_free_children(ctx);
1878 fn(NULL, NULL, data_area); /* completed an entry */
1881 talloc_destroy(ctx);
1885 * count how many replies are in a LDAPMessage
1886 * @param ads connection to ads server
1887 * @param res Results to count
1888 * @return number of replies
1890 int ads_count_replies(ADS_STRUCT *ads, void *res)
1892 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1896 * pull the first entry from a ADS result
1897 * @param ads connection to ads server
1898 * @param res Results of search
1899 * @return first entry from result
1901 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1903 return ldap_first_entry(ads->ld, res);
1907 * pull the next entry from a ADS result
1908 * @param ads connection to ads server
1909 * @param res Results of search
1910 * @return next entry from result
1912 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1914 return ldap_next_entry(ads->ld, res);
1918 * pull a single string from a ADS result
1919 * @param ads connection to ads server
1920 * @param mem_ctx TALLOC_CTX to use for allocating result string
1921 * @param msg Results of search
1922 * @param field Attribute to retrieve
1923 * @return Result string in talloc context
1925 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
1933 values = ldap_get_values(ads->ld, msg, field);
1938 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1940 if (rc != (size_t)-1)
1944 ldap_value_free(values);
1949 * pull an array of strings from a ADS result
1950 * @param ads connection to ads server
1951 * @param mem_ctx TALLOC_CTX to use for allocating result string
1952 * @param msg Results of search
1953 * @param field Attribute to retrieve
1954 * @return Result strings in talloc context
1956 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1957 LDAPMessage *msg, const char *field,
1964 values = ldap_get_values(ads->ld, msg, field);
1968 *num_values = ldap_count_values(values);
1970 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1972 ldap_value_free(values);
1976 for (i=0;i<*num_values;i++) {
1977 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1978 ldap_value_free(values);
1984 ldap_value_free(values);
1989 * pull an array of strings from a ADS result
1990 * (handle large multivalue attributes with range retrieval)
1991 * @param ads connection to ads server
1992 * @param mem_ctx TALLOC_CTX to use for allocating result string
1993 * @param msg Results of search
1994 * @param field Attribute to retrieve
1995 * @param current_strings strings returned by a previous call to this function
1996 * @param next_attribute The next query should ask for this attribute
1997 * @param num_values How many values did we get this time?
1998 * @param more_values Are there more values to get?
1999 * @return Result strings in talloc context
2001 char **ads_pull_strings_range(ADS_STRUCT *ads,
2002 TALLOC_CTX *mem_ctx,
2003 LDAPMessage *msg, const char *field,
2004 char **current_strings,
2005 const char **next_attribute,
2006 size_t *num_strings,
2010 char *expected_range_attrib, *range_attr;
2011 BerElement *ptr = NULL;
2014 size_t num_new_strings;
2015 unsigned long int range_start;
2016 unsigned long int range_end;
2018 /* we might have been given the whole lot anyway */
2019 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2020 *more_strings = False;
2024 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2026 /* look for Range result */
2027 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2029 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2030 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2031 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2039 /* nothing here - this field is just empty */
2040 *more_strings = False;
2044 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2045 &range_start, &range_end) == 2) {
2046 *more_strings = True;
2048 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2049 &range_start) == 1) {
2050 *more_strings = False;
2052 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2054 ldap_memfree(range_attr);
2055 *more_strings = False;
2060 if ((*num_strings) != range_start) {
2061 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2062 " - aborting range retreival\n",
2063 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2064 ldap_memfree(range_attr);
2065 *more_strings = False;
2069 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2071 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2072 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2073 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2074 range_attr, (unsigned long int)range_end - range_start + 1,
2075 (unsigned long int)num_new_strings));
2076 ldap_memfree(range_attr);
2077 *more_strings = False;
2081 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2082 *num_strings + num_new_strings);
2084 if (strings == NULL) {
2085 ldap_memfree(range_attr);
2086 *more_strings = False;
2090 if (new_strings && num_new_strings) {
2091 memcpy(&strings[*num_strings], new_strings,
2092 sizeof(*new_strings) * num_new_strings);
2095 (*num_strings) += num_new_strings;
2097 if (*more_strings) {
2098 *next_attribute = talloc_asprintf(mem_ctx,
2103 if (!*next_attribute) {
2104 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2105 ldap_memfree(range_attr);
2106 *more_strings = False;
2111 ldap_memfree(range_attr);
2117 * pull a single uint32 from a ADS result
2118 * @param ads connection to ads server
2119 * @param msg Results of search
2120 * @param field Attribute to retrieve
2121 * @param v Pointer to int to store result
2122 * @return boolean inidicating success
2124 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2129 values = ldap_get_values(ads->ld, msg, field);
2133 ldap_value_free(values);
2137 *v = atoi(values[0]);
2138 ldap_value_free(values);
2143 * pull a single objectGUID from an ADS result
2144 * @param ads connection to ADS server
2145 * @param msg results of search
2146 * @param guid 37-byte area to receive text guid
2147 * @return boolean indicating success
2149 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2152 UUID_FLAT flat_guid;
2154 values = ldap_get_values(ads->ld, msg, "objectGUID");
2159 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2160 smb_uuid_unpack(flat_guid, guid);
2161 ldap_value_free(values);
2164 ldap_value_free(values);
2171 * pull a single DOM_SID from a ADS result
2172 * @param ads connection to ads server
2173 * @param msg Results of search
2174 * @param field Attribute to retrieve
2175 * @param sid Pointer to sid to store result
2176 * @return boolean inidicating success
2178 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2181 struct berval **values;
2184 values = ldap_get_values_len(ads->ld, msg, field);
2190 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2192 ldap_value_free_len(values);
2197 * pull an array of DOM_SIDs from a ADS result
2198 * @param ads connection to ads server
2199 * @param mem_ctx TALLOC_CTX for allocating sid array
2200 * @param msg Results of search
2201 * @param field Attribute to retrieve
2202 * @param sids pointer to sid array to allocate
2203 * @return the count of SIDs pulled
2205 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2206 LDAPMessage *msg, const char *field, DOM_SID **sids)
2208 struct berval **values;
2212 values = ldap_get_values_len(ads->ld, msg, field);
2217 for (i=0; values[i]; i++)
2220 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2222 ldap_value_free_len(values);
2227 for (i=0; values[i]; i++) {
2228 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2231 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2236 ldap_value_free_len(values);
2241 * pull a SEC_DESC from a ADS result
2242 * @param ads connection to ads server
2243 * @param mem_ctx TALLOC_CTX for allocating sid array
2244 * @param msg Results of search
2245 * @param field Attribute to retrieve
2246 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2247 * @return boolean inidicating success
2249 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2250 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2252 struct berval **values;
2256 values = ldap_get_values_len(ads->ld, msg, field);
2258 if (!values) return False;
2261 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2262 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2263 prs_set_offset(&ps,0);
2265 ret = sec_io_desc("sd", sd, &ps, 1);
2268 ldap_value_free_len(values);
2273 * in order to support usernames longer than 21 characters we need to
2274 * use both the sAMAccountName and the userPrincipalName attributes
2275 * It seems that not all users have the userPrincipalName attribute set
2277 * @param ads connection to ads server
2278 * @param mem_ctx TALLOC_CTX for allocating sid array
2279 * @param msg Results of search
2280 * @return the username
2282 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2288 /* lookup_name() only works on the sAMAccountName to
2289 returning the username portion of userPrincipalName
2290 breaks winbindd_getpwnam() */
2292 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2293 if (ret && (p = strchr_m(ret, '@'))) {
2298 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2303 * find the update serial number - this is the core of the ldap cache
2304 * @param ads connection to ads server
2305 * @param ads connection to ADS server
2306 * @param usn Pointer to retrieved update serial number
2307 * @return status of search
2309 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2311 const char *attrs[] = {"highestCommittedUSN", NULL};
2315 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2316 if (!ADS_ERR_OK(status))
2319 if (ads_count_replies(ads, res) != 1) {
2320 ads_msgfree(ads, res);
2321 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2324 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2325 ads_msgfree(ads, res);
2326 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2329 ads_msgfree(ads, res);
2333 /* parse a ADS timestring - typical string is
2334 '20020917091222.0Z0' which means 09:12.22 17th September
2336 static time_t ads_parse_time(const char *str)
2342 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2343 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2344 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2353 /********************************************************************
2354 ********************************************************************/
2356 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2358 const char *attrs[] = {"currentTime", NULL};
2363 ADS_STRUCT *ads_s = ads;
2365 if (!(ctx = talloc_init("ads_current_time"))) {
2366 return ADS_ERROR(LDAP_NO_MEMORY);
2369 /* establish a new ldap tcp session if necessary */
2372 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2373 ads->server.ldap_server )) == NULL )
2377 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2378 status = ads_connect( ads_s );
2379 if ( !ADS_ERR_OK(status))
2383 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2384 if (!ADS_ERR_OK(status)) {
2388 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2390 ads_msgfree(ads_s, res);
2391 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2395 /* but save the time and offset in the original ADS_STRUCT */
2397 ads->config.current_time = ads_parse_time(timestr);
2399 if (ads->config.current_time != 0) {
2400 ads->auth.time_offset = ads->config.current_time - time(NULL);
2401 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2404 ads_msgfree(ads, res);
2406 status = ADS_SUCCESS;
2409 /* free any temporary ads connections */
2410 if ( ads_s != ads ) {
2411 ads_destroy( &ads_s );
2413 talloc_destroy(ctx);
2418 /********************************************************************
2419 ********************************************************************/
2421 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2423 const char *attrs[] = {"domainFunctionality", NULL};
2426 ADS_STRUCT *ads_s = ads;
2428 *val = DS_DOMAIN_FUNCTION_2000;
2430 /* establish a new ldap tcp session if necessary */
2433 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2434 ads->server.ldap_server )) == NULL )
2438 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2439 status = ads_connect( ads_s );
2440 if ( !ADS_ERR_OK(status))
2444 /* If the attribute does not exist assume it is a Windows 2000
2445 functional domain */
2447 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2448 if (!ADS_ERR_OK(status)) {
2449 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2450 status = ADS_SUCCESS;
2455 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2456 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2458 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2461 ads_msgfree(ads, res);
2464 /* free any temporary ads connections */
2465 if ( ads_s != ads ) {
2466 ads_destroy( &ads_s );
2473 * find the domain sid for our domain
2474 * @param ads connection to ads server
2475 * @param sid Pointer to domain sid
2476 * @return status of search
2478 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2480 const char *attrs[] = {"objectSid", NULL};
2484 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2486 if (!ADS_ERR_OK(rc)) return rc;
2487 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2488 ads_msgfree(ads, res);
2489 return ADS_ERROR_SYSTEM(ENOENT);
2491 ads_msgfree(ads, res);
2497 * find our site name
2498 * @param ads connection to ads server
2499 * @param mem_ctx Pointer to talloc context
2500 * @param site_name Pointer to the sitename
2501 * @return status of search
2503 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2507 const char *dn, *service_name;
2508 const char *attrs[] = { "dsServiceName", NULL };
2510 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2511 if (!ADS_ERR_OK(status)) {
2515 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2516 if (service_name == NULL) {
2517 ads_msgfree(ads, res);
2518 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2521 ads_msgfree(ads, res);
2523 /* go up three levels */
2524 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2526 return ADS_ERROR(LDAP_NO_MEMORY);
2529 *site_name = talloc_strdup(mem_ctx, dn);
2530 if (*site_name == NULL) {
2531 return ADS_ERROR(LDAP_NO_MEMORY);
2536 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2541 * find the site dn where a machine resides
2542 * @param ads connection to ads server
2543 * @param mem_ctx Pointer to talloc context
2544 * @param computer_name name of the machine
2545 * @param site_name Pointer to the sitename
2546 * @return status of search
2548 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2552 const char *parent, *config_context, *filter;
2553 const char *attrs[] = { "configurationNamingContext", NULL };
2556 /* shortcut a query */
2557 if (strequal(computer_name, ads->config.ldap_server_name)) {
2558 return ads_site_dn(ads, mem_ctx, site_dn);
2561 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2562 if (!ADS_ERR_OK(status)) {
2566 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2567 if (config_context == NULL) {
2568 ads_msgfree(ads, res);
2569 return ADS_ERROR(LDAP_NO_MEMORY);
2572 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2573 if (filter == NULL) {
2574 ads_msgfree(ads, res);
2575 return ADS_ERROR(LDAP_NO_MEMORY);
2578 ads_msgfree(ads, res);
2580 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2581 if (!ADS_ERR_OK(status)) {
2585 if (ads_count_replies(ads, res) != 1) {
2586 ads_msgfree(ads, res);
2587 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2590 dn = ads_get_dn(ads, res);
2592 ads_msgfree(ads, res);
2593 return ADS_ERROR(LDAP_NO_MEMORY);
2596 /* go up three levels */
2597 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2598 if (parent == NULL) {
2599 ads_msgfree(ads, res);
2600 ads_memfree(ads, dn);
2601 return ADS_ERROR(LDAP_NO_MEMORY);
2604 *site_dn = talloc_strdup(mem_ctx, parent);
2605 if (*site_dn == NULL) {
2606 ads_msgfree(ads, res);
2607 ads_memfree(ads, dn);
2608 ADS_ERROR(LDAP_NO_MEMORY);
2611 ads_memfree(ads, dn);
2612 ads_msgfree(ads, res);
2618 * get the upn suffixes for a domain
2619 * @param ads connection to ads server
2620 * @param mem_ctx Pointer to talloc context
2621 * @param suffixes Pointer to an array of suffixes
2622 * @param site_name Pointer to the number of suffixes
2623 * @return status of search
2625 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2629 const char *config_context, *base;
2630 const char *attrs[] = { "configurationNamingContext", NULL };
2631 const char *attrs2[] = { "uPNSuffixes", NULL };
2633 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2634 if (!ADS_ERR_OK(status)) {
2638 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2639 if (config_context == NULL) {
2640 return ADS_ERROR(LDAP_NO_MEMORY);
2643 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2645 return ADS_ERROR(LDAP_NO_MEMORY);
2648 status = ads_search_dn(ads, &res, base, attrs2);
2649 if (!ADS_ERR_OK(status)) {
2653 if (ads_count_replies(ads, res) != 1) {
2654 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2657 suffixes = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2658 if (suffixes == NULL) {
2659 ads_msgfree(ads, res);
2660 return ADS_ERROR(LDAP_NO_MEMORY);
2663 ads_msgfree(ads, res);
2669 * pull a DOM_SID from an extended dn string
2670 * @param mem_ctx TALLOC_CTX
2671 * @param flags string type of extended_dn
2672 * @param sid pointer to a DOM_SID
2673 * @return boolean inidicating success
2675 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2677 enum ads_extended_dn_flags flags,
2687 * ADS_EXTENDED_DN_HEX_STRING:
2688 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2690 * ADS_EXTENDED_DN_STRING (only with w2k3):
2691 <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
2694 p = strchr(dn, ';');
2699 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2703 p += strlen(";<SID=");
2712 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2716 case ADS_EXTENDED_DN_STRING:
2717 if (!string_to_sid(sid, p)) {
2721 case ADS_EXTENDED_DN_HEX_STRING: {
2725 buf_len = strhex_to_str(buf, strlen(p), p);
2730 if (!sid_parse(buf, buf_len, sid)) {
2731 DEBUG(10,("failed to parse sid\n"));
2737 DEBUG(10,("unknown extended dn format\n"));
2745 * pull an array of DOM_SIDs from a ADS result
2746 * @param ads connection to ads server
2747 * @param mem_ctx TALLOC_CTX for allocating sid array
2748 * @param msg Results of search
2749 * @param field Attribute to retrieve
2750 * @param flags string type of extended_dn
2751 * @param sids pointer to sid array to allocate
2752 * @return the count of SIDs pulled
2754 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2755 TALLOC_CTX *mem_ctx,
2758 enum ads_extended_dn_flags flags,
2765 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2766 &dn_count)) == NULL) {
2770 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2772 TALLOC_FREE(dn_strings);
2776 for (i=0; i<dn_count; i++) {
2778 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2779 flags, &(*sids)[i])) {
2781 TALLOC_FREE(dn_strings);
2786 TALLOC_FREE(dn_strings);
2791 /********************************************************************
2792 ********************************************************************/
2794 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2796 LDAPMessage *res = NULL;
2801 status = ads_find_machine_acct(ads, &res, global_myname());
2802 if (!ADS_ERR_OK(status)) {
2803 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2808 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2809 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2813 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2814 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2818 ads_msgfree(ads, res);
2823 /********************************************************************
2824 ********************************************************************/
2826 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2828 LDAPMessage *res = NULL;
2833 status = ads_find_machine_acct(ads, &res, global_myname());
2834 if (!ADS_ERR_OK(status)) {
2835 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2840 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2841 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2845 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2846 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2850 ads_msgfree(ads, res);
2855 /********************************************************************
2856 ********************************************************************/
2858 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2860 LDAPMessage *res = NULL;
2865 status = ads_find_machine_acct(ads, &res, global_myname());
2866 if (!ADS_ERR_OK(status)) {
2867 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2872 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2873 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2877 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2878 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2882 ads_msgfree(ads, res);
2889 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2892 * Join a machine to a realm
2893 * Creates the machine account and sets the machine password
2894 * @param ads connection to ads server
2895 * @param machine name of host to add
2896 * @param org_unit Organizational unit to place machine in
2897 * @return status of join
2899 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2900 uint32 account_type, const char *org_unit)
2903 LDAPMessage *res = NULL;
2906 /* machine name must be lowercase */
2907 machine = SMB_STRDUP(machine_name);
2908 strlower_m(machine);
2911 status = ads_find_machine_acct(ads, (void **)&res, machine);
2912 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2913 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2914 status = ads_leave_realm(ads, machine);
2915 if (!ADS_ERR_OK(status)) {
2916 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2917 machine, ads->config.realm));
2922 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2923 if (!ADS_ERR_OK(status)) {
2924 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2929 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2930 if (!ADS_ERR_OK(status)) {
2931 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2937 ads_msgfree(ads, res);
2944 * Delete a machine from the realm
2945 * @param ads connection to ads server
2946 * @param hostname Machine to remove
2947 * @return status of delete
2949 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2954 char *hostnameDN, *host;
2956 LDAPControl ldap_control;
2957 LDAPControl * pldap_control[2] = {NULL, NULL};
2959 pldap_control[0] = &ldap_control;
2960 memset(&ldap_control, 0, sizeof(LDAPControl));
2961 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
2963 /* hostname must be lowercase */
2964 host = SMB_STRDUP(hostname);
2967 status = ads_find_machine_acct(ads, &res, host);
2968 if (!ADS_ERR_OK(status)) {
2969 DEBUG(0, ("Host account for %s does not exist.\n", host));
2974 msg = ads_first_entry(ads, res);
2977 return ADS_ERROR_SYSTEM(ENOENT);
2980 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
2982 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
2984 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
2986 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
2989 if (rc != LDAP_SUCCESS) {
2990 const char *attrs[] = { "cn", NULL };
2991 LDAPMessage *msg_sub;
2993 /* we only search with scope ONE, we do not expect any further
2994 * objects to be created deeper */
2996 status = ads_do_search_retry(ads, hostnameDN,
2997 LDAP_SCOPE_ONELEVEL,
2998 "(objectclass=*)", attrs, &res);
3000 if (!ADS_ERR_OK(status)) {
3002 ads_memfree(ads, hostnameDN);
3006 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3007 msg_sub = ads_next_entry(ads, msg_sub)) {
3011 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3013 ads_memfree(ads, hostnameDN);
3014 return ADS_ERROR(LDAP_NO_MEMORY);
3017 status = ads_del_dn(ads, dn);
3018 if (!ADS_ERR_OK(status)) {
3019 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3021 ads_memfree(ads, dn);
3022 ads_memfree(ads, hostnameDN);
3026 ads_memfree(ads, dn);
3029 /* there should be no subordinate objects anymore */
3030 status = ads_do_search_retry(ads, hostnameDN,
3031 LDAP_SCOPE_ONELEVEL,
3032 "(objectclass=*)", attrs, &res);
3034 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3036 ads_memfree(ads, hostnameDN);
3040 /* delete hostnameDN now */
3041 status = ads_del_dn(ads, hostnameDN);
3042 if (!ADS_ERR_OK(status)) {
3044 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3045 ads_memfree(ads, hostnameDN);
3050 ads_memfree(ads, hostnameDN);
3052 status = ads_find_machine_acct(ads, &res, host);
3053 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3054 DEBUG(3, ("Failed to remove host account.\n"));