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.
28 connect to the LDAP server
30 ADS_STATUS ads_connect(ADS_STRUCT *ads)
32 int version = LDAP_VERSION3;
36 ads->last_attempt = time(NULL);
38 ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
40 return ADS_ERROR_SYSTEM(errno);
42 status = ads_server_info(ads);
43 if (!ADS_ERR_OK(status)) {
44 DEBUG(1,("Failed to get ldap server info\n"));
48 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
51 if ((code = ads_kinit_password(ads)))
52 return ADS_ERROR_KRB5(code);
55 return ads_sasl_bind(ads);
59 do a search with a timeout
61 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
63 const char **attrs, void **res)
65 struct timeval timeout;
68 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
72 rc = ldap_search_ext_s(ads->ld,
74 exp, (char **) attrs, 0, NULL, NULL,
75 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
79 do a general ADS search
81 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
85 return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
90 do a search on a specific DistinguishedName
92 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
96 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
100 free up memory from a ads_search
102 void ads_msgfree(ADS_STRUCT *ads, void *msg)
109 free up memory from various ads requests
111 void ads_memfree(ADS_STRUCT *ads, void *mem)
118 get a dn from search results
120 char *ads_get_dn(ADS_STRUCT *ads, void *res)
122 return ldap_get_dn(ads->ld, res);
126 find a machine account given a hostname
128 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
132 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
134 /* the easiest way to find a machine account anywhere in the tree
135 is to look for hostname$ */
136 asprintf(&exp, "(samAccountName=%s$)", host);
137 status = ads_search(ads, res, exp, attrs);
143 duplicate an already-assembled list of values so that it can be
144 freed as part of the standard msgfree call
146 static char **ads_dup_values(TALLOC_CTX *ctx, char **values)
150 #define ADS_MAX_NUM_VALUES 32
152 for (i=0; values[i] && i<ADS_MAX_NUM_VALUES; i++);
153 if (!(newvals = talloc_zero(ctx, (i+1)*sizeof(char *))))
155 for (i=0; values[i] && i<ADS_MAX_NUM_VALUES; i++)
156 newvals[i] = values[i];
162 initialize a list of mods
165 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
167 #define ADS_MODLIST_ALLOC_SIZE 10
170 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
171 (ADS_MODLIST_ALLOC_SIZE + 1))))
172 /* -1 is safety to make sure we don't go over the end.
173 need to reset it to NULL before doing ldap modify */
174 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
180 add an attribute to the list, with values list already constructed
182 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
183 int mod_op, char *name, char **values)
186 LDAPMod **modlist = (LDAPMod **) *mods;
188 /* find the first empty slot */
189 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
191 if (modlist[curmod] == (LDAPMod *) -1) {
192 if (!(modlist = talloc_realloc(ctx, modlist,
193 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
194 return ADS_ERROR(LDAP_NO_MEMORY);
195 memset(&modlist[curmod], 0,
196 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
197 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
201 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
202 return ADS_ERROR(LDAP_NO_MEMORY);
203 modlist[curmod]->mod_type = name;
204 if (mod_op & LDAP_MOD_BVALUES)
205 modlist[curmod]->mod_bvalues = (struct berval **) values;
207 modlist[curmod]->mod_values = values;
208 modlist[curmod]->mod_op = mod_op;
209 return ADS_ERROR(LDAP_SUCCESS);
212 ADS_STATUS ads_mod_add_list(TALLOC_CTX *ctx, ADS_MODLIST *mods,
213 char *name, char **values)
215 char **newvals = ads_dup_values(ctx, values);
217 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, newvals);
219 return ADS_ERROR(LDAP_NO_MEMORY);
222 ADS_STATUS ads_mod_repl_list(TALLOC_CTX *ctx, ADS_MODLIST *mods,
223 char *name, char **values)
226 if (values && *values) {
227 if (!(newvals = ads_dup_values(ctx, values)))
228 return ADS_ERROR(LDAP_NO_MEMORY);
230 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
234 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
238 add an attribute to the list, with values list to be built from args
240 ADS_STATUS ads_mod_add_var(TALLOC_CTX *ctx, ADS_MODLIST *mods,
241 int mod_op, char *name, ...)
244 int num_vals, i, do_op;
245 char *value, **values;
247 /* count the number of values */
249 for (num_vals=0; va_arg(ap, char *); num_vals++);
253 if (!(values = talloc_zero(ctx, sizeof(char *)*(num_vals+1))))
254 return ADS_ERROR(LDAP_NO_MEMORY);
256 for (i=0; (value = (char *) va_arg(ap, char *)) &&
263 do_op = LDAP_MOD_DELETE;
266 return ads_modlist_add(ctx, mods, do_op, name, values);
269 ADS_STATUS ads_mod_add_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
270 int mod_op, char *name, ...)
273 int num_vals, i, do_op;
274 char *value, **values;
276 /* count the number of values */
278 for (num_vals=0; va_arg(ap, struct berval *); num_vals++);
282 if (!(values = talloc_zero(ctx, sizeof(struct berval) *
284 return ADS_ERROR(LDAP_NO_MEMORY);
286 for (i=0; (value = (char *) va_arg(ap, char *)) &&
293 do_op = LDAP_MOD_DELETE;
296 do_op |= LDAP_MOD_BVALUES;
297 return ads_modlist_add(ctx, mods, do_op, name, values);
300 ADS_STATUS ads_mod_repl(TALLOC_CTX *ctx, ADS_MODLIST *mods,
301 char *name, char *val)
304 return ads_mod_add_var(ctx, mods, LDAP_MOD_REPLACE,
307 return ads_mod_add_var(ctx, mods, LDAP_MOD_DELETE, name, NULL);
310 ADS_STATUS ads_mod_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
311 char *name, char *val)
313 return ads_mod_add_var(ctx, mods, LDAP_MOD_ADD, name, val, NULL);
316 ADS_STATUS ads_mod_add_len(TALLOC_CTX *ctx, ADS_MODLIST *mods,
317 char *name, size_t size, char *val)
319 struct berval *bval = NULL;
321 if (!(bval = talloc_zero(ctx, sizeof(struct berval *))))
322 return ADS_ERROR(LDAP_NO_MEMORY);
323 if (!(bval->bv_val = talloc_zero(ctx, sizeof(char *))))
324 return ADS_ERROR(LDAP_NO_MEMORY);
328 return ads_mod_add_ber(ctx, mods, LDAP_MOD_ADD, name, bval, NULL);
331 ADS_STATUS ads_mod_repl_len(TALLOC_CTX *ctx, ADS_MODLIST *mods,
332 char *name, size_t size, char *val)
334 struct berval *bval = NULL;
336 if (!(bval = talloc_zero(ctx, sizeof(struct berval *))))
337 return ADS_ERROR(LDAP_NO_MEMORY);
340 return ads_mod_add_ber(ctx, mods, LDAP_MOD_DELETE, name, NULL);
342 if (!(bval->bv_val = talloc_zero(ctx, sizeof(char *))))
343 return ADS_ERROR(LDAP_NO_MEMORY);
346 return ads_mod_add_ber(ctx, mods, LDAP_MOD_REPLACE, name,
351 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
355 this control is needed to modify that contains a currently
356 non-existent attribute (but allowable for the object) to run
358 LDAPControl PermitModify = {
359 "1.2.840.113556.1.4.1413",
362 LDAPControl *controls[2];
364 controls[0] = &PermitModify;
367 /* find the end of the list, marked by NULL or -1 */
368 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
369 /* make sure the end of the list is NULL */
371 ret = ldap_modify_ext_s(ads->ld, mod_dn, (LDAPMod **) mods,
373 return ADS_ERROR(ret);
376 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
380 /* find the end of the list, marked by NULL or -1 */
381 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
382 /* make sure the end of the list is NULL */
385 return ADS_ERROR(ldap_add_s(ads->ld, new_dn, mods));
388 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
390 return ADS_ERROR(ldap_delete(ads->ld, del_dn));
394 build an org unit string
395 if org unit is Computers or blank then assume a container, otherwise
396 assume a \ separated list of organisational units
399 char *ads_ou_string(const char *org_unit)
401 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
402 return strdup("cn=Computers");
405 return ads_build_path(org_unit, "\\/", "ou=", 1);
411 add a machine account to the ADS server
413 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
414 const char *org_unit)
417 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
422 if (!(ctx = talloc_init_named("machine_account")))
423 return ADS_ERROR(LDAP_NO_MEMORY);
425 ret = ADS_ERROR(LDAP_NO_MEMORY);
427 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
429 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->realm)))
431 ou_str = ads_ou_string(org_unit);
432 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
438 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
440 if (!(controlstr = talloc_asprintf(ctx, "%u",
441 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
442 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
445 if (!(mods = ads_init_mods(ctx)))
448 ads_mod_add(ctx, &mods, "cn", hostname);
449 ads_mod_add(ctx, &mods, "sAMAccountName", samAccountName);
450 ads_mod_add_var(ctx, &mods, LDAP_MOD_ADD, "objectClass",
451 "top", "person", "organizationalPerson",
452 "user", "computer", NULL);
453 ads_mod_add(ctx, &mods, "userPrincipalName", host_upn);
454 ads_mod_add(ctx, &mods, "servicePrincipalName", host_spn);
455 ads_mod_add(ctx, &mods, "dNSHostName", hostname);
456 ads_mod_add(ctx, &mods, "userAccountControl", controlstr);
457 ads_mod_add(ctx, &mods, "operatingSystem", "Samba");
458 ads_mod_add(ctx, &mods, "operatingSystemVersion", VERSION);
460 ret = ads_gen_add(ads, new_dn, mods);
468 dump a binary result from ldap
470 static void dump_binary(const char *field, struct berval **values)
473 for (i=0; values[i]; i++) {
474 printf("%s: ", field);
475 for (j=0; j<values[i]->bv_len; j++) {
476 printf("%02X", (unsigned char)values[i]->bv_val[j]);
483 dump a sid result from ldap
485 static void dump_sid(const char *field, struct berval **values)
488 for (i=0; values[i]; i++) {
490 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
491 printf("%s: %s\n", field, sid_string_static(&sid));
496 dump a string result from ldap
498 static void dump_string(const char *field, struct berval **values)
501 for (i=0; values[i]; i++) {
502 printf("%s: %s\n", field, values[i]->bv_val);
507 dump a record from LDAP on stdout
510 void ads_dump(ADS_STRUCT *ads, void *res)
517 void (*handler)(const char *, struct berval **);
519 {"objectGUID", dump_binary},
520 {"nTSecurityDescriptor", dump_binary},
521 {"objectSid", dump_sid},
525 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
526 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b);
528 field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
529 struct berval **values;
532 values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
534 for (i=0; handlers[i].name; i++) {
535 if (StrCaseCmp(handlers[i].name, field) == 0) {
536 handlers[i].handler(field, values);
540 if (!handlers[i].name) {
541 dump_string(field, values);
543 ldap_value_free_len(values);
553 count how many replies are in a LDAPMessage
555 int ads_count_replies(ADS_STRUCT *ads, void *res)
557 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
561 join a machine to a realm, creating the machine account
562 and setting the machine password
564 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
570 /* hostname must be lowercase */
571 host = strdup(hostname);
574 status = ads_find_machine_acct(ads, (void **)&res, host);
575 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
576 DEBUG(0, ("Host account for %s already exists - deleting for readd\n", host));
577 status = ads_leave_realm(ads, host);
578 if (!ADS_ERR_OK(status)) {
579 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
585 status = ads_add_machine_acct(ads, host, org_unit);
586 if (!ADS_ERR_OK(status)) {
587 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
591 status = ads_find_machine_acct(ads, (void **)&res, host);
592 if (!ADS_ERR_OK(status)) {
593 DEBUG(0, ("Host account test failed\n"));
603 delete a machine from the realm
605 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
609 char *hostnameDN, *host;
612 /* hostname must be lowercase */
613 host = strdup(hostname);
616 status = ads_find_machine_acct(ads, &res, host);
617 if (!ADS_ERR_OK(status)) {
618 DEBUG(0, ("Host account for %s does not exist.\n", host));
622 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
623 rc = ldap_delete_s(ads->ld, hostnameDN);
624 ads_memfree(ads, hostnameDN);
625 if (rc != LDAP_SUCCESS) {
626 return ADS_ERROR(rc);
629 status = ads_find_machine_acct(ads, &res, host);
630 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
631 DEBUG(0, ("Failed to remove host account.\n"));
641 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
642 const char *hostname,
643 const char *password)
646 char *host = strdup(hostname);
651 asprintf(&principal, "%s@%s", host, ads->realm);
653 status = krb5_set_password(ads->kdc_server, principal, password);
662 pull the first entry from a ADS result
664 void *ads_first_entry(ADS_STRUCT *ads, void *res)
666 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
670 pull the next entry from a ADS result
672 void *ads_next_entry(ADS_STRUCT *ads, void *res)
674 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
678 pull a single string from a ADS result
680 char *ads_pull_string(ADS_STRUCT *ads,
681 TALLOC_CTX *mem_ctx, void *msg, const char *field)
686 values = ldap_get_values(ads->ld, msg, field);
687 if (!values) return NULL;
690 ret = talloc_strdup(mem_ctx, values[0]);
692 ldap_value_free(values);
697 pull a single uint32 from a ADS result
699 BOOL ads_pull_uint32(ADS_STRUCT *ads,
700 void *msg, const char *field, uint32 *v)
704 values = ldap_get_values(ads->ld, msg, field);
705 if (!values) return False;
707 ldap_value_free(values);
711 *v = atoi(values[0]);
712 ldap_value_free(values);
717 pull a single DOM_SID from a ADS result
719 BOOL ads_pull_sid(ADS_STRUCT *ads,
720 void *msg, const char *field, DOM_SID *sid)
722 struct berval **values;
725 values = ldap_get_values_len(ads->ld, msg, field);
727 if (!values) return False;
730 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
733 ldap_value_free_len(values);
738 pull an array of DOM_SIDs from a ADS result
739 return the count of SIDs pulled
741 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
742 void *msg, const char *field, DOM_SID **sids)
744 struct berval **values;
748 values = ldap_get_values_len(ads->ld, msg, field);
750 if (!values) return 0;
752 for (i=0; values[i]; i++) /* nop */ ;
754 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
757 for (i=0; values[i]; i++) {
758 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
762 ldap_value_free_len(values);
767 /* find the update serial number - this is the core of the ldap cache */
768 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
770 const char *attrs[] = {"highestCommittedUSN", NULL};
774 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
775 if (!ADS_ERR_OK(status)) return status;
777 if (ads_count_replies(ads, res) != 1) {
778 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
781 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
782 ads_msgfree(ads, res);
787 /* find the servers name and realm - this can be done before authentication
788 The ldapServiceName field on w2k looks like this:
789 vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
791 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
793 const char *attrs[] = {"ldapServiceName", NULL};
799 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
800 if (!ADS_ERR_OK(status)) return status;
802 values = ldap_get_values(ads->ld, res, "ldapServiceName");
803 if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
805 p = strchr(values[0], ':');
807 ldap_value_free(values);
809 return ADS_ERROR(LDAP_DECODING_ERROR);
812 SAFE_FREE(ads->ldap_server_name);
814 ads->ldap_server_name = strdup(p+1);
815 p = strchr(ads->ldap_server_name, '$');
816 if (!p || p[1] != '@') {
817 ldap_value_free(values);
819 SAFE_FREE(ads->ldap_server_name);
820 return ADS_ERROR(LDAP_DECODING_ERROR);
825 SAFE_FREE(ads->server_realm);
826 SAFE_FREE(ads->bind_path);
828 ads->server_realm = strdup(p+2);
829 ads->bind_path = ads_build_dn(ads->server_realm);
831 /* in case the realm isn't configured in smb.conf */
832 if (!ads->realm || !ads->realm[0]) {
833 SAFE_FREE(ads->realm);
834 ads->realm = strdup(ads->server_realm);
837 DEBUG(3,("got ldap server name %s@%s\n",
838 ads->ldap_server_name, ads->realm));
845 find the list of trusted domains
847 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
848 int *num_trusts, char ***names, DOM_SID **sids)
850 const char *attrs[] = {"flatName", "securityIdentifier", NULL};
857 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
858 if (!ADS_ERR_OK(status)) return status;
860 count = ads_count_replies(ads, res);
862 ads_msgfree(ads, res);
863 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
866 (*names) = talloc(mem_ctx, sizeof(char *) * count);
867 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
868 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
870 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
871 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
872 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
877 ads_msgfree(ads, res);
884 /* find the domain sid for our domain */
885 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
887 const char *attrs[] = {"objectSid", NULL};
891 rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
893 if (!ADS_ERR_OK(rc)) return rc;
894 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
895 return ADS_ERROR_SYSTEM(ENOENT);
897 ads_msgfree(ads, res);