r24267: Fix the build farm
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blobb1a86ad3d30f7344634c5da12f77dfaddab11401
1 /*
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/>.
24 #include "includes.h"
26 #ifdef HAVE_LDAP
28 /**
29 * @file ldap.c
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
33 * ads setups.
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
39 **/
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)
52 gotalarm = 1;
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 LDAP *ldp = NULL;
59 /* Setup timeout */
60 gotalarm = 0;
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
62 alarm(to);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
67 if (ldp == NULL) {
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);
74 alarm(0);
76 return ldp;
79 static int ldap_search_with_timeout(LDAP *ld,
80 LDAP_CONST char *base,
81 int scope,
82 LDAP_CONST char *filter,
83 char **attrs,
84 int attrsonly,
85 LDAPControl **sctrls,
86 LDAPControl **cctrls,
87 int sizelimit,
88 LDAPMessage **res )
90 struct timeval timeout;
91 int result;
93 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
94 timeout.tv_sec = lp_ldap_timeout();
95 timeout.tv_usec = 0;
97 /* Setup alarm timeout.... Do we need both of these ? JRA. */
98 gotalarm = 0;
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,
105 sizelimit, res);
107 /* Teardown timeout. */
108 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
109 alarm(0);
111 if (gotalarm != 0)
112 return LDAP_TIMELIMIT_EXCEEDED;
114 return result;
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"));
126 return True;
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));
133 return True;
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"));
138 return False;
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"));
149 return True;
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"));
155 return True;
158 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
159 ads->config.ldap_server_name));
161 return False;
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 )
171 char *srv;
172 struct cldap_netlogon_reply cldap_reply;
174 if (!server || !*server) {
175 return False;
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));
189 SAFE_FREE( srv );
190 return False;
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",
197 srv));
198 SAFE_FREE( srv );
199 return False;
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);
229 SAFE_FREE(srv);
231 /* Store our site name. */
232 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
234 return True;
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
240 disabled
241 **********************************************************************/
243 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
245 const char *c_realm;
246 int count, i=0;
247 struct ip_service *ip_list;
248 pstring realm;
249 BOOL got_realm = False;
250 BOOL use_own_domain = False;
251 char *sitename;
252 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
254 /* if the realm and workgroup are both empty, assume they are ours */
256 /* realm */
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)
268 got_realm = True;
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);
290 again:
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() ) {
299 got_realm = False;
300 goto again;
303 SAFE_FREE(sitename);
304 return status;
307 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
308 for ( i=0; i<count; i++ ) {
309 fstring server;
311 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
313 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
314 continue;
316 if (!got_realm) {
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 );
332 continue;
336 if ( ads_try_connect(ads, server) ) {
337 SAFE_FREE(ip_list);
338 SAFE_FREE(sitename);
339 return NT_STATUS_OK;
342 /* keep track of failures */
343 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
346 SAFE_FREE(ip_list);
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
350 * - Guenther */
352 if (sitename) {
353 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
354 "trying to find another DC\n", sitename));
355 SAFE_FREE(sitename);
356 namecache_delete(realm, 0x1C);
357 goto again;
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;
372 ADS_STATUS status;
373 NTSTATUS ntstatus;
375 ZERO_STRUCT(ads->ldap);
376 ads->ldap.last_attempt = time(NULL);
377 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
379 /* try with a user specified server */
381 if (ads->server.ldap_server &&
382 ads_try_connect(ads, ads->server.ldap_server)) {
383 goto got_connection;
386 ntstatus = ads_find_dc(ads);
387 if (NT_STATUS_IS_OK(ntstatus)) {
388 goto got_connection;
391 return ADS_ERROR_NT(ntstatus);
393 got_connection:
394 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap.ip)));
396 if (!ads->auth.user_name) {
397 /* Must use the userPrincipalName value here or sAMAccountName
398 and not servicePrincipalName; found by Guenther Deschner */
400 asprintf(&ads->auth.user_name, "%s$", global_myname() );
403 if (!ads->auth.realm) {
404 ads->auth.realm = SMB_STRDUP(ads->config.realm);
407 if (!ads->auth.kdc_server) {
408 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap.ip));
411 #if KRB5_DNS_HACK
412 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
413 to MIT kerberos to work (tridge) */
415 char *env;
416 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
417 setenv(env, ads->auth.kdc_server, 1);
418 free(env);
420 #endif
422 /* If the caller() requested no LDAP bind, then we are done */
424 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
425 return ADS_SUCCESS;
428 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
429 if (!ads->ldap.mem_ctx) {
430 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
433 /* Otherwise setup the TCP LDAP session */
435 if ( (ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
436 LDAP_PORT, lp_ldap_timeout())) == NULL )
438 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
441 /* cache the successful connection for workgroup and realm */
442 if (ads_closest_dc(ads)) {
443 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap.ip));
444 saf_store( ads->server.realm, inet_ntoa(ads->ldap.ip));
447 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
449 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
450 if (!ADS_ERR_OK(status)) {
451 return status;
454 /* fill in the current time and offsets */
456 status = ads_current_time( ads );
457 if ( !ADS_ERR_OK(status) ) {
458 return status;
461 /* Now do the bind */
463 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
464 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, NULL, NULL));
467 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
468 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
471 return ads_sasl_bind(ads);
475 * Disconnect the LDAP server
476 * @param ads Pointer to an existing ADS_STRUCT
478 void ads_disconnect(ADS_STRUCT *ads)
480 if (ads->ldap.ld) {
481 ldap_unbind(ads->ldap.ld);
482 ads->ldap.ld = NULL;
484 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
485 ads->ldap.wrap_ops->disconnect(ads);
487 if (ads->ldap.mem_ctx) {
488 talloc_free(ads->ldap.mem_ctx);
490 ZERO_STRUCT(ads->ldap);
494 Duplicate a struct berval into talloc'ed memory
496 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
498 struct berval *value;
500 if (!in_val) return NULL;
502 value = TALLOC_ZERO_P(ctx, struct berval);
503 if (value == NULL)
504 return NULL;
505 if (in_val->bv_len == 0) return value;
507 value->bv_len = in_val->bv_len;
508 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
509 in_val->bv_len);
510 return value;
514 Make a values list out of an array of (struct berval *)
516 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
517 const struct berval **in_vals)
519 struct berval **values;
520 int i;
522 if (!in_vals) return NULL;
523 for (i=0; in_vals[i]; i++)
524 ; /* count values */
525 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
526 if (!values) return NULL;
528 for (i=0; in_vals[i]; i++) {
529 values[i] = dup_berval(ctx, in_vals[i]);
531 return values;
535 UTF8-encode a values list out of an array of (char *)
537 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
539 char **values;
540 int i;
542 if (!in_vals) return NULL;
543 for (i=0; in_vals[i]; i++)
544 ; /* count values */
545 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
546 if (!values) return NULL;
548 for (i=0; in_vals[i]; i++) {
549 push_utf8_talloc(ctx, &values[i], in_vals[i]);
551 return values;
555 Pull a (char *) array out of a UTF8-encoded values list
557 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
559 char **values;
560 int i;
562 if (!in_vals) return NULL;
563 for (i=0; in_vals[i]; i++)
564 ; /* count values */
565 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
566 if (!values) return NULL;
568 for (i=0; in_vals[i]; i++) {
569 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
571 return values;
575 * Do a search with paged results. cookie must be null on the first
576 * call, and then returned on each subsequent call. It will be null
577 * again when the entire search is complete
578 * @param ads connection to ads server
579 * @param bind_path Base dn for the search
580 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
581 * @param expr Search expression - specified in local charset
582 * @param attrs Attributes to retrieve - specified in utf8 or ascii
583 * @param res ** which will contain results - free res* with ads_msgfree()
584 * @param count Number of entries retrieved on this page
585 * @param cookie The paged results cookie to be returned on subsequent calls
586 * @return status of search
588 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
589 const char *bind_path,
590 int scope, const char *expr,
591 const char **attrs, void *args,
592 LDAPMessage **res,
593 int *count, struct berval **cookie)
595 int rc, i, version;
596 char *utf8_expr, *utf8_path, **search_attrs;
597 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
598 BerElement *cookie_be = NULL;
599 struct berval *cookie_bv= NULL;
600 BerElement *ext_be = NULL;
601 struct berval *ext_bv= NULL;
603 TALLOC_CTX *ctx;
604 ads_control *external_control = (ads_control *) args;
606 *res = NULL;
608 if (!(ctx = talloc_init("ads_do_paged_search_args")))
609 return ADS_ERROR(LDAP_NO_MEMORY);
611 /* 0 means the conversion worked but the result was empty
612 so we only fail if it's -1. In any case, it always
613 at least nulls out the dest */
614 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
615 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
616 rc = LDAP_NO_MEMORY;
617 goto done;
620 if (!attrs || !(*attrs))
621 search_attrs = NULL;
622 else {
623 /* This would be the utf8-encoded version...*/
624 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
625 if (!(str_list_copy(&search_attrs, attrs))) {
626 rc = LDAP_NO_MEMORY;
627 goto done;
631 /* Paged results only available on ldap v3 or later */
632 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
633 if (version < LDAP_VERSION3) {
634 rc = LDAP_NOT_SUPPORTED;
635 goto done;
638 cookie_be = ber_alloc_t(LBER_USE_DER);
639 if (*cookie) {
640 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
641 ber_bvfree(*cookie); /* don't need it from last time */
642 *cookie = NULL;
643 } else {
644 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
646 ber_flatten(cookie_be, &cookie_bv);
647 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
648 PagedResults.ldctl_iscritical = (char) 1;
649 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
650 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
652 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
653 NoReferrals.ldctl_iscritical = (char) 0;
654 NoReferrals.ldctl_value.bv_len = 0;
655 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
657 if (external_control &&
658 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
659 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
661 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
662 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
664 /* win2k does not accept a ldctl_value beeing passed in */
666 if (external_control->val != 0) {
668 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
669 rc = LDAP_NO_MEMORY;
670 goto done;
673 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
674 rc = LDAP_NO_MEMORY;
675 goto done;
677 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
678 rc = LDAP_NO_MEMORY;
679 goto done;
682 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
683 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
685 } else {
686 ExternalCtrl.ldctl_value.bv_len = 0;
687 ExternalCtrl.ldctl_value.bv_val = NULL;
690 controls[0] = &NoReferrals;
691 controls[1] = &PagedResults;
692 controls[2] = &ExternalCtrl;
693 controls[3] = NULL;
695 } else {
696 controls[0] = &NoReferrals;
697 controls[1] = &PagedResults;
698 controls[2] = NULL;
701 /* we need to disable referrals as the openldap libs don't
702 handle them and paged results at the same time. Using them
703 together results in the result record containing the server
704 page control being removed from the result list (tridge/jmcd)
706 leaving this in despite the control that says don't generate
707 referrals, in case the server doesn't support it (jmcd)
709 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
711 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
712 search_attrs, 0, controls,
713 NULL, LDAP_NO_LIMIT,
714 (LDAPMessage **)res);
716 ber_free(cookie_be, 1);
717 ber_bvfree(cookie_bv);
719 if (rc) {
720 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
721 ldap_err2string(rc)));
722 goto done;
725 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
726 NULL, &rcontrols, 0);
728 if (!rcontrols) {
729 goto done;
732 for (i=0; rcontrols[i]; i++) {
733 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
734 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
735 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
736 &cookie_bv);
737 /* the berval is the cookie, but must be freed when
738 it is all done */
739 if (cookie_bv->bv_len) /* still more to do */
740 *cookie=ber_bvdup(cookie_bv);
741 else
742 *cookie=NULL;
743 ber_bvfree(cookie_bv);
744 ber_free(cookie_be, 1);
745 break;
748 ldap_controls_free(rcontrols);
750 done:
751 talloc_destroy(ctx);
753 if (ext_be) {
754 ber_free(ext_be, 1);
757 if (ext_bv) {
758 ber_bvfree(ext_bv);
761 /* if/when we decide to utf8-encode attrs, take out this next line */
762 str_list_free(&search_attrs);
764 return ADS_ERROR(rc);
767 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
768 int scope, const char *expr,
769 const char **attrs, LDAPMessage **res,
770 int *count, struct berval **cookie)
772 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
777 * Get all results for a search. This uses ads_do_paged_search() to return
778 * all entries in a large search.
779 * @param ads connection to ads server
780 * @param bind_path Base dn for the search
781 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
782 * @param expr Search expression
783 * @param attrs Attributes to retrieve
784 * @param res ** which will contain results - free res* with ads_msgfree()
785 * @return status of search
787 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
788 int scope, const char *expr,
789 const char **attrs, void *args,
790 LDAPMessage **res)
792 struct berval *cookie = NULL;
793 int count = 0;
794 ADS_STATUS status;
796 *res = NULL;
797 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
798 &count, &cookie);
800 if (!ADS_ERR_OK(status))
801 return status;
803 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
804 while (cookie) {
805 LDAPMessage *res2 = NULL;
806 ADS_STATUS status2;
807 LDAPMessage *msg, *next;
809 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
810 attrs, args, &res2, &count, &cookie);
812 if (!ADS_ERR_OK(status2)) break;
814 /* this relies on the way that ldap_add_result_entry() works internally. I hope
815 that this works on all ldap libs, but I have only tested with openldap */
816 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
817 next = ads_next_entry(ads, msg);
818 ldap_add_result_entry((LDAPMessage **)res, msg);
820 /* note that we do not free res2, as the memory is now
821 part of the main returned list */
823 #else
824 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
825 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
826 #endif
828 return status;
831 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
832 int scope, const char *expr,
833 const char **attrs, LDAPMessage **res)
835 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
838 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
839 int scope, const char *expr,
840 const char **attrs, uint32 sd_flags,
841 LDAPMessage **res)
843 ads_control args;
845 args.control = ADS_SD_FLAGS_OID;
846 args.val = sd_flags;
847 args.critical = True;
849 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
854 * Run a function on all results for a search. Uses ads_do_paged_search() and
855 * runs the function as each page is returned, using ads_process_results()
856 * @param ads connection to ads server
857 * @param bind_path Base dn for the search
858 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
859 * @param expr Search expression - specified in local charset
860 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
861 * @param fn Function which takes attr name, values list, and data_area
862 * @param data_area Pointer which is passed to function on each call
863 * @return status of search
865 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
866 int scope, const char *expr, const char **attrs,
867 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
868 void *data_area)
870 struct berval *cookie = NULL;
871 int count = 0;
872 ADS_STATUS status;
873 LDAPMessage *res;
875 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
876 &count, &cookie);
878 if (!ADS_ERR_OK(status)) return status;
880 ads_process_results(ads, res, fn, data_area);
881 ads_msgfree(ads, res);
883 while (cookie) {
884 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
885 &res, &count, &cookie);
887 if (!ADS_ERR_OK(status)) break;
889 ads_process_results(ads, res, fn, data_area);
890 ads_msgfree(ads, res);
893 return status;
897 * Do a search with a timeout.
898 * @param ads connection to ads server
899 * @param bind_path Base dn for the search
900 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
901 * @param expr Search expression
902 * @param attrs Attributes to retrieve
903 * @param res ** which will contain results - free res* with ads_msgfree()
904 * @return status of search
906 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
907 const char *expr,
908 const char **attrs, LDAPMessage **res)
910 int rc;
911 char *utf8_expr, *utf8_path, **search_attrs = NULL;
912 TALLOC_CTX *ctx;
914 *res = NULL;
915 if (!(ctx = talloc_init("ads_do_search"))) {
916 DEBUG(1,("ads_do_search: talloc_init() failed!"));
917 return ADS_ERROR(LDAP_NO_MEMORY);
920 /* 0 means the conversion worked but the result was empty
921 so we only fail if it's negative. In any case, it always
922 at least nulls out the dest */
923 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
924 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
925 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
926 rc = LDAP_NO_MEMORY;
927 goto done;
930 if (!attrs || !(*attrs))
931 search_attrs = NULL;
932 else {
933 /* This would be the utf8-encoded version...*/
934 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
935 if (!(str_list_copy(&search_attrs, attrs)))
937 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
938 rc = LDAP_NO_MEMORY;
939 goto done;
943 /* see the note in ads_do_paged_search - we *must* disable referrals */
944 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
946 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
947 search_attrs, 0, NULL, NULL,
948 LDAP_NO_LIMIT,
949 (LDAPMessage **)res);
951 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
952 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
953 rc = 0;
956 done:
957 talloc_destroy(ctx);
958 /* if/when we decide to utf8-encode attrs, take out this next line */
959 str_list_free(&search_attrs);
960 return ADS_ERROR(rc);
963 * Do a general ADS search
964 * @param ads connection to ads server
965 * @param res ** which will contain results - free res* with ads_msgfree()
966 * @param expr Search expression
967 * @param attrs Attributes to retrieve
968 * @return status of search
970 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
971 const char *expr, const char **attrs)
973 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
974 expr, attrs, res);
978 * Do a search on a specific DistinguishedName
979 * @param ads connection to ads server
980 * @param res ** which will contain results - free res* with ads_msgfree()
981 * @param dn DistinguishName to search
982 * @param attrs Attributes to retrieve
983 * @return status of search
985 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
986 const char *dn, const char **attrs)
988 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
989 attrs, res);
993 * Free up memory from a ads_search
994 * @param ads connection to ads server
995 * @param msg Search results to free
997 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
999 if (!msg) return;
1000 ldap_msgfree(msg);
1004 * Free up memory from various ads requests
1005 * @param ads connection to ads server
1006 * @param mem Area to free
1008 void ads_memfree(ADS_STRUCT *ads, void *mem)
1010 SAFE_FREE(mem);
1014 * Get a dn from search results
1015 * @param ads connection to ads server
1016 * @param msg Search result
1017 * @return dn string
1019 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1021 char *utf8_dn, *unix_dn;
1023 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1025 if (!utf8_dn) {
1026 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1027 return NULL;
1030 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1031 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1032 utf8_dn ));
1033 return NULL;
1035 ldap_memfree(utf8_dn);
1036 return unix_dn;
1040 * Get the parent from a dn
1041 * @param dn the dn to return the parent from
1042 * @return parent dn string
1044 char *ads_parent_dn(const char *dn)
1046 char *p;
1048 if (dn == NULL) {
1049 return NULL;
1052 p = strchr(dn, ',');
1054 if (p == NULL) {
1055 return NULL;
1058 return p+1;
1062 * Find a machine account given a hostname
1063 * @param ads connection to ads server
1064 * @param res ** which will contain results - free res* with ads_msgfree()
1065 * @param host Hostname to search for
1066 * @return status of search
1068 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1069 const char *machine)
1071 ADS_STATUS status;
1072 char *expr;
1073 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1075 *res = NULL;
1077 /* the easiest way to find a machine account anywhere in the tree
1078 is to look for hostname$ */
1079 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1080 DEBUG(1, ("asprintf failed!\n"));
1081 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1084 status = ads_search(ads, res, expr, attrs);
1085 SAFE_FREE(expr);
1086 return status;
1090 * Initialize a list of mods to be used in a modify request
1091 * @param ctx An initialized TALLOC_CTX
1092 * @return allocated ADS_MODLIST
1094 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1096 #define ADS_MODLIST_ALLOC_SIZE 10
1097 LDAPMod **mods;
1099 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1100 /* -1 is safety to make sure we don't go over the end.
1101 need to reset it to NULL before doing ldap modify */
1102 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1104 return (ADS_MODLIST)mods;
1109 add an attribute to the list, with values list already constructed
1111 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1112 int mod_op, const char *name,
1113 const void *_invals)
1115 const void **invals = (const void **)_invals;
1116 int curmod;
1117 LDAPMod **modlist = (LDAPMod **) *mods;
1118 struct berval **ber_values = NULL;
1119 char **char_values = NULL;
1121 if (!invals) {
1122 mod_op = LDAP_MOD_DELETE;
1123 } else {
1124 if (mod_op & LDAP_MOD_BVALUES)
1125 ber_values = ads_dup_values(ctx,
1126 (const struct berval **)invals);
1127 else
1128 char_values = ads_push_strvals(ctx,
1129 (const char **) invals);
1132 /* find the first empty slot */
1133 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1134 curmod++);
1135 if (modlist[curmod] == (LDAPMod *) -1) {
1136 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1137 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1138 return ADS_ERROR(LDAP_NO_MEMORY);
1139 memset(&modlist[curmod], 0,
1140 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1141 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1142 *mods = (ADS_MODLIST)modlist;
1145 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1146 return ADS_ERROR(LDAP_NO_MEMORY);
1147 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1148 if (mod_op & LDAP_MOD_BVALUES) {
1149 modlist[curmod]->mod_bvalues = ber_values;
1150 } else if (mod_op & LDAP_MOD_DELETE) {
1151 modlist[curmod]->mod_values = NULL;
1152 } else {
1153 modlist[curmod]->mod_values = char_values;
1156 modlist[curmod]->mod_op = mod_op;
1157 return ADS_ERROR(LDAP_SUCCESS);
1161 * Add a single string value to a mod list
1162 * @param ctx An initialized TALLOC_CTX
1163 * @param mods An initialized ADS_MODLIST
1164 * @param name The attribute name to add
1165 * @param val The value to add - NULL means DELETE
1166 * @return ADS STATUS indicating success of add
1168 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1169 const char *name, const char *val)
1171 const char *values[2];
1173 values[0] = val;
1174 values[1] = NULL;
1176 if (!val)
1177 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1178 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1182 * Add an array of string values to a mod list
1183 * @param ctx An initialized TALLOC_CTX
1184 * @param mods An initialized ADS_MODLIST
1185 * @param name The attribute name to add
1186 * @param vals The array of string values to add - NULL means DELETE
1187 * @return ADS STATUS indicating success of add
1189 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1190 const char *name, const char **vals)
1192 if (!vals)
1193 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1194 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1195 name, (const void **) vals);
1198 #if 0
1200 * Add a single ber-encoded value to a mod list
1201 * @param ctx An initialized TALLOC_CTX
1202 * @param mods An initialized ADS_MODLIST
1203 * @param name The attribute name to add
1204 * @param val The value to add - NULL means DELETE
1205 * @return ADS STATUS indicating success of add
1207 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1208 const char *name, const struct berval *val)
1210 const struct berval *values[2];
1212 values[0] = val;
1213 values[1] = NULL;
1214 if (!val)
1215 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1216 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1217 name, (const void **) values);
1219 #endif
1222 * Perform an ldap modify
1223 * @param ads connection to ads server
1224 * @param mod_dn DistinguishedName to modify
1225 * @param mods list of modifications to perform
1226 * @return status of modify
1228 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1230 int ret,i;
1231 char *utf8_dn = NULL;
1233 this control is needed to modify that contains a currently
1234 non-existent attribute (but allowable for the object) to run
1236 LDAPControl PermitModify = {
1237 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1238 {0, NULL},
1239 (char) 1};
1240 LDAPControl *controls[2];
1242 controls[0] = &PermitModify;
1243 controls[1] = NULL;
1245 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1246 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1249 /* find the end of the list, marked by NULL or -1 */
1250 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1251 /* make sure the end of the list is NULL */
1252 mods[i] = NULL;
1253 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1254 (LDAPMod **) mods, controls, NULL);
1255 SAFE_FREE(utf8_dn);
1256 return ADS_ERROR(ret);
1260 * Perform an ldap add
1261 * @param ads connection to ads server
1262 * @param new_dn DistinguishedName to add
1263 * @param mods list of attributes and values for DN
1264 * @return status of add
1266 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1268 int ret, i;
1269 char *utf8_dn = NULL;
1271 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1272 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1273 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1276 /* find the end of the list, marked by NULL or -1 */
1277 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1278 /* make sure the end of the list is NULL */
1279 mods[i] = NULL;
1281 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1282 SAFE_FREE(utf8_dn);
1283 return ADS_ERROR(ret);
1287 * Delete a DistinguishedName
1288 * @param ads connection to ads server
1289 * @param new_dn DistinguishedName to delete
1290 * @return status of delete
1292 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1294 int ret;
1295 char *utf8_dn = NULL;
1296 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1297 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1298 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1301 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1302 SAFE_FREE(utf8_dn);
1303 return ADS_ERROR(ret);
1307 * Build an org unit string
1308 * if org unit is Computers or blank then assume a container, otherwise
1309 * assume a / separated list of organisational units.
1310 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1311 * @param ads connection to ads server
1312 * @param org_unit Organizational unit
1313 * @return org unit string - caller must free
1315 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1317 char *ret = NULL;
1319 if (!org_unit || !*org_unit) {
1321 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1323 /* samba4 might not yet respond to a wellknownobject-query */
1324 return ret ? ret : SMB_STRDUP("cn=Computers");
1327 if (strequal(org_unit, "Computers")) {
1328 return SMB_STRDUP("cn=Computers");
1331 /* jmcd: removed "\\" from the separation chars, because it is
1332 needed as an escape for chars like '#' which are valid in an
1333 OU name */
1334 return ads_build_path(org_unit, "/", "ou=", 1);
1338 * Get a org unit string for a well-known GUID
1339 * @param ads connection to ads server
1340 * @param wknguid Well known GUID
1341 * @return org unit string - caller must free
1343 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1345 ADS_STATUS status;
1346 LDAPMessage *res = NULL;
1347 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1348 **bind_dn_exp = NULL;
1349 const char *attrs[] = {"distinguishedName", NULL};
1350 int new_ln, wkn_ln, bind_ln, i;
1352 if (wknguid == NULL) {
1353 return NULL;
1356 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1357 DEBUG(1, ("asprintf failed!\n"));
1358 return NULL;
1361 status = ads_search_dn(ads, &res, base, attrs);
1362 if (!ADS_ERR_OK(status)) {
1363 DEBUG(1,("Failed while searching for: %s\n", base));
1364 goto out;
1367 if (ads_count_replies(ads, res) != 1) {
1368 goto out;
1371 /* substitute the bind-path from the well-known-guid-search result */
1372 wkn_dn = ads_get_dn(ads, res);
1373 if (!wkn_dn) {
1374 goto out;
1377 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1378 if (!wkn_dn_exp) {
1379 goto out;
1382 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1383 if (!bind_dn_exp) {
1384 goto out;
1387 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1389 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1392 new_ln = wkn_ln - bind_ln;
1394 ret = SMB_STRDUP(wkn_dn_exp[0]);
1395 if (!ret) {
1396 goto out;
1399 for (i=1; i < new_ln; i++) {
1400 char *s = NULL;
1402 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1403 SAFE_FREE(ret);
1404 goto out;
1407 SAFE_FREE(ret);
1408 ret = SMB_STRDUP(s);
1409 free(s);
1410 if (!ret) {
1411 goto out;
1415 out:
1416 SAFE_FREE(base);
1417 ads_msgfree(ads, res);
1418 ads_memfree(ads, wkn_dn);
1419 if (wkn_dn_exp) {
1420 ldap_value_free(wkn_dn_exp);
1422 if (bind_dn_exp) {
1423 ldap_value_free(bind_dn_exp);
1426 return ret;
1430 * Adds (appends) an item to an attribute array, rather then
1431 * replacing the whole list
1432 * @param ctx An initialized TALLOC_CTX
1433 * @param mods An initialized ADS_MODLIST
1434 * @param name name of the ldap attribute to append to
1435 * @param vals an array of values to add
1436 * @return status of addition
1439 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1440 const char *name, const char **vals)
1442 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1443 (const void *) vals);
1447 * Determines the computer account's current KVNO via an LDAP lookup
1448 * @param ads An initialized ADS_STRUCT
1449 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1450 * @return the kvno for the computer account, or -1 in case of a failure.
1453 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1455 LDAPMessage *res = NULL;
1456 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1457 char *filter;
1458 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1459 char *dn_string = NULL;
1460 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1462 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1463 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1464 return kvno;
1466 ret = ads_search(ads, &res, filter, attrs);
1467 SAFE_FREE(filter);
1468 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1469 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1470 ads_msgfree(ads, res);
1471 return kvno;
1474 dn_string = ads_get_dn(ads, res);
1475 if (!dn_string) {
1476 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1477 ads_msgfree(ads, res);
1478 return kvno;
1480 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1481 ads_memfree(ads, dn_string);
1483 /* ---------------------------------------------------------
1484 * 0 is returned as a default KVNO from this point on...
1485 * This is done because Windows 2000 does not support key
1486 * version numbers. Chances are that a failure in the next
1487 * step is simply due to Windows 2000 being used for a
1488 * domain controller. */
1489 kvno = 0;
1491 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1492 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1493 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1494 ads_msgfree(ads, res);
1495 return kvno;
1498 /* Success */
1499 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1500 ads_msgfree(ads, res);
1501 return kvno;
1505 * This clears out all registered spn's for a given hostname
1506 * @param ads An initilaized ADS_STRUCT
1507 * @param machine_name the NetBIOS name of the computer.
1508 * @return 0 upon success, non-zero otherwise.
1511 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1513 TALLOC_CTX *ctx;
1514 LDAPMessage *res = NULL;
1515 ADS_MODLIST mods;
1516 const char *servicePrincipalName[1] = {NULL};
1517 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1518 char *dn_string = NULL;
1520 ret = ads_find_machine_acct(ads, &res, machine_name);
1521 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1522 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1523 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1524 ads_msgfree(ads, res);
1525 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1528 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1529 ctx = talloc_init("ads_clear_service_principal_names");
1530 if (!ctx) {
1531 ads_msgfree(ads, res);
1532 return ADS_ERROR(LDAP_NO_MEMORY);
1535 if (!(mods = ads_init_mods(ctx))) {
1536 talloc_destroy(ctx);
1537 ads_msgfree(ads, res);
1538 return ADS_ERROR(LDAP_NO_MEMORY);
1540 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1541 if (!ADS_ERR_OK(ret)) {
1542 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1543 ads_msgfree(ads, res);
1544 talloc_destroy(ctx);
1545 return ret;
1547 dn_string = ads_get_dn(ads, res);
1548 if (!dn_string) {
1549 talloc_destroy(ctx);
1550 ads_msgfree(ads, res);
1551 return ADS_ERROR(LDAP_NO_MEMORY);
1553 ret = ads_gen_mod(ads, dn_string, mods);
1554 ads_memfree(ads,dn_string);
1555 if (!ADS_ERR_OK(ret)) {
1556 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1557 machine_name));
1558 ads_msgfree(ads, res);
1559 talloc_destroy(ctx);
1560 return ret;
1563 ads_msgfree(ads, res);
1564 talloc_destroy(ctx);
1565 return ret;
1569 * This adds a service principal name to an existing computer account
1570 * (found by hostname) in AD.
1571 * @param ads An initialized ADS_STRUCT
1572 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1573 * @param my_fqdn The fully qualified DNS name of the machine
1574 * @param spn A string of the service principal to add, i.e. 'host'
1575 * @return 0 upon sucess, or non-zero if a failure occurs
1578 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1579 const char *my_fqdn, const char *spn)
1581 ADS_STATUS ret;
1582 TALLOC_CTX *ctx;
1583 LDAPMessage *res = NULL;
1584 char *psp1, *psp2;
1585 ADS_MODLIST mods;
1586 char *dn_string = NULL;
1587 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1589 ret = ads_find_machine_acct(ads, &res, machine_name);
1590 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1591 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1592 machine_name));
1593 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1594 spn, machine_name, ads->config.realm));
1595 ads_msgfree(ads, res);
1596 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1599 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1600 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1601 ads_msgfree(ads, res);
1602 return ADS_ERROR(LDAP_NO_MEMORY);
1605 /* add short name spn */
1607 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1608 talloc_destroy(ctx);
1609 ads_msgfree(ads, res);
1610 return ADS_ERROR(LDAP_NO_MEMORY);
1612 strupper_m(psp1);
1613 strlower_m(&psp1[strlen(spn)]);
1614 servicePrincipalName[0] = psp1;
1616 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1617 psp1, machine_name));
1620 /* add fully qualified spn */
1622 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1623 ret = ADS_ERROR(LDAP_NO_MEMORY);
1624 goto out;
1626 strupper_m(psp2);
1627 strlower_m(&psp2[strlen(spn)]);
1628 servicePrincipalName[1] = psp2;
1630 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1631 psp2, machine_name));
1633 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1634 ret = ADS_ERROR(LDAP_NO_MEMORY);
1635 goto out;
1638 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1639 if (!ADS_ERR_OK(ret)) {
1640 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1641 goto out;
1644 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1645 ret = ADS_ERROR(LDAP_NO_MEMORY);
1646 goto out;
1649 ret = ads_gen_mod(ads, dn_string, mods);
1650 ads_memfree(ads,dn_string);
1651 if (!ADS_ERR_OK(ret)) {
1652 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1653 goto out;
1656 out:
1657 TALLOC_FREE( ctx );
1658 ads_msgfree(ads, res);
1659 return ret;
1663 * adds a machine account to the ADS server
1664 * @param ads An intialized ADS_STRUCT
1665 * @param machine_name - the NetBIOS machine name of this account.
1666 * @param account_type A number indicating the type of account to create
1667 * @param org_unit The LDAP path in which to place this account
1668 * @return 0 upon success, or non-zero otherwise
1671 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1672 const char *org_unit)
1674 ADS_STATUS ret;
1675 char *samAccountName, *controlstr;
1676 TALLOC_CTX *ctx;
1677 ADS_MODLIST mods;
1678 char *machine_escaped = NULL;
1679 char *new_dn;
1680 const char *objectClass[] = {"top", "person", "organizationalPerson",
1681 "user", "computer", NULL};
1682 LDAPMessage *res = NULL;
1683 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1684 UF_DONT_EXPIRE_PASSWD |\
1685 UF_ACCOUNTDISABLE );
1687 if (!(ctx = talloc_init("ads_add_machine_acct")))
1688 return ADS_ERROR(LDAP_NO_MEMORY);
1690 ret = ADS_ERROR(LDAP_NO_MEMORY);
1692 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1693 if (!machine_escaped) {
1694 goto done;
1697 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1698 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1700 if ( !new_dn || !samAccountName ) {
1701 goto done;
1704 #ifndef ENCTYPE_ARCFOUR_HMAC
1705 acct_control |= UF_USE_DES_KEY_ONLY;
1706 #endif
1708 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1709 goto done;
1712 if (!(mods = ads_init_mods(ctx))) {
1713 goto done;
1716 ads_mod_str(ctx, &mods, "cn", machine_name);
1717 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1718 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1719 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1721 ret = ads_gen_add(ads, new_dn, mods);
1723 done:
1724 SAFE_FREE(machine_escaped);
1725 ads_msgfree(ads, res);
1726 talloc_destroy(ctx);
1728 return ret;
1732 * move a machine account to another OU on the ADS server
1733 * @param ads - An intialized ADS_STRUCT
1734 * @param machine_name - the NetBIOS machine name of this account.
1735 * @param org_unit - The LDAP path in which to place this account
1736 * @param moved - whether we moved the machine account (optional)
1737 * @return 0 upon success, or non-zero otherwise
1740 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1741 const char *org_unit, BOOL *moved)
1743 ADS_STATUS rc;
1744 int ldap_status;
1745 LDAPMessage *res = NULL;
1746 char *filter = NULL;
1747 char *computer_dn = NULL;
1748 char *parent_dn;
1749 char *computer_rdn = NULL;
1750 BOOL need_move = False;
1752 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1753 rc = ADS_ERROR(LDAP_NO_MEMORY);
1754 goto done;
1757 /* Find pre-existing machine */
1758 rc = ads_search(ads, &res, filter, NULL);
1759 if (!ADS_ERR_OK(rc)) {
1760 goto done;
1763 computer_dn = ads_get_dn(ads, res);
1764 if (!computer_dn) {
1765 rc = ADS_ERROR(LDAP_NO_MEMORY);
1766 goto done;
1769 parent_dn = ads_parent_dn(computer_dn);
1770 if (strequal(parent_dn, org_unit)) {
1771 goto done;
1774 need_move = True;
1776 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1777 rc = ADS_ERROR(LDAP_NO_MEMORY);
1778 goto done;
1781 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1782 org_unit, 1, NULL, NULL);
1783 rc = ADS_ERROR(ldap_status);
1785 done:
1786 ads_msgfree(ads, res);
1787 SAFE_FREE(filter);
1788 SAFE_FREE(computer_dn);
1789 SAFE_FREE(computer_rdn);
1791 if (!ADS_ERR_OK(rc)) {
1792 need_move = False;
1795 if (moved) {
1796 *moved = need_move;
1799 return rc;
1803 dump a binary result from ldap
1805 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1807 int i, j;
1808 for (i=0; values[i]; i++) {
1809 printf("%s: ", field);
1810 for (j=0; j<values[i]->bv_len; j++) {
1811 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1813 printf("\n");
1817 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1819 int i;
1820 UUID_FLAT guid;
1821 for (i=0; values[i]; i++) {
1822 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1823 printf("%s: %s\n", field,
1824 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1829 dump a sid result from ldap
1831 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1833 int i;
1834 for (i=0; values[i]; i++) {
1835 DOM_SID sid;
1836 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1837 printf("%s: %s\n", field, sid_string_static(&sid));
1842 dump ntSecurityDescriptor
1844 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1846 prs_struct ps;
1848 SEC_DESC *psd = 0;
1849 TALLOC_CTX *ctx = 0;
1851 if (!(ctx = talloc_init("sec_io_desc")))
1852 return;
1854 /* prepare data */
1855 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1856 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1857 prs_set_offset(&ps,0);
1859 /* parse secdesc */
1860 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1861 prs_mem_free(&ps);
1862 talloc_destroy(ctx);
1863 return;
1865 if (psd) {
1866 ads_disp_sd(ads, ctx, psd);
1869 prs_mem_free(&ps);
1870 talloc_destroy(ctx);
1874 dump a string result from ldap
1876 static void dump_string(const char *field, char **values)
1878 int i;
1879 for (i=0; values[i]; i++) {
1880 printf("%s: %s\n", field, values[i]);
1885 dump a field from LDAP on stdout
1886 used for debugging
1889 static BOOL ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1891 const struct {
1892 const char *name;
1893 BOOL string;
1894 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1895 } handlers[] = {
1896 {"objectGUID", False, dump_guid},
1897 {"netbootGUID", False, dump_guid},
1898 {"nTSecurityDescriptor", False, dump_sd},
1899 {"dnsRecord", False, dump_binary},
1900 {"objectSid", False, dump_sid},
1901 {"tokenGroups", False, dump_sid},
1902 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1903 {"tokengroupsGlobalandUniversal", False, dump_sid},
1904 {"mS-DS-CreatorSID", False, dump_sid},
1905 {"msExchMailboxGuid", False, dump_guid},
1906 {NULL, True, NULL}
1908 int i;
1910 if (!field) { /* must be end of an entry */
1911 printf("\n");
1912 return False;
1915 for (i=0; handlers[i].name; i++) {
1916 if (StrCaseCmp(handlers[i].name, field) == 0) {
1917 if (!values) /* first time, indicate string or not */
1918 return handlers[i].string;
1919 handlers[i].handler(ads, field, (struct berval **) values);
1920 break;
1923 if (!handlers[i].name) {
1924 if (!values) /* first time, indicate string conversion */
1925 return True;
1926 dump_string(field, (char **)values);
1928 return False;
1932 * Dump a result from LDAP on stdout
1933 * used for debugging
1934 * @param ads connection to ads server
1935 * @param res Results to dump
1938 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1940 ads_process_results(ads, res, ads_dump_field, NULL);
1944 * Walk through results, calling a function for each entry found.
1945 * The function receives a field name, a berval * array of values,
1946 * and a data area passed through from the start. The function is
1947 * called once with null for field and values at the end of each
1948 * entry.
1949 * @param ads connection to ads server
1950 * @param res Results to process
1951 * @param fn Function for processing each result
1952 * @param data_area user-defined area to pass to function
1954 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1955 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
1956 void *data_area)
1958 LDAPMessage *msg;
1959 TALLOC_CTX *ctx;
1961 if (!(ctx = talloc_init("ads_process_results")))
1962 return;
1964 for (msg = ads_first_entry(ads, res); msg;
1965 msg = ads_next_entry(ads, msg)) {
1966 char *utf8_field;
1967 BerElement *b;
1969 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1970 (LDAPMessage *)msg,&b);
1971 utf8_field;
1972 utf8_field=ldap_next_attribute(ads->ldap.ld,
1973 (LDAPMessage *)msg,b)) {
1974 struct berval **ber_vals;
1975 char **str_vals, **utf8_vals;
1976 char *field;
1977 BOOL string;
1979 pull_utf8_talloc(ctx, &field, utf8_field);
1980 string = fn(ads, field, NULL, data_area);
1982 if (string) {
1983 utf8_vals = ldap_get_values(ads->ldap.ld,
1984 (LDAPMessage *)msg, field);
1985 str_vals = ads_pull_strvals(ctx,
1986 (const char **) utf8_vals);
1987 fn(ads, field, (void **) str_vals, data_area);
1988 ldap_value_free(utf8_vals);
1989 } else {
1990 ber_vals = ldap_get_values_len(ads->ldap.ld,
1991 (LDAPMessage *)msg, field);
1992 fn(ads, field, (void **) ber_vals, data_area);
1994 ldap_value_free_len(ber_vals);
1996 ldap_memfree(utf8_field);
1998 ber_free(b, 0);
1999 talloc_free_children(ctx);
2000 fn(ads, NULL, NULL, data_area); /* completed an entry */
2003 talloc_destroy(ctx);
2007 * count how many replies are in a LDAPMessage
2008 * @param ads connection to ads server
2009 * @param res Results to count
2010 * @return number of replies
2012 int ads_count_replies(ADS_STRUCT *ads, void *res)
2014 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2018 * pull the first entry from a ADS result
2019 * @param ads connection to ads server
2020 * @param res Results of search
2021 * @return first entry from result
2023 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2025 return ldap_first_entry(ads->ldap.ld, res);
2029 * pull the next entry from a ADS result
2030 * @param ads connection to ads server
2031 * @param res Results of search
2032 * @return next entry from result
2034 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2036 return ldap_next_entry(ads->ldap.ld, res);
2040 * pull a single string from a ADS result
2041 * @param ads connection to ads server
2042 * @param mem_ctx TALLOC_CTX to use for allocating result string
2043 * @param msg Results of search
2044 * @param field Attribute to retrieve
2045 * @return Result string in talloc context
2047 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2048 const char *field)
2050 char **values;
2051 char *ret = NULL;
2052 char *ux_string;
2053 size_t rc;
2055 values = ldap_get_values(ads->ldap.ld, msg, field);
2056 if (!values)
2057 return NULL;
2059 if (values[0]) {
2060 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2061 values[0]);
2062 if (rc != (size_t)-1)
2063 ret = ux_string;
2066 ldap_value_free(values);
2067 return ret;
2071 * pull an array of strings from a ADS result
2072 * @param ads connection to ads server
2073 * @param mem_ctx TALLOC_CTX to use for allocating result string
2074 * @param msg Results of search
2075 * @param field Attribute to retrieve
2076 * @return Result strings in talloc context
2078 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2079 LDAPMessage *msg, const char *field,
2080 size_t *num_values)
2082 char **values;
2083 char **ret = NULL;
2084 int i;
2086 values = ldap_get_values(ads->ldap.ld, msg, field);
2087 if (!values)
2088 return NULL;
2090 *num_values = ldap_count_values(values);
2092 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2093 if (!ret) {
2094 ldap_value_free(values);
2095 return NULL;
2098 for (i=0;i<*num_values;i++) {
2099 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2100 ldap_value_free(values);
2101 return NULL;
2104 ret[i] = NULL;
2106 ldap_value_free(values);
2107 return ret;
2111 * pull an array of strings from a ADS result
2112 * (handle large multivalue attributes with range retrieval)
2113 * @param ads connection to ads server
2114 * @param mem_ctx TALLOC_CTX to use for allocating result string
2115 * @param msg Results of search
2116 * @param field Attribute to retrieve
2117 * @param current_strings strings returned by a previous call to this function
2118 * @param next_attribute The next query should ask for this attribute
2119 * @param num_values How many values did we get this time?
2120 * @param more_values Are there more values to get?
2121 * @return Result strings in talloc context
2123 char **ads_pull_strings_range(ADS_STRUCT *ads,
2124 TALLOC_CTX *mem_ctx,
2125 LDAPMessage *msg, const char *field,
2126 char **current_strings,
2127 const char **next_attribute,
2128 size_t *num_strings,
2129 BOOL *more_strings)
2131 char *attr;
2132 char *expected_range_attrib, *range_attr;
2133 BerElement *ptr = NULL;
2134 char **strings;
2135 char **new_strings;
2136 size_t num_new_strings;
2137 unsigned long int range_start;
2138 unsigned long int range_end;
2140 /* we might have been given the whole lot anyway */
2141 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2142 *more_strings = False;
2143 return strings;
2146 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2148 /* look for Range result */
2149 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2150 attr;
2151 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2152 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2153 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2154 range_attr = attr;
2155 break;
2157 ldap_memfree(attr);
2159 if (!attr) {
2160 ber_free(ptr, 0);
2161 /* nothing here - this field is just empty */
2162 *more_strings = False;
2163 return NULL;
2166 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2167 &range_start, &range_end) == 2) {
2168 *more_strings = True;
2169 } else {
2170 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2171 &range_start) == 1) {
2172 *more_strings = False;
2173 } else {
2174 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2175 range_attr));
2176 ldap_memfree(range_attr);
2177 *more_strings = False;
2178 return NULL;
2182 if ((*num_strings) != range_start) {
2183 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2184 " - aborting range retreival\n",
2185 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2186 ldap_memfree(range_attr);
2187 *more_strings = False;
2188 return NULL;
2191 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2193 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2194 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2195 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2196 range_attr, (unsigned long int)range_end - range_start + 1,
2197 (unsigned long int)num_new_strings));
2198 ldap_memfree(range_attr);
2199 *more_strings = False;
2200 return NULL;
2203 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2204 *num_strings + num_new_strings);
2206 if (strings == NULL) {
2207 ldap_memfree(range_attr);
2208 *more_strings = False;
2209 return NULL;
2212 if (new_strings && num_new_strings) {
2213 memcpy(&strings[*num_strings], new_strings,
2214 sizeof(*new_strings) * num_new_strings);
2217 (*num_strings) += num_new_strings;
2219 if (*more_strings) {
2220 *next_attribute = talloc_asprintf(mem_ctx,
2221 "%s;range=%d-*",
2222 field,
2223 (int)*num_strings);
2225 if (!*next_attribute) {
2226 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2227 ldap_memfree(range_attr);
2228 *more_strings = False;
2229 return NULL;
2233 ldap_memfree(range_attr);
2235 return strings;
2239 * pull a single uint32 from a ADS result
2240 * @param ads connection to ads server
2241 * @param msg Results of search
2242 * @param field Attribute to retrieve
2243 * @param v Pointer to int to store result
2244 * @return boolean inidicating success
2246 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2247 uint32 *v)
2249 char **values;
2251 values = ldap_get_values(ads->ldap.ld, msg, field);
2252 if (!values)
2253 return False;
2254 if (!values[0]) {
2255 ldap_value_free(values);
2256 return False;
2259 *v = atoi(values[0]);
2260 ldap_value_free(values);
2261 return True;
2265 * pull a single objectGUID from an ADS result
2266 * @param ads connection to ADS server
2267 * @param msg results of search
2268 * @param guid 37-byte area to receive text guid
2269 * @return boolean indicating success
2271 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2273 char **values;
2274 UUID_FLAT flat_guid;
2276 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2277 if (!values)
2278 return False;
2280 if (values[0]) {
2281 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2282 smb_uuid_unpack(flat_guid, guid);
2283 ldap_value_free(values);
2284 return True;
2286 ldap_value_free(values);
2287 return False;
2293 * pull a single DOM_SID from a ADS result
2294 * @param ads connection to ads server
2295 * @param msg Results of search
2296 * @param field Attribute to retrieve
2297 * @param sid Pointer to sid to store result
2298 * @return boolean inidicating success
2300 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2301 DOM_SID *sid)
2303 struct berval **values;
2304 BOOL ret = False;
2306 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2308 if (!values)
2309 return False;
2311 if (values[0])
2312 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2314 ldap_value_free_len(values);
2315 return ret;
2319 * pull an array of DOM_SIDs from a ADS result
2320 * @param ads connection to ads server
2321 * @param mem_ctx TALLOC_CTX for allocating sid array
2322 * @param msg Results of search
2323 * @param field Attribute to retrieve
2324 * @param sids pointer to sid array to allocate
2325 * @return the count of SIDs pulled
2327 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2328 LDAPMessage *msg, const char *field, DOM_SID **sids)
2330 struct berval **values;
2331 BOOL ret;
2332 int count, i;
2334 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2336 if (!values)
2337 return 0;
2339 for (i=0; values[i]; i++)
2340 /* nop */ ;
2342 if (i) {
2343 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2344 if (!(*sids)) {
2345 ldap_value_free_len(values);
2346 return 0;
2348 } else {
2349 (*sids) = NULL;
2352 count = 0;
2353 for (i=0; values[i]; i++) {
2354 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2355 if (ret) {
2356 fstring sid;
2357 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2358 count++;
2362 ldap_value_free_len(values);
2363 return count;
2367 * pull a SEC_DESC from a ADS result
2368 * @param ads connection to ads server
2369 * @param mem_ctx TALLOC_CTX for allocating sid array
2370 * @param msg Results of search
2371 * @param field Attribute to retrieve
2372 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2373 * @return boolean inidicating success
2375 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2376 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2378 struct berval **values;
2379 BOOL ret = False;
2381 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2383 if (!values) return False;
2385 if (values[0]) {
2386 prs_struct ps;
2387 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2388 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2389 prs_set_offset(&ps,0);
2391 ret = sec_io_desc("sd", sd, &ps, 1);
2392 prs_mem_free(&ps);
2395 ldap_value_free_len(values);
2396 return ret;
2400 * in order to support usernames longer than 21 characters we need to
2401 * use both the sAMAccountName and the userPrincipalName attributes
2402 * It seems that not all users have the userPrincipalName attribute set
2404 * @param ads connection to ads server
2405 * @param mem_ctx TALLOC_CTX for allocating sid array
2406 * @param msg Results of search
2407 * @return the username
2409 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2410 LDAPMessage *msg)
2412 #if 0 /* JERRY */
2413 char *ret, *p;
2415 /* lookup_name() only works on the sAMAccountName to
2416 returning the username portion of userPrincipalName
2417 breaks winbindd_getpwnam() */
2419 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2420 if (ret && (p = strchr_m(ret, '@'))) {
2421 *p = 0;
2422 return ret;
2424 #endif
2425 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2430 * find the update serial number - this is the core of the ldap cache
2431 * @param ads connection to ads server
2432 * @param ads connection to ADS server
2433 * @param usn Pointer to retrieved update serial number
2434 * @return status of search
2436 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2438 const char *attrs[] = {"highestCommittedUSN", NULL};
2439 ADS_STATUS status;
2440 LDAPMessage *res;
2442 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2443 if (!ADS_ERR_OK(status))
2444 return status;
2446 if (ads_count_replies(ads, res) != 1) {
2447 ads_msgfree(ads, res);
2448 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2451 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2452 ads_msgfree(ads, res);
2453 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2456 ads_msgfree(ads, res);
2457 return ADS_SUCCESS;
2460 /* parse a ADS timestring - typical string is
2461 '20020917091222.0Z0' which means 09:12.22 17th September
2462 2002, timezone 0 */
2463 static time_t ads_parse_time(const char *str)
2465 struct tm tm;
2467 ZERO_STRUCT(tm);
2469 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2470 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2471 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2472 return 0;
2474 tm.tm_year -= 1900;
2475 tm.tm_mon -= 1;
2477 return timegm(&tm);
2480 /********************************************************************
2481 ********************************************************************/
2483 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2485 const char *attrs[] = {"currentTime", NULL};
2486 ADS_STATUS status;
2487 LDAPMessage *res;
2488 char *timestr;
2489 TALLOC_CTX *ctx;
2490 ADS_STRUCT *ads_s = ads;
2492 if (!(ctx = talloc_init("ads_current_time"))) {
2493 return ADS_ERROR(LDAP_NO_MEMORY);
2496 /* establish a new ldap tcp session if necessary */
2498 if ( !ads->ldap.ld ) {
2499 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2500 ads->server.ldap_server )) == NULL )
2502 goto done;
2504 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2505 status = ads_connect( ads_s );
2506 if ( !ADS_ERR_OK(status))
2507 goto done;
2510 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2511 if (!ADS_ERR_OK(status)) {
2512 goto done;
2515 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2516 if (!timestr) {
2517 ads_msgfree(ads_s, res);
2518 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2519 goto done;
2522 /* but save the time and offset in the original ADS_STRUCT */
2524 ads->config.current_time = ads_parse_time(timestr);
2526 if (ads->config.current_time != 0) {
2527 ads->auth.time_offset = ads->config.current_time - time(NULL);
2528 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2531 ads_msgfree(ads, res);
2533 status = ADS_SUCCESS;
2535 done:
2536 /* free any temporary ads connections */
2537 if ( ads_s != ads ) {
2538 ads_destroy( &ads_s );
2540 talloc_destroy(ctx);
2542 return status;
2545 /********************************************************************
2546 ********************************************************************/
2548 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2550 const char *attrs[] = {"domainFunctionality", NULL};
2551 ADS_STATUS status;
2552 LDAPMessage *res;
2553 ADS_STRUCT *ads_s = ads;
2555 *val = DS_DOMAIN_FUNCTION_2000;
2557 /* establish a new ldap tcp session if necessary */
2559 if ( !ads->ldap.ld ) {
2560 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2561 ads->server.ldap_server )) == NULL )
2563 goto done;
2565 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2566 status = ads_connect( ads_s );
2567 if ( !ADS_ERR_OK(status))
2568 goto done;
2571 /* If the attribute does not exist assume it is a Windows 2000
2572 functional domain */
2574 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2575 if (!ADS_ERR_OK(status)) {
2576 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2577 status = ADS_SUCCESS;
2579 goto done;
2582 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2583 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2585 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2588 ads_msgfree(ads, res);
2590 done:
2591 /* free any temporary ads connections */
2592 if ( ads_s != ads ) {
2593 ads_destroy( &ads_s );
2596 return status;
2600 * find the domain sid for our domain
2601 * @param ads connection to ads server
2602 * @param sid Pointer to domain sid
2603 * @return status of search
2605 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2607 const char *attrs[] = {"objectSid", NULL};
2608 LDAPMessage *res;
2609 ADS_STATUS rc;
2611 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2612 attrs, &res);
2613 if (!ADS_ERR_OK(rc)) return rc;
2614 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2615 ads_msgfree(ads, res);
2616 return ADS_ERROR_SYSTEM(ENOENT);
2618 ads_msgfree(ads, res);
2620 return ADS_SUCCESS;
2624 * find our site name
2625 * @param ads connection to ads server
2626 * @param mem_ctx Pointer to talloc context
2627 * @param site_name Pointer to the sitename
2628 * @return status of search
2630 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2632 ADS_STATUS status;
2633 LDAPMessage *res;
2634 const char *dn, *service_name;
2635 const char *attrs[] = { "dsServiceName", NULL };
2637 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2638 if (!ADS_ERR_OK(status)) {
2639 return status;
2642 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2643 if (service_name == NULL) {
2644 ads_msgfree(ads, res);
2645 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2648 ads_msgfree(ads, res);
2650 /* go up three levels */
2651 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2652 if (dn == NULL) {
2653 return ADS_ERROR(LDAP_NO_MEMORY);
2656 *site_name = talloc_strdup(mem_ctx, dn);
2657 if (*site_name == NULL) {
2658 return ADS_ERROR(LDAP_NO_MEMORY);
2661 return status;
2663 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2668 * find the site dn where a machine resides
2669 * @param ads connection to ads server
2670 * @param mem_ctx Pointer to talloc context
2671 * @param computer_name name of the machine
2672 * @param site_name Pointer to the sitename
2673 * @return status of search
2675 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2677 ADS_STATUS status;
2678 LDAPMessage *res;
2679 const char *parent, *filter;
2680 char *config_context = NULL;
2681 char *dn;
2683 /* shortcut a query */
2684 if (strequal(computer_name, ads->config.ldap_server_name)) {
2685 return ads_site_dn(ads, mem_ctx, site_dn);
2688 status = ads_config_path(ads, mem_ctx, &config_context);
2689 if (!ADS_ERR_OK(status)) {
2690 return status;
2693 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2694 if (filter == NULL) {
2695 return ADS_ERROR(LDAP_NO_MEMORY);
2698 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2699 filter, NULL, &res);
2700 if (!ADS_ERR_OK(status)) {
2701 return status;
2704 if (ads_count_replies(ads, res) != 1) {
2705 ads_msgfree(ads, res);
2706 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2709 dn = ads_get_dn(ads, res);
2710 if (dn == NULL) {
2711 ads_msgfree(ads, res);
2712 return ADS_ERROR(LDAP_NO_MEMORY);
2715 /* go up three levels */
2716 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2717 if (parent == NULL) {
2718 ads_msgfree(ads, res);
2719 ads_memfree(ads, dn);
2720 return ADS_ERROR(LDAP_NO_MEMORY);
2723 *site_dn = talloc_strdup(mem_ctx, parent);
2724 if (*site_dn == NULL) {
2725 ads_msgfree(ads, res);
2726 ads_memfree(ads, dn);
2727 return ADS_ERROR(LDAP_NO_MEMORY);
2730 ads_memfree(ads, dn);
2731 ads_msgfree(ads, res);
2733 return status;
2737 * get the upn suffixes for a domain
2738 * @param ads connection to ads server
2739 * @param mem_ctx Pointer to talloc context
2740 * @param suffixes Pointer to an array of suffixes
2741 * @param num_suffixes Pointer to the number of suffixes
2742 * @return status of search
2744 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2746 ADS_STATUS status;
2747 LDAPMessage *res;
2748 const char *base;
2749 char *config_context = NULL;
2750 const char *attrs[] = { "uPNSuffixes", NULL };
2752 status = ads_config_path(ads, mem_ctx, &config_context);
2753 if (!ADS_ERR_OK(status)) {
2754 return status;
2757 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2758 if (base == NULL) {
2759 return ADS_ERROR(LDAP_NO_MEMORY);
2762 status = ads_search_dn(ads, &res, base, attrs);
2763 if (!ADS_ERR_OK(status)) {
2764 return status;
2767 if (ads_count_replies(ads, res) != 1) {
2768 ads_msgfree(ads, res);
2769 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2772 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2773 if ((*suffixes) == NULL) {
2774 ads_msgfree(ads, res);
2775 return ADS_ERROR(LDAP_NO_MEMORY);
2778 ads_msgfree(ads, res);
2780 return status;
2784 * pull a DOM_SID from an extended dn string
2785 * @param mem_ctx TALLOC_CTX
2786 * @param extended_dn string
2787 * @param flags string type of extended_dn
2788 * @param sid pointer to a DOM_SID
2789 * @return boolean inidicating success
2791 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2792 const char *extended_dn,
2793 enum ads_extended_dn_flags flags,
2794 DOM_SID *sid)
2796 char *p, *q, *dn;
2798 if (!extended_dn) {
2799 return False;
2802 /* otherwise extended_dn gets stripped off */
2803 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2804 return False;
2807 * ADS_EXTENDED_DN_HEX_STRING:
2808 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2810 * ADS_EXTENDED_DN_STRING (only with w2k3):
2811 <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
2814 p = strchr(dn, ';');
2815 if (!p) {
2816 return False;
2819 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2820 return False;
2823 p += strlen(";<SID=");
2825 q = strchr(p, '>');
2826 if (!q) {
2827 return False;
2830 *q = '\0';
2832 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2834 switch (flags) {
2836 case ADS_EXTENDED_DN_STRING:
2837 if (!string_to_sid(sid, p)) {
2838 return False;
2840 break;
2841 case ADS_EXTENDED_DN_HEX_STRING: {
2842 pstring buf;
2843 size_t buf_len;
2845 buf_len = strhex_to_str(buf, strlen(p), p);
2846 if (buf_len == 0) {
2847 return False;
2850 if (!sid_parse(buf, buf_len, sid)) {
2851 DEBUG(10,("failed to parse sid\n"));
2852 return False;
2854 break;
2856 default:
2857 DEBUG(10,("unknown extended dn format\n"));
2858 return False;
2861 return True;
2865 * pull an array of DOM_SIDs from a ADS result
2866 * @param ads connection to ads server
2867 * @param mem_ctx TALLOC_CTX for allocating sid array
2868 * @param msg Results of search
2869 * @param field Attribute to retrieve
2870 * @param flags string type of extended_dn
2871 * @param sids pointer to sid array to allocate
2872 * @return the count of SIDs pulled
2874 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2875 TALLOC_CTX *mem_ctx,
2876 LDAPMessage *msg,
2877 const char *field,
2878 enum ads_extended_dn_flags flags,
2879 DOM_SID **sids)
2881 int i;
2882 size_t dn_count;
2883 char **dn_strings;
2885 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2886 &dn_count)) == NULL) {
2887 return 0;
2890 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2891 if (!(*sids)) {
2892 TALLOC_FREE(dn_strings);
2893 return 0;
2896 for (i=0; i<dn_count; i++) {
2898 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2899 flags, &(*sids)[i])) {
2900 TALLOC_FREE(*sids);
2901 TALLOC_FREE(dn_strings);
2902 return 0;
2906 TALLOC_FREE(dn_strings);
2908 return dn_count;
2911 /********************************************************************
2912 ********************************************************************/
2914 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2916 LDAPMessage *res = NULL;
2917 ADS_STATUS status;
2918 int count = 0;
2919 char *name = NULL;
2921 status = ads_find_machine_acct(ads, &res, global_myname());
2922 if (!ADS_ERR_OK(status)) {
2923 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2924 global_myname()));
2925 goto out;
2928 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2929 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2930 goto out;
2933 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2934 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2937 out:
2938 ads_msgfree(ads, res);
2940 return name;
2943 /********************************************************************
2944 ********************************************************************/
2946 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2948 LDAPMessage *res = NULL;
2949 ADS_STATUS status;
2950 int count = 0;
2951 char *name = NULL;
2953 status = ads_find_machine_acct(ads, &res, global_myname());
2954 if (!ADS_ERR_OK(status)) {
2955 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2956 global_myname()));
2957 goto out;
2960 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2961 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2962 goto out;
2965 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2966 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2969 out:
2970 ads_msgfree(ads, res);
2972 return name;
2975 /********************************************************************
2976 ********************************************************************/
2978 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2980 LDAPMessage *res = NULL;
2981 ADS_STATUS status;
2982 int count = 0;
2983 char *name = NULL;
2985 status = ads_find_machine_acct(ads, &res, global_myname());
2986 if (!ADS_ERR_OK(status)) {
2987 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2988 global_myname()));
2989 goto out;
2992 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2993 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2994 goto out;
2997 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2998 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3001 out:
3002 ads_msgfree(ads, res);
3004 return name;
3007 #if 0
3009 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3012 * Join a machine to a realm
3013 * Creates the machine account and sets the machine password
3014 * @param ads connection to ads server
3015 * @param machine name of host to add
3016 * @param org_unit Organizational unit to place machine in
3017 * @return status of join
3019 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3020 uint32 account_type, const char *org_unit)
3022 ADS_STATUS status;
3023 LDAPMessage *res = NULL;
3024 char *machine;
3026 /* machine name must be lowercase */
3027 machine = SMB_STRDUP(machine_name);
3028 strlower_m(machine);
3031 status = ads_find_machine_acct(ads, (void **)&res, machine);
3032 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3033 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3034 status = ads_leave_realm(ads, machine);
3035 if (!ADS_ERR_OK(status)) {
3036 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3037 machine, ads->config.realm));
3038 return status;
3042 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3043 if (!ADS_ERR_OK(status)) {
3044 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3045 SAFE_FREE(machine);
3046 return status;
3049 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3050 if (!ADS_ERR_OK(status)) {
3051 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3052 SAFE_FREE(machine);
3053 return status;
3056 SAFE_FREE(machine);
3057 ads_msgfree(ads, res);
3059 return status;
3061 #endif
3064 * Delete a machine from the realm
3065 * @param ads connection to ads server
3066 * @param hostname Machine to remove
3067 * @return status of delete
3069 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3071 ADS_STATUS status;
3072 void *msg;
3073 LDAPMessage *res;
3074 char *hostnameDN, *host;
3075 int rc;
3076 LDAPControl ldap_control;
3077 LDAPControl * pldap_control[2] = {NULL, NULL};
3079 pldap_control[0] = &ldap_control;
3080 memset(&ldap_control, 0, sizeof(LDAPControl));
3081 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3083 /* hostname must be lowercase */
3084 host = SMB_STRDUP(hostname);
3085 strlower_m(host);
3087 status = ads_find_machine_acct(ads, &res, host);
3088 if (!ADS_ERR_OK(status)) {
3089 DEBUG(0, ("Host account for %s does not exist.\n", host));
3090 SAFE_FREE(host);
3091 return status;
3094 msg = ads_first_entry(ads, res);
3095 if (!msg) {
3096 SAFE_FREE(host);
3097 return ADS_ERROR_SYSTEM(ENOENT);
3100 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3102 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3103 if (rc) {
3104 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3105 }else {
3106 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3109 if (rc != LDAP_SUCCESS) {
3110 const char *attrs[] = { "cn", NULL };
3111 LDAPMessage *msg_sub;
3113 /* we only search with scope ONE, we do not expect any further
3114 * objects to be created deeper */
3116 status = ads_do_search_retry(ads, hostnameDN,
3117 LDAP_SCOPE_ONELEVEL,
3118 "(objectclass=*)", attrs, &res);
3120 if (!ADS_ERR_OK(status)) {
3121 SAFE_FREE(host);
3122 ads_memfree(ads, hostnameDN);
3123 return status;
3126 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3127 msg_sub = ads_next_entry(ads, msg_sub)) {
3129 char *dn = NULL;
3131 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3132 SAFE_FREE(host);
3133 ads_memfree(ads, hostnameDN);
3134 return ADS_ERROR(LDAP_NO_MEMORY);
3137 status = ads_del_dn(ads, dn);
3138 if (!ADS_ERR_OK(status)) {
3139 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3140 SAFE_FREE(host);
3141 ads_memfree(ads, dn);
3142 ads_memfree(ads, hostnameDN);
3143 return status;
3146 ads_memfree(ads, dn);
3149 /* there should be no subordinate objects anymore */
3150 status = ads_do_search_retry(ads, hostnameDN,
3151 LDAP_SCOPE_ONELEVEL,
3152 "(objectclass=*)", attrs, &res);
3154 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3155 SAFE_FREE(host);
3156 ads_memfree(ads, hostnameDN);
3157 return status;
3160 /* delete hostnameDN now */
3161 status = ads_del_dn(ads, hostnameDN);
3162 if (!ADS_ERR_OK(status)) {
3163 SAFE_FREE(host);
3164 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3165 ads_memfree(ads, hostnameDN);
3166 return status;
3170 ads_memfree(ads, hostnameDN);
3172 status = ads_find_machine_acct(ads, &res, host);
3173 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3174 DEBUG(3, ("Failed to remove host account.\n"));
3175 SAFE_FREE(host);
3176 return status;
3179 SAFE_FREE(host);
3180 return status;
3184 * pull all token-sids from an LDAP dn
3185 * @param ads connection to ads server
3186 * @param mem_ctx TALLOC_CTX for allocating sid array
3187 * @param dn of LDAP object
3188 * @param user_sid pointer to DOM_SID (objectSid)
3189 * @param primary_group_sid pointer to DOM_SID (self composed)
3190 * @param sids pointer to sid array to allocate
3191 * @param num_sids counter of SIDs pulled
3192 * @return status of token query
3194 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3195 TALLOC_CTX *mem_ctx,
3196 const char *dn,
3197 DOM_SID *user_sid,
3198 DOM_SID *primary_group_sid,
3199 DOM_SID **sids,
3200 size_t *num_sids)
3202 ADS_STATUS status;
3203 LDAPMessage *res = NULL;
3204 int count = 0;
3205 size_t tmp_num_sids;
3206 DOM_SID *tmp_sids;
3207 DOM_SID tmp_user_sid;
3208 DOM_SID tmp_primary_group_sid;
3209 uint32 pgid;
3210 const char *attrs[] = {
3211 "objectSid",
3212 "tokenGroups",
3213 "primaryGroupID",
3214 NULL
3217 status = ads_search_retry_dn(ads, &res, dn, attrs);
3218 if (!ADS_ERR_OK(status)) {
3219 return status;
3222 count = ads_count_replies(ads, res);
3223 if (count != 1) {
3224 ads_msgfree(ads, res);
3225 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3228 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3229 ads_msgfree(ads, res);
3230 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3233 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3234 ads_msgfree(ads, res);
3235 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3239 /* hack to compose the primary group sid without knowing the
3240 * domsid */
3242 DOM_SID domsid;
3243 uint32 dummy_rid;
3245 sid_copy(&domsid, &tmp_user_sid);
3247 if (!sid_split_rid(&domsid, &dummy_rid)) {
3248 ads_msgfree(ads, res);
3249 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3252 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3253 ads_msgfree(ads, res);
3254 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3258 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3260 if (tmp_num_sids == 0 || !tmp_sids) {
3261 ads_msgfree(ads, res);
3262 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3265 if (num_sids) {
3266 *num_sids = tmp_num_sids;
3269 if (sids) {
3270 *sids = tmp_sids;
3273 if (user_sid) {
3274 *user_sid = tmp_user_sid;
3277 if (primary_group_sid) {
3278 *primary_group_sid = tmp_primary_group_sid;
3281 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3283 ads_msgfree(ads, res);
3284 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3288 * Find a sAMAccoutName in LDAP
3289 * @param ads connection to ads server
3290 * @param mem_ctx TALLOC_CTX for allocating sid array
3291 * @param samaccountname to search
3292 * @param uac_ret uint32 pointer userAccountControl attribute value
3293 * @param dn_ret pointer to dn
3294 * @return status of token query
3296 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3297 TALLOC_CTX *mem_ctx,
3298 const char *samaccountname,
3299 uint32 *uac_ret,
3300 const char **dn_ret)
3302 ADS_STATUS status;
3303 const char *attrs[] = { "userAccountControl", NULL };
3304 const char *filter;
3305 LDAPMessage *res = NULL;
3306 char *dn = NULL;
3307 uint32 uac = 0;
3309 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3310 samaccountname);
3311 if (filter == NULL) {
3312 goto out;
3315 status = ads_do_search_all(ads, ads->config.bind_path,
3316 LDAP_SCOPE_SUBTREE,
3317 filter, attrs, &res);
3319 if (!ADS_ERR_OK(status)) {
3320 goto out;
3323 if (ads_count_replies(ads, res) != 1) {
3324 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3325 goto out;
3328 dn = ads_get_dn(ads, res);
3329 if (dn == NULL) {
3330 status = ADS_ERROR(LDAP_NO_MEMORY);
3331 goto out;
3334 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3335 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3336 goto out;
3339 if (uac_ret) {
3340 *uac_ret = uac;
3343 if (dn_ret) {
3344 *dn_ret = talloc_strdup(mem_ctx, dn);
3345 if (!*dn_ret) {
3346 status = ADS_ERROR(LDAP_NO_MEMORY);
3347 goto out;
3350 out:
3351 ads_memfree(ads, dn);
3352 ads_msgfree(ads, res);
3354 return status;
3358 * find our configuration path
3359 * @param ads connection to ads server
3360 * @param mem_ctx Pointer to talloc context
3361 * @param config_path Pointer to the config path
3362 * @return status of search
3364 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3365 TALLOC_CTX *mem_ctx,
3366 char **config_path)
3368 ADS_STATUS status;
3369 LDAPMessage *res = NULL;
3370 const char *config_context = NULL;
3371 const char *attrs[] = { "configurationNamingContext", NULL };
3373 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3374 "(objectclass=*)", attrs, &res);
3375 if (!ADS_ERR_OK(status)) {
3376 return status;
3379 config_context = ads_pull_string(ads, mem_ctx, res,
3380 "configurationNamingContext");
3381 ads_msgfree(ads, res);
3382 if (!config_context) {
3383 return ADS_ERROR(LDAP_NO_MEMORY);
3386 if (config_path) {
3387 *config_path = talloc_strdup(mem_ctx, config_context);
3388 if (!*config_path) {
3389 return ADS_ERROR(LDAP_NO_MEMORY);
3393 return ADS_ERROR(LDAP_SUCCESS);
3397 * find the displayName of an extended right
3398 * @param ads connection to ads server
3399 * @param config_path The config path
3400 * @param mem_ctx Pointer to talloc context
3401 * @param GUID struct of the rightsGUID
3402 * @return status of search
3404 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3405 const char *config_path,
3406 TALLOC_CTX *mem_ctx,
3407 const struct GUID *rights_guid)
3409 ADS_STATUS rc;
3410 LDAPMessage *res = NULL;
3411 char *expr = NULL;
3412 const char *attrs[] = { "displayName", NULL };
3413 const char *result = NULL;
3414 const char *path;
3416 if (!ads || !mem_ctx || !rights_guid) {
3417 goto done;
3420 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3421 smb_uuid_string_static(*rights_guid));
3422 if (!expr) {
3423 goto done;
3426 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3427 if (!path) {
3428 goto done;
3431 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3432 expr, attrs, &res);
3433 if (!ADS_ERR_OK(rc)) {
3434 goto done;
3437 if (ads_count_replies(ads, res) != 1) {
3438 goto done;
3441 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3443 done:
3444 ads_msgfree(ads, res);
3445 return result;
3449 #endif