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 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
41 * Connect to the LDAP server
42 * @param ads Pointer to an existing ADS_STRUCT
43 * @return status of connection
45 ADS_STATUS ads_connect(ADS_STRUCT *ads)
47 int version = LDAP_VERSION3;
51 ads->last_attempt = time(NULL);
55 if (ads->ldap_server) {
56 ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
59 /* if that failed then try each of the BDC's in turn */
61 struct in_addr *ip_list;
64 if (get_dc_list(False, ads->workgroup, &ip_list, &count)) {
66 for (i=0;i<count;i++) {
67 ads->ld = ldap_open(inet_ntoa(ip_list[i]),
72 SAFE_FREE(ads->ldap_server);
73 ads->ldap_server = strdup(inet_ntoa(ip_list[i]));
80 return ADS_ERROR_SYSTEM(errno);
83 DEBUG(3,("Connected to LDAP server %s\n", ads->ldap_server));
85 status = ads_server_info(ads);
86 if (!ADS_ERR_OK(status)) {
87 DEBUG(1,("Failed to get ldap server info\n"));
91 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
94 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
95 to MIT kerberos to work (tridge) */
98 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->server_realm);
99 setenv(env, inet_ntoa(*interpret_addr2(ads->ldap_server)), 1);
105 if ((code = ads_kinit_password(ads)))
106 return ADS_ERROR_KRB5(code);
109 return ads_sasl_bind(ads);
113 Duplicate a struct berval into talloc'ed memory
115 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
117 struct berval *value;
119 if (!in_val) return NULL;
121 value = talloc_zero(ctx, sizeof(struct berval));
122 if (in_val->bv_len == 0) return value;
124 value->bv_len = in_val->bv_len;
125 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
130 Make a values list out of an array of (struct berval *)
132 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
133 const struct berval **in_vals)
135 struct berval **values;
138 if (!in_vals) return NULL;
139 for (i=0; in_vals[i]; i++); /* count values */
140 values = (struct berval **) talloc_zero(ctx,
141 (i+1)*sizeof(struct berval *));
142 if (!values) return NULL;
144 for (i=0; in_vals[i]; i++) {
145 values[i] = dup_berval(ctx, in_vals[i]);
151 UTF8-encode a values list out of an array of (char *)
153 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
158 if (!in_vals) return NULL;
159 for (i=0; in_vals[i]; i++); /* count values */
160 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
161 if (!values) return NULL;
163 for (i=0; in_vals[i]; i++) {
164 push_utf8_talloc(ctx, &values[i], in_vals[i]);
170 Pull a (char *) array out of a UTF8-encoded values list
172 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
177 if (!in_vals) return NULL;
178 for (i=0; in_vals[i]; i++); /* count values */
179 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
180 if (!values) return NULL;
182 for (i=0; in_vals[i]; i++) {
183 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
189 * Do a search with paged results. cookie must be null on the first
190 * call, and then returned on each subsequent call. It will be null
191 * again when the entire search is complete
192 * @param ads connection to ads server
193 * @param bind_path Base dn for the search
194 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
195 * @param exp Search expression - specified in local charset
196 * @param attrs Attributes to retrieve - specified in utf8 or ascii
197 * @param res ** which will contain results - free res* with ads_msgfree()
198 * @param count Number of entries retrieved on this page
199 * @param cookie The paged results cookie to be returned on subsequent calls
200 * @return status of search
202 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
203 int scope, const char *exp,
204 const char **attrs, void **res,
205 int *count, void **cookie)
208 char *utf8_exp, *utf8_path, **search_attrs;
209 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
210 BerElement *cookie_be = NULL;
211 struct berval *cookie_bv= NULL;
216 if (!(ctx = talloc_init()))
217 return ADS_ERROR(LDAP_NO_MEMORY);
219 /* 0 means the conversion worked but the result was empty
220 so we only fail if it's negative. In any case, it always
221 at least nulls out the dest */
222 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
223 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
228 if (!attrs || !(*attrs))
231 /* This would be the utf8-encoded version...*/
232 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
233 if (!(str_list_copy(&search_attrs, attrs)))
241 /* Paged results only available on ldap v3 or later */
242 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
243 if (version < LDAP_VERSION3) {
244 rc = LDAP_NOT_SUPPORTED;
248 cookie_be = ber_alloc_t(LBER_USE_DER);
249 if (cookie && *cookie) {
250 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
251 ber_bvfree(*cookie); /* don't need it from last time */
254 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
256 ber_flatten(cookie_be, &cookie_bv);
257 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
258 PagedResults.ldctl_iscritical = (char) 1;
259 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
260 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
262 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
263 NoReferrals.ldctl_iscritical = (char) 0;
264 NoReferrals.ldctl_value.bv_len = 0;
265 NoReferrals.ldctl_value.bv_val = "";
268 controls[0] = &NoReferrals;
269 controls[1] = &PagedResults;
274 /* we need to disable referrals as the openldap libs don't
275 handle them and paged results at the same time. Using them
276 together results in the result record containing the server
277 page control being removed from the result list (tridge/jmcd)
279 leaving this in despite the control that says don't generate
280 referrals, in case the server doesn't support it (jmcd)
282 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
284 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
285 search_attrs, 0, controls,
286 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
288 ber_free(cookie_be, 1);
289 ber_bvfree(cookie_bv);
292 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
296 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
297 NULL, &rcontrols, 0);
303 for (i=0; rcontrols[i]; i++) {
304 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
305 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
306 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
308 /* the berval is the cookie, but must be freed when
310 if (cookie_bv->bv_len) /* still more to do */
311 *cookie=ber_bvdup(cookie_bv);
314 ber_bvfree(cookie_bv);
315 ber_free(cookie_be, 1);
319 ldap_controls_free(rcontrols);
323 /* if/when we decide to utf8-encode attrs, take out this next line */
324 str_list_free(&search_attrs);
326 return ADS_ERROR(rc);
331 * Get all results for a search. This uses ads_do_paged_search() to return
332 * all entries in a large search.
333 * @param ads connection to ads server
334 * @param bind_path Base dn for the search
335 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
336 * @param exp Search expression
337 * @param attrs Attributes to retrieve
338 * @param res ** which will contain results - free res* with ads_msgfree()
339 * @return status of search
341 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
342 int scope, const char *exp,
343 const char **attrs, void **res)
349 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
352 if (!ADS_ERR_OK(status)) return status;
357 LDAPMessage *msg, *next;
359 status2 = ads_do_paged_search(ads, bind_path, scope, exp,
360 attrs, &res2, &count, &cookie);
362 if (!ADS_ERR_OK(status2)) break;
364 /* this relies on the way that ldap_add_result_entry() works internally. I hope
365 that this works on all ldap libs, but I have only tested with openldap */
366 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
367 next = ads_next_entry(ads, msg);
368 ldap_add_result_entry((LDAPMessage **)res, msg);
370 /* note that we do not free res2, as the memory is now
371 part of the main returned list */
378 * Run a function on all results for a search. Uses ads_do_paged_search() and
379 * runs the function as each page is returned, using ads_process_results()
380 * @param ads connection to ads server
381 * @param bind_path Base dn for the search
382 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
383 * @param exp Search expression - specified in local charset
384 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
385 * @param fn Function which takes attr name, values list, and data_area
386 * @param data_area Pointer which is passed to function on each call
387 * @return status of search
389 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
390 int scope, const char *exp, const char **attrs,
391 BOOL(*fn)(char *, void **, void *),
399 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
402 if (!ADS_ERR_OK(status)) return status;
404 ads_process_results(ads, res, fn, data_area);
405 ads_msgfree(ads, res);
408 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
409 &res, &count, &cookie);
411 if (!ADS_ERR_OK(status)) break;
413 ads_process_results(ads, res, fn, data_area);
414 ads_msgfree(ads, res);
421 * Do a search with a timeout.
422 * @param ads connection to ads server
423 * @param bind_path Base dn for the search
424 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
425 * @param exp Search expression
426 * @param attrs Attributes to retrieve
427 * @param res ** which will contain results - free res* with ads_msgfree()
428 * @return status of search
430 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
432 const char **attrs, void **res)
434 struct timeval timeout;
436 char *utf8_exp, *utf8_path, **search_attrs = NULL;
439 if (!(ctx = talloc_init()))
440 return ADS_ERROR(LDAP_NO_MEMORY);
442 /* 0 means the conversion worked but the result was empty
443 so we only fail if it's negative. In any case, it always
444 at least nulls out the dest */
445 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
446 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
451 if (!attrs || !(*attrs))
454 /* This would be the utf8-encoded version...*/
455 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
456 if (!(str_list_copy(&search_attrs, attrs)))
463 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
467 /* see the note in ads_do_paged_search - we *must* disable referrals */
468 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
470 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
471 search_attrs, 0, NULL, NULL,
472 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
474 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
475 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
481 /* if/when we decide to utf8-encode attrs, take out this next line */
482 str_list_free(&search_attrs);
483 return ADS_ERROR(rc);
486 * Do a general ADS search
487 * @param ads connection to ads server
488 * @param res ** which will contain results - free res* with ads_msgfree()
489 * @param exp Search expression
490 * @param attrs Attributes to retrieve
491 * @return status of search
493 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
497 return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
502 * Do a search on a specific DistinguishedName
503 * @param ads connection to ads server
504 * @param res ** which will contain results - free res* with ads_msgfree()
505 * @param dn DistinguishName to search
506 * @param attrs Attributes to retrieve
507 * @return status of search
509 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
513 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
517 * Free up memory from a ads_search
518 * @param ads connection to ads server
519 * @param msg Search results to free
521 void ads_msgfree(ADS_STRUCT *ads, void *msg)
528 * Free up memory from various ads requests
529 * @param ads connection to ads server
530 * @param mem Area to free
532 void ads_memfree(ADS_STRUCT *ads, void *mem)
538 * Get a dn from search results
539 * @param ads connection to ads server
540 * @param res Search results
543 char *ads_get_dn(ADS_STRUCT *ads, void *res)
545 char *utf8_dn, *unix_dn;
547 utf8_dn = ldap_get_dn(ads->ld, res);
548 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
549 ldap_memfree(utf8_dn);
554 * Find a machine account given a hostname
555 * @param ads connection to ads server
556 * @param res ** which will contain results - free res* with ads_msgfree()
557 * @param host Hostname to search for
558 * @return status of search
560 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
564 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
566 /* the easiest way to find a machine account anywhere in the tree
567 is to look for hostname$ */
568 asprintf(&exp, "(samAccountName=%s$)", host);
569 status = ads_search(ads, res, exp, attrs);
575 * Initialize a list of mods to be used in a modify request
576 * @param ctx An initialized TALLOC_CTX
577 * @return allocated ADS_MODLIST
579 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
581 #define ADS_MODLIST_ALLOC_SIZE 10
584 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
585 (ADS_MODLIST_ALLOC_SIZE + 1))))
586 /* -1 is safety to make sure we don't go over the end.
587 need to reset it to NULL before doing ldap modify */
588 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
595 add an attribute to the list, with values list already constructed
597 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
598 int mod_op, const char *name,
602 LDAPMod **modlist = (LDAPMod **) *mods;
607 mod_op = LDAP_MOD_DELETE;
609 if (mod_op & LDAP_MOD_BVALUES)
610 values = (void **) ads_dup_values(ctx,
611 (const struct berval **)invals);
613 values = (void **) ads_push_strvals(ctx,
614 (const char **) invals);
617 /* find the first empty slot */
618 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
620 if (modlist[curmod] == (LDAPMod *) -1) {
621 if (!(modlist = talloc_realloc(ctx, modlist,
622 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
623 return ADS_ERROR(LDAP_NO_MEMORY);
624 memset(&modlist[curmod], 0,
625 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
626 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
630 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
631 return ADS_ERROR(LDAP_NO_MEMORY);
632 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
633 if (mod_op & LDAP_MOD_BVALUES)
634 modlist[curmod]->mod_bvalues = (struct berval **) values;
636 modlist[curmod]->mod_values = (char **) values;
637 modlist[curmod]->mod_op = mod_op;
638 return ADS_ERROR(LDAP_SUCCESS);
642 * Add a single string value to a mod list
643 * @param ctx An initialized TALLOC_CTX
644 * @param mods An initialized ADS_MODLIST
645 * @param name The attribute name to add
646 * @param val The value to add - NULL means DELETE
647 * @return ADS STATUS indicating success of add
649 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
650 const char *name, const char *val)
652 const char *values[2] = {val, NULL};
654 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
655 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
656 (const void **) values);
660 * Add an array of string values to a mod list
661 * @param ctx An initialized TALLOC_CTX
662 * @param mods An initialized ADS_MODLIST
663 * @param name The attribute name to add
664 * @param vals The array of string values to add - NULL means DELETE
665 * @return ADS STATUS indicating success of add
667 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
668 const char *name, const char **vals)
671 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
672 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
673 name, (const void **) vals);
677 * Add a single ber-encoded value to a mod list
678 * @param ctx An initialized TALLOC_CTX
679 * @param mods An initialized ADS_MODLIST
680 * @param name The attribute name to add
681 * @param val The value to add - NULL means DELETE
682 * @return ADS STATUS indicating success of add
684 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
685 const char *name, const struct berval *val)
687 const struct berval *values[2] = {val, NULL};
689 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
690 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
691 name, (const void **) values);
695 * Perform an ldap modify
696 * @param ads connection to ads server
697 * @param mod_dn DistinguishedName to modify
698 * @param mods list of modifications to perform
699 * @return status of modify
701 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
704 char *utf8_dn = NULL;
706 this control is needed to modify that contains a currently
707 non-existent attribute (but allowable for the object) to run
709 LDAPControl PermitModify = {
710 "1.2.840.113556.1.4.1413",
713 LDAPControl *controls[2];
715 controls[0] = &PermitModify;
718 push_utf8_allocate((void **) &utf8_dn, mod_dn);
720 /* find the end of the list, marked by NULL or -1 */
721 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
722 /* make sure the end of the list is NULL */
724 ret = ldap_modify_ext_s(ads->ld, utf8_dn ? utf8_dn : mod_dn,
725 (LDAPMod **) mods, controls, NULL);
727 return ADS_ERROR(ret);
731 * Perform an ldap add
732 * @param ads connection to ads server
733 * @param new_dn DistinguishedName to add
734 * @param mods list of attributes and values for DN
735 * @return status of add
737 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
740 char *utf8_dn = NULL;
742 push_utf8_allocate((void **) &utf8_dn, new_dn);
744 /* find the end of the list, marked by NULL or -1 */
745 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
746 /* make sure the end of the list is NULL */
749 ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
751 return ADS_ERROR(ret);
755 * Delete a DistinguishedName
756 * @param ads connection to ads server
757 * @param new_dn DistinguishedName to delete
758 * @return status of delete
760 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
763 char *utf8_dn = NULL;
764 push_utf8_allocate((void **) &utf8_dn, del_dn);
765 ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
766 return ADS_ERROR(ret);
770 * Build an org unit string
771 * if org unit is Computers or blank then assume a container, otherwise
772 * assume a \ separated list of organisational units
773 * @param org_unit Organizational unit
774 * @return org unit string - caller must free
776 char *ads_ou_string(const char *org_unit)
778 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
779 return strdup("cn=Computers");
782 return ads_build_path(org_unit, "\\/", "ou=", 1);
788 add a machine account to the ADS server
790 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
791 const char *org_unit)
794 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
798 const char *objectClass[] = {"top", "person", "organizationalPerson",
799 "user", "computer", NULL};
801 if (!(ctx = talloc_init_named("machine_account")))
802 return ADS_ERROR(LDAP_NO_MEMORY);
804 ret = ADS_ERROR(LDAP_NO_MEMORY);
806 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
808 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->realm)))
810 ou_str = ads_ou_string(org_unit);
811 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
817 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
819 if (!(controlstr = talloc_asprintf(ctx, "%u",
820 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
821 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
824 if (!(mods = ads_init_mods(ctx)))
827 ads_mod_str(ctx, &mods, "cn", hostname);
828 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
829 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
830 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
831 ads_mod_str(ctx, &mods, "servicePrincipalName", host_spn);
832 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
833 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
834 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
835 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
837 ads_gen_add(ads, new_dn, mods);
838 ret = ads_set_machine_sd(ads, hostname, new_dn);
846 dump a binary result from ldap
848 static void dump_binary(const char *field, struct berval **values)
851 for (i=0; values[i]; i++) {
852 printf("%s: ", field);
853 for (j=0; j<values[i]->bv_len; j++) {
854 printf("%02X", (unsigned char)values[i]->bv_val[j]);
861 dump a sid result from ldap
863 static void dump_sid(const char *field, struct berval **values)
866 for (i=0; values[i]; i++) {
868 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
869 printf("%s: %s\n", field, sid_string_static(&sid));
874 dump ntSecurityDescriptor
876 static void dump_sd(const char *filed, struct berval **values)
883 if (!(ctx = talloc_init_named("sec_io_desc")))
887 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
888 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
892 if (!sec_io_desc("sd", &psd, &ps, 1)) {
897 if (psd) ads_disp_sd(psd);
904 dump a string result from ldap
906 static void dump_string(const char *field, char **values)
909 for (i=0; values[i]; i++) {
910 printf("%s: %s\n", field, values[i]);
915 dump a field from LDAP on stdout
919 static BOOL ads_dump_field(char *field, void **values, void *data_area)
924 void (*handler)(const char *, struct berval **);
926 {"objectGUID", False, dump_binary},
927 {"nTSecurityDescriptor", False, dump_sd},
928 {"objectSid", False, dump_sid},
933 if (!field) { /* must be end of an entry */
938 for (i=0; handlers[i].name; i++) {
939 if (StrCaseCmp(handlers[i].name, field) == 0) {
940 if (!values) /* first time, indicate string or not */
941 return handlers[i].string;
942 handlers[i].handler(field, (struct berval **) values);
946 if (!handlers[i].name) {
947 if (!values) /* first time, indicate string conversion */
949 dump_string(field, (char **)values);
955 * Dump a result from LDAP on stdout
957 * @param ads connection to ads server
958 * @param res Results to dump
961 void ads_dump(ADS_STRUCT *ads, void *res)
963 ads_process_results(ads, res, ads_dump_field, NULL);
967 * Walk through results, calling a function for each entry found.
968 * The function receives a field name, a berval * array of values,
969 * and a data area passed through from the start. The function is
970 * called once with null for field and values at the end of each
972 * @param ads connection to ads server
973 * @param res Results to process
974 * @param fn Function for processing each result
975 * @param data_area user-defined area to pass to function
977 void ads_process_results(ADS_STRUCT *ads, void *res,
978 BOOL(*fn)(char *, void **, void *),
984 if (!(ctx = talloc_init()))
987 for (msg = ads_first_entry(ads, res); msg;
988 msg = ads_next_entry(ads, msg)) {
992 for (utf8_field=ldap_first_attribute(ads->ld,
993 (LDAPMessage *)msg,&b);
995 utf8_field=ldap_next_attribute(ads->ld,
996 (LDAPMessage *)msg,b)) {
997 struct berval **ber_vals;
998 char **str_vals, **utf8_vals;
1002 pull_utf8_talloc(ctx, &field, utf8_field);
1003 string = fn(field, NULL, data_area);
1006 utf8_vals = ldap_get_values(ads->ld,
1007 (LDAPMessage *)msg, field);
1008 str_vals = ads_pull_strvals(ctx,
1009 (const char **) utf8_vals);
1010 fn(field, (void **) str_vals, data_area);
1011 ldap_value_free(utf8_vals);
1013 ber_vals = ldap_get_values_len(ads->ld,
1014 (LDAPMessage *)msg, field);
1015 fn(field, (void **) ber_vals, data_area);
1017 ldap_value_free_len(ber_vals);
1019 ldap_memfree(utf8_field);
1022 talloc_destroy_pool(ctx);
1023 fn(NULL, NULL, data_area); /* completed an entry */
1026 talloc_destroy(ctx);
1030 * count how many replies are in a LDAPMessage
1031 * @param ads connection to ads server
1032 * @param res Results to count
1033 * @return number of replies
1035 int ads_count_replies(ADS_STRUCT *ads, void *res)
1037 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1041 * Join a machine to a realm
1042 * Creates the machine account and sets the machine password
1043 * @param ads connection to ads server
1044 * @param hostname name of host to add
1045 * @param org_unit Organizational unit to place machine in
1046 * @return status of join
1048 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1054 /* hostname must be lowercase */
1055 host = strdup(hostname);
1058 status = ads_find_machine_acct(ads, (void **)&res, host);
1059 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1060 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1061 status = ads_leave_realm(ads, host);
1062 if (!ADS_ERR_OK(status)) {
1063 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1069 status = ads_add_machine_acct(ads, host, org_unit);
1070 if (!ADS_ERR_OK(status)) {
1071 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1075 status = ads_find_machine_acct(ads, (void **)&res, host);
1076 if (!ADS_ERR_OK(status)) {
1077 DEBUG(0, ("Host account test failed\n"));
1087 * Delete a machine from the realm
1088 * @param ads connection to ads server
1089 * @param hostname Machine to remove
1090 * @return status of delete
1092 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1096 char *hostnameDN, *host;
1099 /* hostname must be lowercase */
1100 host = strdup(hostname);
1103 status = ads_find_machine_acct(ads, &res, host);
1104 if (!ADS_ERR_OK(status)) {
1105 DEBUG(0, ("Host account for %s does not exist.\n", host));
1109 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1110 rc = ldap_delete_s(ads->ld, hostnameDN);
1111 ads_memfree(ads, hostnameDN);
1112 if (rc != LDAP_SUCCESS) {
1113 return ADS_ERROR(rc);
1116 status = ads_find_machine_acct(ads, &res, host);
1117 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1118 DEBUG(0, ("Failed to remove host account.\n"));
1128 * add machine account to existing security descriptor
1129 * @param ads connection to ads server
1130 * @param hostname machine to add
1131 * @param dn DN of security descriptor
1134 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1136 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1139 struct berval **bvals = 0;
1140 struct berval bval = {0, NULL};
1144 LDAPMessage *res = 0;
1145 LDAPMessage *msg = 0;
1146 ADS_MODLIST mods = 0;
1152 TALLOC_CTX *ctx = 0;
1154 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1156 ret = ADS_ERROR(LDAP_SUCCESS);
1158 asprintf(&exp, "(samAccountName=%s$)", hostname);
1159 ret = ads_search(ads, (void *) &res, exp, attrs);
1161 if (!ADS_ERR_OK(ret)) return ret;
1163 msg = ads_first_entry(ads, res);
1164 bvals = ldap_get_values_len(ads->ld, msg, attrs[0]);
1165 ads_pull_sid(ads, msg, attrs[1], &sid);
1166 ads_msgfree(ads, res);
1168 file_save("/tmp/sec_desc.old", bvals[0]->bv_val, bvals[0]->bv_len);
1170 if (!(ctx = talloc_init_named("sec_io_desc")))
1171 return ADS_ERROR(LDAP_NO_MEMORY);
1173 prs_init(&ps, bvals[0]->bv_len, ctx, UNMARSHALL);
1174 prs_append_data(&ps, bvals[0]->bv_val, bvals[0]->bv_len);
1176 ldap_value_free_len(bvals);
1178 if (!sec_io_desc("sd", &psd, &ps, 1))
1179 goto ads_set_sd_error;
1181 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1183 if (!NT_STATUS_IS_OK(status))
1184 goto ads_set_sd_error;
1186 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1187 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1))
1188 goto ads_set_sd_error;
1191 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1193 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1195 bval.bv_len = sd_size;
1196 bval.bv_val = prs_data_p(&ps_wire);
1197 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1198 ret = ads_gen_mod(ads, dn, mods);
1201 prs_mem_free(&ps_wire);
1202 talloc_destroy(ctx);
1207 prs_mem_free(&ps_wire);
1208 talloc_destroy(ctx);
1209 return ADS_ERROR(LDAP_NO_MEMORY);
1213 * Set the machine account password
1214 * @param ads connection to ads server
1215 * @param hostname machine whose password is being set
1216 * @param password new password
1217 * @return status of password change
1219 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
1220 const char *hostname,
1221 const char *password)
1224 char *host = strdup(hostname);
1227 if (!ads->kdc_server) {
1228 DEBUG(0, ("Unable to find KDC server\n"));
1229 return ADS_ERROR(LDAP_SERVER_DOWN);
1235 we need to use the '$' form of the name here, as otherwise the
1236 server might end up setting the password for a user instead
1238 asprintf(&principal, "%s$@%s", host, ads->realm);
1240 status = krb5_set_password(ads->kdc_server, principal, password);
1249 * pull the first entry from a ADS result
1250 * @param ads connection to ads server
1251 * @param res Results of search
1252 * @return first entry from result
1254 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1256 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1260 * pull the next entry from a ADS result
1261 * @param ads connection to ads server
1262 * @param res Results of search
1263 * @return next entry from result
1265 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1267 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1271 * pull a single string from a ADS result
1272 * @param ads connection to ads server
1273 * @param mem_ctx TALLOC_CTX to use for allocating result string
1274 * @param msg Results of search
1275 * @param field Attribute to retrieve
1276 * @return Result string in talloc context
1278 char *ads_pull_string(ADS_STRUCT *ads,
1279 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1286 values = ldap_get_values(ads->ld, msg, field);
1287 if (!values) return NULL;
1290 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1296 ldap_value_free(values);
1301 * pull an array of strings from a ADS result
1302 * @param ads connection to ads server
1303 * @param mem_ctx TALLOC_CTX to use for allocating result string
1304 * @param msg Results of search
1305 * @param field Attribute to retrieve
1306 * @return Result strings in talloc context
1308 char **ads_pull_strings(ADS_STRUCT *ads,
1309 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1315 values = ldap_get_values(ads->ld, msg, field);
1316 if (!values) return NULL;
1318 for (i=0;values[i];i++) /* noop */ ;
1321 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1324 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1330 ldap_value_free(values);
1336 * pull a single uint32 from a ADS result
1337 * @param ads connection to ads server
1338 * @param msg Results of search
1339 * @param field Attribute to retrieve
1340 * @param v Pointer to int to store result
1341 * @return boolean inidicating success
1343 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1344 void *msg, const char *field, uint32 *v)
1348 values = ldap_get_values(ads->ld, msg, field);
1349 if (!values) return False;
1351 ldap_value_free(values);
1355 *v = atoi(values[0]);
1356 ldap_value_free(values);
1361 * pull a single DOM_SID from a ADS result
1362 * @param ads connection to ads server
1363 * @param msg Results of search
1364 * @param field Attribute to retrieve
1365 * @param sid Pointer to sid to store result
1366 * @return boolean inidicating success
1368 BOOL ads_pull_sid(ADS_STRUCT *ads,
1369 void *msg, const char *field, DOM_SID *sid)
1371 struct berval **values;
1374 values = ldap_get_values_len(ads->ld, msg, field);
1376 if (!values) return False;
1379 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1382 ldap_value_free_len(values);
1387 * pull an array of DOM_SIDs from a ADS result
1388 * @param ads connection to ads server
1389 * @param mem_ctx TALLOC_CTX for allocating sid array
1390 * @param msg Results of search
1391 * @param field Attribute to retrieve
1392 * @param sids pointer to sid array to allocate
1393 * @return the count of SIDs pulled
1395 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1396 void *msg, const char *field, DOM_SID **sids)
1398 struct berval **values;
1402 values = ldap_get_values_len(ads->ld, msg, field);
1404 if (!values) return 0;
1406 for (i=0; values[i]; i++) /* nop */ ;
1408 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1411 for (i=0; values[i]; i++) {
1412 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1416 ldap_value_free_len(values);
1422 * find the update serial number - this is the core of the ldap cache
1423 * @param ads connection to ads server
1424 * @param ads connection to ADS server
1425 * @param usn Pointer to retrieved update serial number
1426 * @return status of search
1428 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1430 const char *attrs[] = {"highestCommittedUSN", NULL};
1434 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1435 if (!ADS_ERR_OK(status)) return status;
1437 if (ads_count_replies(ads, res) != 1) {
1438 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1441 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1442 ads_msgfree(ads, res);
1448 * Find the servers name and realm - this can be done before authentication
1449 * The ldapServiceName field on w2k looks like this:
1450 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1451 * @param ads connection to ads server
1452 * @return status of search
1454 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1456 const char *attrs[] = {"ldapServiceName", NULL};
1462 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1463 if (!ADS_ERR_OK(status)) return status;
1465 values = ldap_get_values(ads->ld, res, "ldapServiceName");
1466 if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1468 p = strchr(values[0], ':');
1470 ldap_value_free(values);
1472 return ADS_ERROR(LDAP_DECODING_ERROR);
1475 SAFE_FREE(ads->ldap_server_name);
1477 ads->ldap_server_name = strdup(p+1);
1478 p = strchr(ads->ldap_server_name, '$');
1479 if (!p || p[1] != '@') {
1480 ldap_value_free(values);
1482 SAFE_FREE(ads->ldap_server_name);
1483 return ADS_ERROR(LDAP_DECODING_ERROR);
1488 SAFE_FREE(ads->server_realm);
1489 SAFE_FREE(ads->bind_path);
1491 ads->server_realm = strdup(p+2);
1492 ads->bind_path = ads_build_dn(ads->server_realm);
1494 /* in case the realm isn't configured in smb.conf */
1495 if (!ads->realm || !ads->realm[0]) {
1496 SAFE_FREE(ads->realm);
1497 ads->realm = strdup(ads->server_realm);
1500 DEBUG(3,("got ldap server name %s@%s\n",
1501 ads->ldap_server_name, ads->realm));
1508 * find the list of trusted domains
1509 * @param ads connection to ads server
1510 * @param mem_ctx TALLOC_CTX for allocating results
1511 * @param num_trusts pointer to number of trusts
1512 * @param names pointer to trusted domain name list
1513 * @param sids pointer to list of sids of trusted domains
1514 * @return the count of SIDs pulled
1516 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1517 int *num_trusts, char ***names, DOM_SID **sids)
1519 const char *attrs[] = {"flatName", "securityIdentifier", NULL};
1526 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1527 if (!ADS_ERR_OK(status)) return status;
1529 count = ads_count_replies(ads, res);
1531 ads_msgfree(ads, res);
1532 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1535 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1536 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1537 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1539 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1540 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
1541 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1546 ads_msgfree(ads, res);
1554 * find the domain sid for our domain
1555 * @param ads connection to ads server
1556 * @param sid Pointer to domain sid
1557 * @return status of search
1559 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1561 const char *attrs[] = {"objectSid", NULL};
1565 rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1567 if (!ADS_ERR_OK(rc)) return rc;
1568 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1569 return ADS_ERROR_SYSTEM(ENOENT);
1571 ads_msgfree(ads, res);