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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
67 /* Teardown timeout. */
68 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
74 static int ldap_search_with_timeout(LDAP *ld,
75 LDAP_CONST char *base,
77 LDAP_CONST char *filter,
85 struct timeval timeout;
88 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
89 timeout.tv_sec = lp_ldap_timeout();
92 /* Setup alarm timeout.... Do we need both of these ? JRA. */
94 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
95 alarm(lp_ldap_timeout());
96 /* End setup timeout. */
98 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
99 attrsonly, sctrls, cctrls, &timeout,
102 /* Teardown timeout. */
103 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
107 return LDAP_TIMELIMIT_EXCEEDED;
113 try a connection to a given ldap server, returning True and setting the servers IP
114 in the ads struct if successful
116 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
119 struct cldap_netlogon_reply cldap_reply;
121 if (!server || !*server) {
125 DEBUG(5,("ads_try_connect: sending CLDAP request to %s\n", server));
127 /* this copes with inet_ntoa brokenness */
129 srv = SMB_STRDUP(server);
131 ZERO_STRUCT( cldap_reply );
133 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
134 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
138 /* Check the CLDAP reply flags */
140 if ( !(cldap_reply.flags & ADS_LDAP) ) {
141 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
147 /* Fill in the ads->config values */
149 SAFE_FREE(ads->config.realm);
150 SAFE_FREE(ads->config.bind_path);
151 SAFE_FREE(ads->config.ldap_server_name);
153 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
154 strupper_m(cldap_reply.domain);
155 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
156 ads->config.bind_path = ads_build_dn(ads->config.realm);
158 ads->ldap_port = LDAP_PORT;
159 ads->ldap_ip = *interpret_addr2(srv);
162 /* cache the successful connection */
164 saf_store( ads->server.workgroup, server );
169 /**********************************************************************
170 Try to find an AD dc using our internal name resolution routines
171 Try the realm first and then then workgroup name if netbios is not
173 **********************************************************************/
175 static BOOL ads_find_dc(ADS_STRUCT *ads)
179 struct ip_service *ip_list;
181 BOOL got_realm = False;
182 BOOL use_own_domain = False;
184 /* if the realm and workgroup are both empty, assume they are ours */
187 c_realm = ads->server.realm;
189 if ( !c_realm || !*c_realm ) {
190 /* special case where no realm and no workgroup means our own */
191 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
192 use_own_domain = True;
193 c_realm = lp_realm();
197 if (c_realm && *c_realm)
201 /* we need to try once with the realm name and fallback to the
202 netbios domain name if we fail (if netbios has not been disabled */
204 if ( !got_realm && !lp_disable_netbios() ) {
205 c_realm = ads->server.workgroup;
206 if (!c_realm || !*c_realm) {
207 if ( use_own_domain )
208 c_realm = lp_workgroup();
211 if ( !c_realm || !*c_realm ) {
212 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
217 pstrcpy( realm, c_realm );
219 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
220 (got_realm ? "realm" : "domain"), realm));
222 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
223 /* fall back to netbios if we can */
224 if ( got_realm && !lp_disable_netbios() ) {
232 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
233 for ( i=0; i<count; i++ ) {
236 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
238 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
241 if ( ads_try_connect(ads, server) ) {
246 /* keep track of failures */
247 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
257 * Connect to the LDAP server
258 * @param ads Pointer to an existing ADS_STRUCT
259 * @return status of connection
261 ADS_STATUS ads_connect(ADS_STRUCT *ads)
263 int version = LDAP_VERSION3;
266 ads->last_attempt = time(NULL);
269 /* try with a user specified server */
271 if (ads->server.ldap_server &&
272 ads_try_connect(ads, ads->server.ldap_server)) {
276 if (ads_find_dc(ads)) {
280 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
283 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
285 if (!ads->auth.user_name) {
286 /* have to use the userPrincipalName value here and
287 not servicePrincipalName; found by Guenther Deschner @ Sernet.
289 Is this still correct? The comment does not match
292 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
295 if (!ads->auth.realm) {
296 ads->auth.realm = SMB_STRDUP(ads->config.realm);
299 if (!ads->auth.kdc_server) {
300 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
304 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
305 to MIT kerberos to work (tridge) */
308 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
309 setenv(env, ads->auth.kdc_server, 1);
314 /* If the caller() requested no LDAP bind, then we are done */
316 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
320 /* Otherwise setup the TCP LDAP session */
322 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
323 LDAP_PORT, lp_ldap_timeout())) == NULL )
325 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
327 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
329 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
330 if (!ADS_ERR_OK(status)) {
334 /* fill in the current time and offsets */
336 status = ads_current_time( ads );
337 if ( !ADS_ERR_OK(status) ) {
341 /* Now do the bind */
343 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
344 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
347 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
348 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
351 return ads_sasl_bind(ads);
355 Duplicate a struct berval into talloc'ed memory
357 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
359 struct berval *value;
361 if (!in_val) return NULL;
363 value = TALLOC_ZERO_P(ctx, struct berval);
366 if (in_val->bv_len == 0) return value;
368 value->bv_len = in_val->bv_len;
369 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
374 Make a values list out of an array of (struct berval *)
376 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
377 const struct berval **in_vals)
379 struct berval **values;
382 if (!in_vals) return NULL;
383 for (i=0; in_vals[i]; i++)
385 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
386 if (!values) return NULL;
388 for (i=0; in_vals[i]; i++) {
389 values[i] = dup_berval(ctx, in_vals[i]);
395 UTF8-encode a values list out of an array of (char *)
397 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
402 if (!in_vals) return NULL;
403 for (i=0; in_vals[i]; i++)
405 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
406 if (!values) return NULL;
408 for (i=0; in_vals[i]; i++) {
409 push_utf8_talloc(ctx, &values[i], in_vals[i]);
415 Pull a (char *) array out of a UTF8-encoded values list
417 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
422 if (!in_vals) return NULL;
423 for (i=0; in_vals[i]; i++)
425 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
426 if (!values) return NULL;
428 for (i=0; in_vals[i]; i++) {
429 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
435 * Do a search with paged results. cookie must be null on the first
436 * call, and then returned on each subsequent call. It will be null
437 * again when the entire search is complete
438 * @param ads connection to ads server
439 * @param bind_path Base dn for the search
440 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
441 * @param expr Search expression - specified in local charset
442 * @param attrs Attributes to retrieve - specified in utf8 or ascii
443 * @param res ** which will contain results - free res* with ads_msgfree()
444 * @param count Number of entries retrieved on this page
445 * @param cookie The paged results cookie to be returned on subsequent calls
446 * @return status of search
448 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
449 int scope, const char *expr,
450 const char **attrs, void **res,
451 int *count, void **cookie)
454 char *utf8_expr, *utf8_path, **search_attrs;
455 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
456 BerElement *cookie_be = NULL;
457 struct berval *cookie_bv= NULL;
462 if (!(ctx = talloc_init("ads_do_paged_search")))
463 return ADS_ERROR(LDAP_NO_MEMORY);
465 /* 0 means the conversion worked but the result was empty
466 so we only fail if it's -1. In any case, it always
467 at least nulls out the dest */
468 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
469 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
474 if (!attrs || !(*attrs))
477 /* This would be the utf8-encoded version...*/
478 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
479 if (!(str_list_copy(&search_attrs, attrs))) {
486 /* Paged results only available on ldap v3 or later */
487 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
488 if (version < LDAP_VERSION3) {
489 rc = LDAP_NOT_SUPPORTED;
493 cookie_be = ber_alloc_t(LBER_USE_DER);
494 if (cookie && *cookie) {
495 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
496 ber_bvfree(*cookie); /* don't need it from last time */
499 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
501 ber_flatten(cookie_be, &cookie_bv);
502 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
503 PagedResults.ldctl_iscritical = (char) 1;
504 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
505 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
507 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
508 NoReferrals.ldctl_iscritical = (char) 0;
509 NoReferrals.ldctl_value.bv_len = 0;
510 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
513 controls[0] = &NoReferrals;
514 controls[1] = &PagedResults;
517 /* we need to disable referrals as the openldap libs don't
518 handle them and paged results at the same time. Using them
519 together results in the result record containing the server
520 page control being removed from the result list (tridge/jmcd)
522 leaving this in despite the control that says don't generate
523 referrals, in case the server doesn't support it (jmcd)
525 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
527 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
528 search_attrs, 0, controls,
530 (LDAPMessage **)res);
532 ber_free(cookie_be, 1);
533 ber_bvfree(cookie_bv);
536 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr,
537 ldap_err2string(rc)));
541 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
542 NULL, &rcontrols, 0);
548 for (i=0; rcontrols[i]; i++) {
549 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
550 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
551 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
553 /* the berval is the cookie, but must be freed when
555 if (cookie_bv->bv_len) /* still more to do */
556 *cookie=ber_bvdup(cookie_bv);
559 ber_bvfree(cookie_bv);
560 ber_free(cookie_be, 1);
564 ldap_controls_free(rcontrols);
568 /* if/when we decide to utf8-encode attrs, take out this next line */
569 str_list_free(&search_attrs);
571 return ADS_ERROR(rc);
576 * Get all results for a search. This uses ads_do_paged_search() to return
577 * all entries in a large search.
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
582 * @param attrs Attributes to retrieve
583 * @param res ** which will contain results - free res* with ads_msgfree()
584 * @return status of search
586 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
587 int scope, const char *expr,
588 const char **attrs, void **res)
595 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
598 if (!ADS_ERR_OK(status))
601 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
605 LDAPMessage *msg, *next;
607 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
608 attrs, &res2, &count, &cookie);
610 if (!ADS_ERR_OK(status2)) break;
612 /* this relies on the way that ldap_add_result_entry() works internally. I hope
613 that this works on all ldap libs, but I have only tested with openldap */
614 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
615 next = ads_next_entry(ads, msg);
616 ldap_add_result_entry((LDAPMessage **)res, msg);
618 /* note that we do not free res2, as the memory is now
619 part of the main returned list */
622 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
623 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
630 * Run a function on all results for a search. Uses ads_do_paged_search() and
631 * runs the function as each page is returned, using ads_process_results()
632 * @param ads connection to ads server
633 * @param bind_path Base dn for the search
634 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
635 * @param expr Search expression - specified in local charset
636 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
637 * @param fn Function which takes attr name, values list, and data_area
638 * @param data_area Pointer which is passed to function on each call
639 * @return status of search
641 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
642 int scope, const char *expr, const char **attrs,
643 BOOL(*fn)(char *, void **, void *),
651 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
654 if (!ADS_ERR_OK(status)) return status;
656 ads_process_results(ads, res, fn, data_area);
657 ads_msgfree(ads, res);
660 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
661 &res, &count, &cookie);
663 if (!ADS_ERR_OK(status)) break;
665 ads_process_results(ads, res, fn, data_area);
666 ads_msgfree(ads, res);
673 * Do a search with a timeout.
674 * @param ads connection to ads server
675 * @param bind_path Base dn for the search
676 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
677 * @param expr Search expression
678 * @param attrs Attributes to retrieve
679 * @param res ** which will contain results - free res* with ads_msgfree()
680 * @return status of search
682 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
684 const char **attrs, void **res)
687 char *utf8_expr, *utf8_path, **search_attrs = NULL;
691 if (!(ctx = talloc_init("ads_do_search"))) {
692 DEBUG(1,("ads_do_search: talloc_init() failed!"));
693 return ADS_ERROR(LDAP_NO_MEMORY);
696 /* 0 means the conversion worked but the result was empty
697 so we only fail if it's negative. In any case, it always
698 at least nulls out the dest */
699 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
700 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
701 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
706 if (!attrs || !(*attrs))
709 /* This would be the utf8-encoded version...*/
710 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
711 if (!(str_list_copy(&search_attrs, attrs)))
713 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
719 /* see the note in ads_do_paged_search - we *must* disable referrals */
720 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
722 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
723 search_attrs, 0, NULL, NULL,
725 (LDAPMessage **)res);
727 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
728 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
734 /* if/when we decide to utf8-encode attrs, take out this next line */
735 str_list_free(&search_attrs);
736 return ADS_ERROR(rc);
739 * Do a general ADS search
740 * @param ads connection to ads server
741 * @param res ** which will contain results - free res* with ads_msgfree()
742 * @param expr Search expression
743 * @param attrs Attributes to retrieve
744 * @return status of search
746 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
750 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
755 * Do a search on a specific DistinguishedName
756 * @param ads connection to ads server
757 * @param res ** which will contain results - free res* with ads_msgfree()
758 * @param dn DistinguishName to search
759 * @param attrs Attributes to retrieve
760 * @return status of search
762 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
766 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
770 * Free up memory from a ads_search
771 * @param ads connection to ads server
772 * @param msg Search results to free
774 void ads_msgfree(ADS_STRUCT *ads, void *msg)
781 * Free up memory from various ads requests
782 * @param ads connection to ads server
783 * @param mem Area to free
785 void ads_memfree(ADS_STRUCT *ads, void *mem)
791 * Get a dn from search results
792 * @param ads connection to ads server
793 * @param msg Search result
796 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
798 char *utf8_dn, *unix_dn;
800 utf8_dn = ldap_get_dn(ads->ld, msg);
803 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
807 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
808 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
812 ldap_memfree(utf8_dn);
817 * Get a canonical dn from search results
818 * @param ads connection to ads server
819 * @param msg Search result
822 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
824 #ifdef HAVE_LDAP_DN2AD_CANONICAL
825 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
832 * Get the parent from a dn
833 * @param dn the dn to return the parent from
834 * @return parent dn string
836 char *ads_parent_dn(const char *dn)
838 char *p = strchr(dn, ',');
848 * Find a machine account given a hostname
849 * @param ads connection to ads server
850 * @param res ** which will contain results - free res* with ads_msgfree()
851 * @param host Hostname to search for
852 * @return status of search
854 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
858 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
862 /* the easiest way to find a machine account anywhere in the tree
863 is to look for hostname$ */
864 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
865 DEBUG(1, ("asprintf failed!\n"));
866 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
869 status = ads_search(ads, res, expr, attrs);
875 * Initialize a list of mods to be used in a modify request
876 * @param ctx An initialized TALLOC_CTX
877 * @return allocated ADS_MODLIST
879 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
881 #define ADS_MODLIST_ALLOC_SIZE 10
884 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
885 /* -1 is safety to make sure we don't go over the end.
886 need to reset it to NULL before doing ldap modify */
887 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
889 return (ADS_MODLIST)mods;
894 add an attribute to the list, with values list already constructed
896 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
897 int mod_op, const char *name,
901 LDAPMod **modlist = (LDAPMod **) *mods;
902 struct berval **ber_values = NULL;
903 char **char_values = NULL;
906 mod_op = LDAP_MOD_DELETE;
908 if (mod_op & LDAP_MOD_BVALUES)
909 ber_values = ads_dup_values(ctx,
910 (const struct berval **)invals);
912 char_values = ads_push_strvals(ctx,
913 (const char **) invals);
916 /* find the first empty slot */
917 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
919 if (modlist[curmod] == (LDAPMod *) -1) {
920 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
921 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
922 return ADS_ERROR(LDAP_NO_MEMORY);
923 memset(&modlist[curmod], 0,
924 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
925 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
926 *mods = (ADS_MODLIST)modlist;
929 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
930 return ADS_ERROR(LDAP_NO_MEMORY);
931 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
932 if (mod_op & LDAP_MOD_BVALUES) {
933 modlist[curmod]->mod_bvalues = ber_values;
934 } else if (mod_op & LDAP_MOD_DELETE) {
935 modlist[curmod]->mod_values = NULL;
937 modlist[curmod]->mod_values = char_values;
940 modlist[curmod]->mod_op = mod_op;
941 return ADS_ERROR(LDAP_SUCCESS);
945 * Add a single string value to a mod list
946 * @param ctx An initialized TALLOC_CTX
947 * @param mods An initialized ADS_MODLIST
948 * @param name The attribute name to add
949 * @param val The value to add - NULL means DELETE
950 * @return ADS STATUS indicating success of add
952 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
953 const char *name, const char *val)
955 const char *values[2];
961 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
962 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
963 (const void **) values);
967 * Add an array of string values to a mod list
968 * @param ctx An initialized TALLOC_CTX
969 * @param mods An initialized ADS_MODLIST
970 * @param name The attribute name to add
971 * @param vals The array of string values to add - NULL means DELETE
972 * @return ADS STATUS indicating success of add
974 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
975 const char *name, const char **vals)
978 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
979 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
980 name, (const void **) vals);
984 * Add a single ber-encoded value to a mod list
985 * @param ctx An initialized TALLOC_CTX
986 * @param mods An initialized ADS_MODLIST
987 * @param name The attribute name to add
988 * @param val The value to add - NULL means DELETE
989 * @return ADS STATUS indicating success of add
991 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
992 const char *name, const struct berval *val)
994 const struct berval *values[2];
999 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1000 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1001 name, (const void **) values);
1005 * Perform an ldap modify
1006 * @param ads connection to ads server
1007 * @param mod_dn DistinguishedName to modify
1008 * @param mods list of modifications to perform
1009 * @return status of modify
1011 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1014 char *utf8_dn = NULL;
1016 this control is needed to modify that contains a currently
1017 non-existent attribute (but allowable for the object) to run
1019 LDAPControl PermitModify = {
1020 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1023 LDAPControl *controls[2];
1025 controls[0] = &PermitModify;
1028 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1029 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1032 /* find the end of the list, marked by NULL or -1 */
1033 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1034 /* make sure the end of the list is NULL */
1036 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1037 (LDAPMod **) mods, controls, NULL);
1039 return ADS_ERROR(ret);
1043 * Perform an ldap add
1044 * @param ads connection to ads server
1045 * @param new_dn DistinguishedName to add
1046 * @param mods list of attributes and values for DN
1047 * @return status of add
1049 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1052 char *utf8_dn = NULL;
1054 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1055 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1056 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1059 /* find the end of the list, marked by NULL or -1 */
1060 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1061 /* make sure the end of the list is NULL */
1064 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1066 return ADS_ERROR(ret);
1070 * Delete a DistinguishedName
1071 * @param ads connection to ads server
1072 * @param new_dn DistinguishedName to delete
1073 * @return status of delete
1075 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1078 char *utf8_dn = NULL;
1079 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1080 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1081 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1084 ret = ldap_delete_s(ads->ld, utf8_dn);
1085 return ADS_ERROR(ret);
1089 * Build an org unit string
1090 * if org unit is Computers or blank then assume a container, otherwise
1091 * assume a / separated list of organisational units.
1092 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1093 * @param ads connection to ads server
1094 * @param org_unit Organizational unit
1095 * @return org unit string - caller must free
1097 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1101 if (!org_unit || !*org_unit) {
1103 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1105 /* samba4 might not yet respond to a wellknownobject-query */
1106 return ret ? ret : SMB_STRDUP("cn=Computers");
1109 if (strequal(org_unit, "Computers")) {
1110 return SMB_STRDUP("cn=Computers");
1113 /* jmcd: removed "\\" from the separation chars, because it is
1114 needed as an escape for chars like '#' which are valid in an
1116 return ads_build_path(org_unit, "/", "ou=", 1);
1120 * Get a org unit string for a well-known GUID
1121 * @param ads connection to ads server
1122 * @param wknguid Well known GUID
1123 * @return org unit string - caller must free
1125 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1129 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1130 const char *attrs[] = {"distinguishedName", NULL};
1131 int new_ln, wkn_ln, bind_ln, i;
1133 if (wknguid == NULL) {
1137 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1138 DEBUG(1, ("asprintf failed!\n"));
1142 status = ads_search_dn(ads, &res, base, attrs);
1143 if (!ADS_ERR_OK(status)) {
1144 DEBUG(1,("Failed while searching for: %s\n", base));
1149 if (ads_count_replies(ads, res) != 1) {
1153 /* substitute the bind-path from the well-known-guid-search result */
1154 wkn_dn = ads_get_dn(ads, res);
1155 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1156 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1158 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1160 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1163 new_ln = wkn_ln - bind_ln;
1165 ret = wkn_dn_exp[0];
1167 for (i=1; i < new_ln; i++) {
1169 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1170 ret = SMB_STRDUP(s);
1178 * Adds (appends) an item to an attribute array, rather then
1179 * replacing the whole list
1180 * @param ctx An initialized TALLOC_CTX
1181 * @param mods An initialized ADS_MODLIST
1182 * @param name name of the ldap attribute to append to
1183 * @param vals an array of values to add
1184 * @return status of addition
1187 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1188 const char *name, const char **vals)
1190 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1194 * Determines the computer account's current KVNO via an LDAP lookup
1195 * @param ads An initialized ADS_STRUCT
1196 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1197 * @return the kvno for the computer account, or -1 in case of a failure.
1200 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1202 LDAPMessage *res = NULL;
1203 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1205 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1206 char *dn_string = NULL;
1207 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1209 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1210 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1213 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1215 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1216 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1217 ads_msgfree(ads, res);
1221 dn_string = ads_get_dn(ads, res);
1223 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1224 ads_msgfree(ads, res);
1227 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1228 ads_memfree(ads, dn_string);
1230 /* ---------------------------------------------------------
1231 * 0 is returned as a default KVNO from this point on...
1232 * This is done because Windows 2000 does not support key
1233 * version numbers. Chances are that a failure in the next
1234 * step is simply due to Windows 2000 being used for a
1235 * domain controller. */
1238 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1239 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1240 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1241 ads_msgfree(ads, res);
1246 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1247 ads_msgfree(ads, res);
1252 * This clears out all registered spn's for a given hostname
1253 * @param ads An initilaized ADS_STRUCT
1254 * @param machine_name the NetBIOS name of the computer.
1255 * @return 0 upon success, non-zero otherwise.
1258 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1261 LDAPMessage *res = NULL;
1263 const char *servicePrincipalName[1] = {NULL};
1264 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1265 char *dn_string = NULL;
1267 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1268 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1269 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1270 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1271 ads_msgfree(ads, res);
1272 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1275 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1276 ctx = talloc_init("ads_clear_service_principal_names");
1278 ads_msgfree(ads, res);
1279 return ADS_ERROR(LDAP_NO_MEMORY);
1282 if (!(mods = ads_init_mods(ctx))) {
1283 talloc_destroy(ctx);
1284 ads_msgfree(ads, res);
1285 return ADS_ERROR(LDAP_NO_MEMORY);
1287 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1288 if (!ADS_ERR_OK(ret)) {
1289 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1290 ads_msgfree(ads, res);
1291 talloc_destroy(ctx);
1294 dn_string = ads_get_dn(ads, res);
1296 talloc_destroy(ctx);
1297 ads_msgfree(ads, res);
1298 return ADS_ERROR(LDAP_NO_MEMORY);
1300 ret = ads_gen_mod(ads, dn_string, mods);
1301 ads_memfree(ads,dn_string);
1302 if (!ADS_ERR_OK(ret)) {
1303 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1305 ads_msgfree(ads, res);
1306 talloc_destroy(ctx);
1310 ads_msgfree(ads, res);
1311 talloc_destroy(ctx);
1316 * This adds a service principal name to an existing computer account
1317 * (found by hostname) in AD.
1318 * @param ads An initialized ADS_STRUCT
1319 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1320 * @param spn A string of the service principal to add, i.e. 'host'
1321 * @return 0 upon sucess, or non-zero if a failure occurs
1324 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1328 LDAPMessage *res = NULL;
1329 char *host_spn, *psp1, *psp2, *psp3;
1332 char *dn_string = NULL;
1333 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1335 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1336 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1337 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1339 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1340 spn, machine_name, ads->config.realm));
1341 ads_msgfree(ads, res);
1342 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1345 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1346 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1347 ads_msgfree(ads, res);
1348 return ADS_ERROR(LDAP_NO_MEMORY);
1351 name_to_fqdn(my_fqdn, machine_name);
1352 strlower_m(my_fqdn);
1354 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1355 talloc_destroy(ctx);
1356 ads_msgfree(ads, res);
1357 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1360 /* Add the extra principal */
1361 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1363 strlower_m(&psp1[strlen(spn)]);
1364 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1365 servicePrincipalName[0] = psp1;
1366 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1368 strlower_m(&psp2[strlen(spn)]);
1369 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1370 servicePrincipalName[1] = psp2;
1372 /* Add another principal in case the realm != the DNS domain, so that
1373 * the KDC doesn't send "server principal unknown" errors to clients
1374 * which use the DNS name in determining service principal names. */
1375 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1377 strlower_m(&psp3[strlen(spn)]);
1378 if (strcmp(psp2, psp3) != 0) {
1379 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1380 servicePrincipalName[2] = psp3;
1383 if (!(mods = ads_init_mods(ctx))) {
1384 talloc_destroy(ctx);
1385 ads_msgfree(ads, res);
1386 return ADS_ERROR(LDAP_NO_MEMORY);
1388 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1389 if (!ADS_ERR_OK(ret)) {
1390 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1391 talloc_destroy(ctx);
1392 ads_msgfree(ads, res);
1395 dn_string = ads_get_dn(ads, res);
1397 talloc_destroy(ctx);
1398 ads_msgfree(ads, res);
1399 return ADS_ERROR(LDAP_NO_MEMORY);
1401 ret = ads_gen_mod(ads, dn_string, mods);
1402 ads_memfree(ads,dn_string);
1403 if (!ADS_ERR_OK(ret)) {
1404 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1405 talloc_destroy(ctx);
1406 ads_msgfree(ads, res);
1410 talloc_destroy(ctx);
1411 ads_msgfree(ads, res);
1416 * adds a machine account to the ADS server
1417 * @param ads An intialized ADS_STRUCT
1418 * @param machine_name - the NetBIOS machine name of this account.
1419 * @param account_type A number indicating the type of account to create
1420 * @param org_unit The LDAP path in which to place this account
1421 * @return 0 upon success, or non-zero otherwise
1424 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1425 uint32 account_type,
1426 const char *org_unit)
1428 ADS_STATUS ret, status;
1429 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1432 const char *objectClass[] = {"top", "person", "organizationalPerson",
1433 "user", "computer", NULL};
1434 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1435 char *psp, *psp2, *psp3, *psp4;
1436 unsigned acct_control;
1439 LDAPMessage *res = NULL;
1442 if (!(ctx = talloc_init("ads_add_machine_acct")))
1443 return ADS_ERROR(LDAP_NO_MEMORY);
1445 ret = ADS_ERROR(LDAP_NO_MEMORY);
1447 name_to_fqdn(my_fqdn, machine_name);
1449 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1450 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1451 char *dn_string = ads_get_dn(ads, res);
1453 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1456 new_dn = talloc_strdup(ctx, dn_string);
1457 ads_memfree(ads,dn_string);
1458 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1462 char *ou_str = ads_ou_string(ads,org_unit);
1464 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1467 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1468 ads->config.bind_path);
1477 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1479 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1481 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1482 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1485 strlower_m(&psp[5]);
1486 servicePrincipalName[1] = psp;
1487 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1488 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1491 strlower_m(&psp2[5]);
1492 servicePrincipalName[3] = psp2;
1494 /* Ensure servicePrincipalName[4] and [5] are unique. */
1495 strlower_m(my_fqdn);
1496 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1497 strlower_m(&psp3[5]);
1500 for (i = 0; i < next_spn; i++) {
1501 if (strequal(servicePrincipalName[i], psp3))
1504 if (i == next_spn) {
1505 servicePrincipalName[next_spn++] = psp3;
1508 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1509 strlower_m(&psp4[5]);
1510 for (i = 0; i < next_spn; i++) {
1511 if (strequal(servicePrincipalName[i], psp4))
1514 if (i == next_spn) {
1515 servicePrincipalName[next_spn++] = psp4;
1518 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1522 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1523 #ifndef ENCTYPE_ARCFOUR_HMAC
1524 acct_control |= UF_USE_DES_KEY_ONLY;
1527 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1531 if (!(mods = ads_init_mods(ctx))) {
1536 ads_mod_str(ctx, &mods, "cn", machine_name);
1537 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1538 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1540 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1541 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1542 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1543 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1544 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1545 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1548 ret = ads_gen_add(ads, new_dn, mods);
1550 ret = ads_gen_mod(ads, new_dn, mods);
1553 if (!ADS_ERR_OK(ret)) {
1557 /* Do not fail if we can't set security descriptor
1558 * it shouldn't be mandatory and probably we just
1559 * don't have enough rights to do it.
1562 status = ads_set_machine_sd(ads, machine_name, new_dn);
1564 if (!ADS_ERR_OK(status)) {
1565 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1566 ads_errstr(status)));
1570 ads_msgfree(ads, res);
1571 talloc_destroy(ctx);
1576 dump a binary result from ldap
1578 static void dump_binary(const char *field, struct berval **values)
1581 for (i=0; values[i]; i++) {
1582 printf("%s: ", field);
1583 for (j=0; j<values[i]->bv_len; j++) {
1584 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1590 static void dump_guid(const char *field, struct berval **values)
1594 for (i=0; values[i]; i++) {
1595 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1596 printf("%s: %s\n", field,
1597 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1602 dump a sid result from ldap
1604 static void dump_sid(const char *field, struct berval **values)
1607 for (i=0; values[i]; i++) {
1609 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1610 printf("%s: %s\n", field, sid_string_static(&sid));
1615 dump ntSecurityDescriptor
1617 static void dump_sd(const char *filed, struct berval **values)
1622 TALLOC_CTX *ctx = 0;
1624 if (!(ctx = talloc_init("sec_io_desc")))
1628 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1629 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1630 prs_set_offset(&ps,0);
1633 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1635 talloc_destroy(ctx);
1638 if (psd) ads_disp_sd(psd);
1641 talloc_destroy(ctx);
1645 dump a string result from ldap
1647 static void dump_string(const char *field, char **values)
1650 for (i=0; values[i]; i++) {
1651 printf("%s: %s\n", field, values[i]);
1656 dump a field from LDAP on stdout
1660 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1665 void (*handler)(const char *, struct berval **);
1667 {"objectGUID", False, dump_guid},
1668 {"netbootGUID", False, dump_guid},
1669 {"nTSecurityDescriptor", False, dump_sd},
1670 {"dnsRecord", False, dump_binary},
1671 {"objectSid", False, dump_sid},
1672 {"tokenGroups", False, dump_sid},
1673 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1674 {"tokengroupsGlobalandUniversal", False, dump_sid},
1679 if (!field) { /* must be end of an entry */
1684 for (i=0; handlers[i].name; i++) {
1685 if (StrCaseCmp(handlers[i].name, field) == 0) {
1686 if (!values) /* first time, indicate string or not */
1687 return handlers[i].string;
1688 handlers[i].handler(field, (struct berval **) values);
1692 if (!handlers[i].name) {
1693 if (!values) /* first time, indicate string conversion */
1695 dump_string(field, (char **)values);
1701 * Dump a result from LDAP on stdout
1702 * used for debugging
1703 * @param ads connection to ads server
1704 * @param res Results to dump
1707 void ads_dump(ADS_STRUCT *ads, void *res)
1709 ads_process_results(ads, res, ads_dump_field, NULL);
1713 * Walk through results, calling a function for each entry found.
1714 * The function receives a field name, a berval * array of values,
1715 * and a data area passed through from the start. The function is
1716 * called once with null for field and values at the end of each
1718 * @param ads connection to ads server
1719 * @param res Results to process
1720 * @param fn Function for processing each result
1721 * @param data_area user-defined area to pass to function
1723 void ads_process_results(ADS_STRUCT *ads, void *res,
1724 BOOL(*fn)(char *, void **, void *),
1730 if (!(ctx = talloc_init("ads_process_results")))
1733 for (msg = ads_first_entry(ads, res); msg;
1734 msg = ads_next_entry(ads, msg)) {
1738 for (utf8_field=ldap_first_attribute(ads->ld,
1739 (LDAPMessage *)msg,&b);
1741 utf8_field=ldap_next_attribute(ads->ld,
1742 (LDAPMessage *)msg,b)) {
1743 struct berval **ber_vals;
1744 char **str_vals, **utf8_vals;
1748 pull_utf8_talloc(ctx, &field, utf8_field);
1749 string = fn(field, NULL, data_area);
1752 utf8_vals = ldap_get_values(ads->ld,
1753 (LDAPMessage *)msg, field);
1754 str_vals = ads_pull_strvals(ctx,
1755 (const char **) utf8_vals);
1756 fn(field, (void **) str_vals, data_area);
1757 ldap_value_free(utf8_vals);
1759 ber_vals = ldap_get_values_len(ads->ld,
1760 (LDAPMessage *)msg, field);
1761 fn(field, (void **) ber_vals, data_area);
1763 ldap_value_free_len(ber_vals);
1765 ldap_memfree(utf8_field);
1768 talloc_free_children(ctx);
1769 fn(NULL, NULL, data_area); /* completed an entry */
1772 talloc_destroy(ctx);
1776 * count how many replies are in a LDAPMessage
1777 * @param ads connection to ads server
1778 * @param res Results to count
1779 * @return number of replies
1781 int ads_count_replies(ADS_STRUCT *ads, void *res)
1783 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1787 * Join a machine to a realm
1788 * Creates the machine account and sets the machine password
1789 * @param ads connection to ads server
1790 * @param machine name of host to add
1791 * @param org_unit Organizational unit to place machine in
1792 * @return status of join
1794 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1795 uint32 account_type, const char *org_unit)
1798 LDAPMessage *res = NULL;
1801 /* machine name must be lowercase */
1802 machine = SMB_STRDUP(machine_name);
1803 strlower_m(machine);
1806 status = ads_find_machine_acct(ads, (void **)&res, machine);
1807 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1808 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1809 status = ads_leave_realm(ads, machine);
1810 if (!ADS_ERR_OK(status)) {
1811 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1812 machine, ads->config.realm));
1818 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1819 if (!ADS_ERR_OK(status)) {
1820 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1825 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
1826 if (!ADS_ERR_OK(status)) {
1827 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1833 ads_msgfree(ads, res);
1839 * Delete a machine from the realm
1840 * @param ads connection to ads server
1841 * @param hostname Machine to remove
1842 * @return status of delete
1844 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1848 char *hostnameDN, *host;
1850 LDAPControl ldap_control;
1851 LDAPControl * pldap_control[2] = {NULL, NULL};
1853 pldap_control[0] = &ldap_control;
1854 memset(&ldap_control, 0, sizeof(LDAPControl));
1855 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
1857 /* hostname must be lowercase */
1858 host = SMB_STRDUP(hostname);
1861 status = ads_find_machine_acct(ads, &res, host);
1862 if (!ADS_ERR_OK(status)) {
1863 DEBUG(0, ("Host account for %s does not exist.\n", host));
1867 msg = ads_first_entry(ads, res);
1869 return ADS_ERROR_SYSTEM(ENOENT);
1872 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1875 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
1877 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
1879 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
1882 ads_memfree(ads, hostnameDN);
1883 if (rc != LDAP_SUCCESS) {
1884 return ADS_ERROR(rc);
1887 status = ads_find_machine_acct(ads, &res, host);
1888 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1889 DEBUG(0, ("Failed to remove host account.\n"));
1899 * add machine account to existing security descriptor
1900 * @param ads connection to ads server
1901 * @param hostname machine to add
1902 * @param dn DN of security descriptor
1905 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1907 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1910 struct berval bval = {0, NULL};
1912 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1914 LDAPMessage *res = 0;
1915 LDAPMessage *msg = 0;
1916 ADS_MODLIST mods = 0;
1921 SEC_DESC *psd = NULL;
1922 TALLOC_CTX *ctx = NULL;
1924 /* Avoid segmentation fault in prs_mem_free if
1925 * we have to bail out before prs_init */
1926 ps_wire.is_dynamic = False;
1929 SAFE_FREE(escaped_hostname);
1930 return ADS_ERROR(LDAP_SERVER_DOWN);
1933 ret = ADS_ERROR(LDAP_SUCCESS);
1935 if (!escaped_hostname) {
1936 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1939 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1940 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1941 SAFE_FREE(escaped_hostname);
1942 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1945 SAFE_FREE(escaped_hostname);
1947 ret = ads_search(ads, (void *) &res, expr, attrs);
1951 if (!ADS_ERR_OK(ret)) return ret;
1953 if ( !(msg = ads_first_entry(ads, res) )) {
1954 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1955 goto ads_set_sd_error;
1958 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1959 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1960 goto ads_set_sd_error;
1963 if (!(ctx = talloc_init("sec_io_desc"))) {
1964 ret = ADS_ERROR(LDAP_NO_MEMORY);
1965 goto ads_set_sd_error;
1968 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1969 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1970 goto ads_set_sd_error;
1973 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1975 if (!NT_STATUS_IS_OK(status)) {
1976 ret = ADS_ERROR_NT(status);
1977 goto ads_set_sd_error;
1980 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1981 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1984 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1985 ret = ADS_ERROR(LDAP_NO_MEMORY);
1986 goto ads_set_sd_error;
1990 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1992 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1994 bval.bv_len = prs_offset(&ps_wire);
1995 bval.bv_val = TALLOC(ctx, bval.bv_len);
1997 ret = ADS_ERROR(LDAP_NO_MEMORY);
1998 goto ads_set_sd_error;
2001 prs_set_offset(&ps_wire, 0);
2003 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
2004 ret = ADS_ERROR(LDAP_NO_MEMORY);
2005 goto ads_set_sd_error;
2008 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
2009 if (ADS_ERR_OK(ret)) {
2010 ret = ads_gen_mod(ads, dn, mods);
2014 ads_msgfree(ads, res);
2015 prs_mem_free(&ps_wire);
2016 talloc_destroy(ctx);
2021 * pull the first entry from a ADS result
2022 * @param ads connection to ads server
2023 * @param res Results of search
2024 * @return first entry from result
2026 void *ads_first_entry(ADS_STRUCT *ads, void *res)
2028 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
2032 * pull the next entry from a ADS result
2033 * @param ads connection to ads server
2034 * @param res Results of search
2035 * @return next entry from result
2037 void *ads_next_entry(ADS_STRUCT *ads, void *res)
2039 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
2043 * pull a single string from a ADS result
2044 * @param ads connection to ads server
2045 * @param mem_ctx TALLOC_CTX to use for allocating result string
2046 * @param msg Results of search
2047 * @param field Attribute to retrieve
2048 * @return Result string in talloc context
2050 char *ads_pull_string(ADS_STRUCT *ads,
2051 TALLOC_CTX *mem_ctx, void *msg, const char *field)
2058 values = ldap_get_values(ads->ld, msg, field);
2063 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2065 if (rc != (size_t)-1)
2069 ldap_value_free(values);
2074 * pull an array of strings from a ADS result
2075 * @param ads connection to ads server
2076 * @param mem_ctx TALLOC_CTX to use for allocating result string
2077 * @param msg Results of search
2078 * @param field Attribute to retrieve
2079 * @return Result strings in talloc context
2081 char **ads_pull_strings(ADS_STRUCT *ads,
2082 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2089 values = ldap_get_values(ads->ld, msg, field);
2093 *num_values = ldap_count_values(values);
2095 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2097 ldap_value_free(values);
2101 for (i=0;i<*num_values;i++) {
2102 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2103 ldap_value_free(values);
2109 ldap_value_free(values);
2114 * pull an array of strings from a ADS result
2115 * (handle large multivalue attributes with range retrieval)
2116 * @param ads connection to ads server
2117 * @param mem_ctx TALLOC_CTX to use for allocating result string
2118 * @param msg Results of search
2119 * @param field Attribute to retrieve
2120 * @param current_strings strings returned by a previous call to this function
2121 * @param next_attribute The next query should ask for this attribute
2122 * @param num_values How many values did we get this time?
2123 * @param more_values Are there more values to get?
2124 * @return Result strings in talloc context
2126 char **ads_pull_strings_range(ADS_STRUCT *ads,
2127 TALLOC_CTX *mem_ctx,
2128 void *msg, const char *field,
2129 char **current_strings,
2130 const char **next_attribute,
2131 size_t *num_strings,
2135 char *expected_range_attrib, *range_attr;
2136 BerElement *ptr = NULL;
2139 size_t num_new_strings;
2140 unsigned long int range_start;
2141 unsigned long int range_end;
2143 /* we might have been given the whole lot anyway */
2144 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2145 *more_strings = False;
2149 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2151 /* look for Range result */
2152 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2154 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2155 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2156 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2164 /* nothing here - this field is just empty */
2165 *more_strings = False;
2169 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2170 &range_start, &range_end) == 2) {
2171 *more_strings = True;
2173 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2174 &range_start) == 1) {
2175 *more_strings = False;
2177 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2179 ldap_memfree(range_attr);
2180 *more_strings = False;
2185 if ((*num_strings) != range_start) {
2186 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2187 " - aborting range retreival\n",
2188 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2189 ldap_memfree(range_attr);
2190 *more_strings = False;
2194 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2196 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2197 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2198 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2199 range_attr, (unsigned long int)range_end - range_start + 1,
2200 (unsigned long int)num_new_strings));
2201 ldap_memfree(range_attr);
2202 *more_strings = False;
2206 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2207 *num_strings + num_new_strings);
2209 if (strings == NULL) {
2210 ldap_memfree(range_attr);
2211 *more_strings = False;
2215 memcpy(&strings[*num_strings], new_strings,
2216 sizeof(*new_strings) * num_new_strings);
2218 (*num_strings) += num_new_strings;
2220 if (*more_strings) {
2221 *next_attribute = talloc_asprintf(mem_ctx,
2226 if (!*next_attribute) {
2227 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2228 ldap_memfree(range_attr);
2229 *more_strings = False;
2234 ldap_memfree(range_attr);
2240 * pull a single uint32 from a ADS result
2241 * @param ads connection to ads server
2242 * @param msg Results of search
2243 * @param field Attribute to retrieve
2244 * @param v Pointer to int to store result
2245 * @return boolean inidicating success
2247 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2248 void *msg, const char *field, uint32 *v)
2252 values = ldap_get_values(ads->ld, msg, field);
2256 ldap_value_free(values);
2260 *v = atoi(values[0]);
2261 ldap_value_free(values);
2266 * pull a single objectGUID from an ADS result
2267 * @param ads connection to ADS server
2268 * @param msg results of search
2269 * @param guid 37-byte area to receive text guid
2270 * @return boolean indicating success
2272 BOOL ads_pull_guid(ADS_STRUCT *ads,
2273 void *msg, struct uuid *guid)
2276 UUID_FLAT flat_guid;
2278 values = ldap_get_values(ads->ld, msg, "objectGUID");
2283 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2284 smb_uuid_unpack(flat_guid, guid);
2285 ldap_value_free(values);
2288 ldap_value_free(values);
2295 * pull a single DOM_SID from a ADS result
2296 * @param ads connection to ads server
2297 * @param msg Results of search
2298 * @param field Attribute to retrieve
2299 * @param sid Pointer to sid to store result
2300 * @return boolean inidicating success
2302 BOOL ads_pull_sid(ADS_STRUCT *ads,
2303 void *msg, const char *field, DOM_SID *sid)
2305 struct berval **values;
2308 values = ldap_get_values_len(ads->ld, msg, field);
2314 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2316 ldap_value_free_len(values);
2321 * pull an array of DOM_SIDs from a ADS result
2322 * @param ads connection to ads server
2323 * @param mem_ctx TALLOC_CTX for allocating sid array
2324 * @param msg Results of search
2325 * @param field Attribute to retrieve
2326 * @param sids pointer to sid array to allocate
2327 * @return the count of SIDs pulled
2329 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2330 void *msg, const char *field, DOM_SID **sids)
2332 struct berval **values;
2336 values = ldap_get_values_len(ads->ld, msg, field);
2341 for (i=0; values[i]; i++)
2344 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2346 ldap_value_free_len(values);
2351 for (i=0; values[i]; i++) {
2352 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2355 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2360 ldap_value_free_len(values);
2365 * pull a SEC_DESC from a ADS result
2366 * @param ads connection to ads server
2367 * @param mem_ctx TALLOC_CTX for allocating sid array
2368 * @param msg Results of search
2369 * @param field Attribute to retrieve
2370 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2371 * @return boolean inidicating success
2373 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2374 void *msg, const char *field, SEC_DESC **sd)
2376 struct berval **values;
2380 values = ldap_get_values_len(ads->ld, msg, field);
2382 if (!values) return False;
2385 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2386 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2387 prs_set_offset(&ps,0);
2389 ret = sec_io_desc("sd", sd, &ps, 1);
2392 ldap_value_free_len(values);
2397 * in order to support usernames longer than 21 characters we need to
2398 * use both the sAMAccountName and the userPrincipalName attributes
2399 * It seems that not all users have the userPrincipalName attribute set
2401 * @param ads connection to ads server
2402 * @param mem_ctx TALLOC_CTX for allocating sid array
2403 * @param msg Results of search
2404 * @return the username
2406 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2411 /* lookup_name() only works on the sAMAccountName to
2412 returning the username portion of userPrincipalName
2413 breaks winbindd_getpwnam() */
2415 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2416 if (ret && (p = strchr_m(ret, '@'))) {
2421 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2426 * find the update serial number - this is the core of the ldap cache
2427 * @param ads connection to ads server
2428 * @param ads connection to ADS server
2429 * @param usn Pointer to retrieved update serial number
2430 * @return status of search
2432 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2434 const char *attrs[] = {"highestCommittedUSN", NULL};
2438 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2439 if (!ADS_ERR_OK(status))
2442 if (ads_count_replies(ads, res) != 1) {
2443 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2446 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2447 ads_msgfree(ads, res);
2451 /* parse a ADS timestring - typical string is
2452 '20020917091222.0Z0' which means 09:12.22 17th September
2454 static time_t ads_parse_time(const char *str)
2460 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2461 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2462 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2472 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, const char *schema_path, TALLOC_CTX *mem_ctx, const char * OID)
2478 const char *attrs[] = { "lDAPDisplayName", NULL };
2481 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2485 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2490 rc = ads_do_search_retry(ads, schema_path, LDAP_SCOPE_SUBTREE,
2492 if (!ADS_ERR_OK(rc)) {
2496 count = ads_count_replies(ads, res);
2497 if (count == 0 || !res) {
2501 result = ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2502 ads_msgfree(ads, res);
2507 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2510 ads_msgfree(ads, res);
2515 * Find the servers name and realm - this can be done before authentication
2516 * The ldapServiceName field on w2k looks like this:
2517 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2518 * @param ads connection to ads server
2519 * @return status of search
2521 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2523 const char *attrs[] = {"currentTime", NULL};
2528 ADS_STRUCT *ads_s = ads;
2530 if (!(ctx = talloc_init("ads_server_info"))) {
2531 return ADS_ERROR(LDAP_NO_MEMORY);
2534 /* establish a new ldap tcp session if necessary */
2537 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2538 ads->server.ldap_server )) == NULL )
2542 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2543 status = ads_connect( ads_s );
2544 if ( !ADS_ERR_OK(status))
2548 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2549 if (!ADS_ERR_OK(status)) {
2550 talloc_destroy(ctx);
2554 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2556 ads_msgfree(ads, res);
2557 talloc_destroy(ctx);
2558 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2562 /* but save the time and offset in the original ADS_STRUCT */
2564 ads->config.current_time = ads_parse_time(timestr);
2566 if (ads->config.current_time != 0) {
2567 ads->auth.time_offset = ads->config.current_time - time(NULL);
2568 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2571 status = ADS_SUCCESS;
2574 /* free any temporary ads connections */
2575 if ( ads_s != ads ) {
2576 ads_destroy( &ads_s );
2578 talloc_destroy(ctx);
2583 /*********************************************************************
2584 *********************************************************************/
2586 static ADS_STATUS ads_schema_path(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **schema_path)
2591 const char *attrs[] = { "schemaNamingContext", NULL };
2593 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2594 if (!ADS_ERR_OK(status)) {
2598 if ( (schema = ads_pull_string(ads, mem_ctx, res, "schemaNamingContext")) == NULL ) {
2599 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2602 if ( (*schema_path = talloc_strdup(mem_ctx, schema)) == NULL ) {
2603 return ADS_ERROR(LDAP_NO_MEMORY);
2606 ads_msgfree(ads, res);
2612 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2613 * @param ads connection to ads server
2614 * @return BOOL status of search (False if one or more attributes couldn't be
2615 * found in Active Directory)
2617 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2620 TALLOC_CTX *ctx = NULL;
2621 const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
2623 ADS_STRUCT *ads_s = ads;
2626 if ( (ctx = talloc_init("ads_check_sfu_mapping")) == NULL ) {
2630 /* establish a new ldap tcp session if necessary */
2633 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2634 ads->server.ldap_server )) == NULL )
2639 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2640 status = ads_connect( ads_s );
2641 if ( !ADS_ERR_OK(status))
2645 status = ads_schema_path( ads, ctx, &schema_path );
2646 if ( !ADS_ERR_OK(status) ) {
2647 DEBUG(3,("ads_check_sfu_mapping: Unable to retrieve schema DN!\n"));
2651 gidnumber = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2652 if (gidnumber == NULL)
2654 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2656 uidnumber = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2657 if (uidnumber == NULL)
2659 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2661 homedir = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2662 if (homedir == NULL)
2664 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2666 shell = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_SHELL_OID);
2669 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2671 gecos = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_GECOS_OID);
2674 ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
2678 /* free any temporary ads connections */
2679 if ( ads_s != ads ) {
2680 ads_destroy( &ads_s );
2684 talloc_destroy(ctx);
2691 * find the domain sid for our domain
2692 * @param ads connection to ads server
2693 * @param sid Pointer to domain sid
2694 * @return status of search
2696 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2698 const char *attrs[] = {"objectSid", NULL};
2702 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2704 if (!ADS_ERR_OK(rc)) return rc;
2705 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2706 ads_msgfree(ads, res);
2707 return ADS_ERROR_SYSTEM(ENOENT);
2709 ads_msgfree(ads, res);
2715 * find our site name
2716 * @param ads connection to ads server
2717 * @param mem_ctx Pointer to talloc context
2718 * @param site_name Pointer to the sitename
2719 * @return status of search
2721 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2725 const char *dn, *service_name;
2726 const char *attrs[] = { "dsServiceName", NULL };
2728 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2729 if (!ADS_ERR_OK(status)) {
2733 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2734 if (service_name == NULL) {
2735 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2738 /* go up three levels */
2739 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2741 return ADS_ERROR(LDAP_NO_MEMORY);
2744 *site_name = talloc_strdup(mem_ctx, dn);
2745 if (*site_name == NULL) {
2746 return ADS_ERROR(LDAP_NO_MEMORY);
2749 ads_msgfree(ads, res);
2753 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2758 * find the site dn where a machine resides
2759 * @param ads connection to ads server
2760 * @param mem_ctx Pointer to talloc context
2761 * @param computer_name name of the machine
2762 * @param site_name Pointer to the sitename
2763 * @return status of search
2765 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2769 const char *parent, *config_context, *filter;
2770 const char *attrs[] = { "configurationNamingContext", NULL };
2773 /* shortcut a query */
2774 if (strequal(computer_name, ads->config.ldap_server_name)) {
2775 return ads_site_dn(ads, mem_ctx, site_dn);
2778 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2779 if (!ADS_ERR_OK(status)) {
2783 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2784 if (config_context == NULL) {
2785 return ADS_ERROR(LDAP_NO_MEMORY);
2788 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2789 if (filter == NULL) {
2790 return ADS_ERROR(LDAP_NO_MEMORY);
2793 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2794 if (!ADS_ERR_OK(status)) {
2798 if (ads_count_replies(ads, res) != 1) {
2799 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2802 dn = ads_get_dn(ads, res);
2804 return ADS_ERROR(LDAP_NO_MEMORY);
2807 /* go up three levels */
2808 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2809 if (parent == NULL) {
2810 ads_memfree(ads, dn);
2811 return ADS_ERROR(LDAP_NO_MEMORY);
2814 *site_dn = talloc_strdup(mem_ctx, parent);
2815 if (*site_dn == NULL) {
2816 ads_memfree(ads, dn);
2817 ADS_ERROR(LDAP_NO_MEMORY);
2820 ads_memfree(ads, dn);
2821 ads_msgfree(ads, res);
2827 * get the upn suffixes for a domain
2828 * @param ads connection to ads server
2829 * @param mem_ctx Pointer to talloc context
2830 * @param suffixes Pointer to an array of suffixes
2831 * @param site_name Pointer to the number of suffixes
2832 * @return status of search
2834 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2838 const char *config_context, *base;
2839 const char *attrs[] = { "configurationNamingContext", NULL };
2840 const char *attrs2[] = { "uPNSuffixes", NULL };
2842 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2843 if (!ADS_ERR_OK(status)) {
2847 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2848 if (config_context == NULL) {
2849 return ADS_ERROR(LDAP_NO_MEMORY);
2852 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2854 return ADS_ERROR(LDAP_NO_MEMORY);
2857 status = ads_search_dn(ads, &res, base, attrs2);
2858 if (!ADS_ERR_OK(status)) {
2862 if (ads_count_replies(ads, res) != 1) {
2863 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2866 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2867 if (suffixes == NULL) {
2868 ads_msgfree(ads, res);
2869 return ADS_ERROR(LDAP_NO_MEMORY);
2872 ads_msgfree(ads, res);