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_ADD, 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] = {
366 /* find the end of the list, marked by NULL or -1 */
367 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
368 /* make sure the end of the list is NULL */
370 ret = ldap_modify_ext_s(ads->ld, mod_dn, (LDAPMod **) mods,
372 return ADS_ERROR(ret);
375 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
379 /* find the end of the list, marked by NULL or -1 */
380 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
381 /* make sure the end of the list is NULL */
384 return ADS_ERROR(ldap_add_s(ads->ld, new_dn, mods));
387 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
389 return ADS_ERROR(ldap_delete(ads->ld, del_dn));
393 build an org unit string
394 if org unit is Computers or blank then assume a container, otherwise
395 assume a \ separated list of organisational units
398 char *ads_ou_string(const char *org_unit)
400 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
401 return strdup("cn=Computers");
404 return ads_build_path(org_unit, "\\/", "ou=", 1);
410 add a machine account to the ADS server
412 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
413 const char *org_unit)
416 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
421 if (!(ctx = talloc_init_named("machine_account")))
422 return ADS_ERROR(LDAP_NO_MEMORY);
424 ret = ADS_ERROR(LDAP_NO_MEMORY);
426 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
428 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->realm)))
430 ou_str = ads_ou_string(org_unit);
431 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
437 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
439 if (!(controlstr = talloc_asprintf(ctx, "%u",
440 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
441 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
444 if (!(mods = ads_init_mods(ctx)))
447 ads_mod_add(ctx, &mods, "cn", hostname);
448 ads_mod_add(ctx, &mods, "sAMAccountName", samAccountName);
449 ads_mod_add_var(ctx, &mods, LDAP_MOD_ADD, "objectClass",
450 "top", "person", "organizationalPerson",
451 "user", "computer", NULL);
452 ads_mod_add(ctx, &mods, "userPrincipalName", host_upn);
453 ads_mod_add(ctx, &mods, "servicePrincipalName", host_spn);
454 ads_mod_add(ctx, &mods, "dNSHostName", hostname);
455 ads_mod_add(ctx, &mods, "userAccountControl", controlstr);
456 ads_mod_add(ctx, &mods, "operatingSystem", "Samba");
457 ads_mod_add(ctx, &mods, "operatingSystemVersion", VERSION);
459 ret = ads_gen_add(ads, new_dn, mods);
467 dump a binary result from ldap
469 static void dump_binary(const char *field, struct berval **values)
472 for (i=0; values[i]; i++) {
473 printf("%s: ", field);
474 for (j=0; j<values[i]->bv_len; j++) {
475 printf("%02X", (unsigned char)values[i]->bv_val[j]);
482 dump a sid result from ldap
484 static void dump_sid(const char *field, struct berval **values)
487 for (i=0; values[i]; i++) {
489 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
490 printf("%s: %s\n", field, sid_string_static(&sid));
495 dump a string result from ldap
497 static void dump_string(const char *field, struct berval **values)
500 for (i=0; values[i]; i++) {
501 printf("%s: %s\n", field, values[i]->bv_val);
506 dump a record from LDAP on stdout
509 void ads_dump(ADS_STRUCT *ads, void *res)
516 void (*handler)(const char *, struct berval **);
518 {"objectGUID", dump_binary},
519 {"nTSecurityDescriptor", dump_binary},
520 {"objectSid", dump_sid},
524 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
525 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b);
527 field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
528 struct berval **values;
531 values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
533 for (i=0; handlers[i].name; i++) {
534 if (StrCaseCmp(handlers[i].name, field) == 0) {
535 handlers[i].handler(field, values);
539 if (!handlers[i].name) {
540 dump_string(field, values);
542 ldap_value_free_len(values);
552 count how many replies are in a LDAPMessage
554 int ads_count_replies(ADS_STRUCT *ads, void *res)
556 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
560 join a machine to a realm, creating the machine account
561 and setting the machine password
563 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
569 /* hostname must be lowercase */
570 host = strdup(hostname);
573 status = ads_find_machine_acct(ads, (void **)&res, host);
574 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
575 DEBUG(0, ("Host account for %s already exists - deleting for readd\n", host));
576 status = ads_leave_realm(ads, host);
577 if (!ADS_ERR_OK(status)) {
578 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
584 status = ads_add_machine_acct(ads, host, org_unit);
585 if (!ADS_ERR_OK(status)) {
586 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
590 status = ads_find_machine_acct(ads, (void **)&res, host);
591 if (!ADS_ERR_OK(status)) {
592 DEBUG(0, ("Host account test failed\n"));
602 delete a machine from the realm
604 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
608 char *hostnameDN, *host;
611 /* hostname must be lowercase */
612 host = strdup(hostname);
615 status = ads_find_machine_acct(ads, &res, host);
616 if (!ADS_ERR_OK(status)) {
617 DEBUG(0, ("Host account for %s does not exist.\n", host));
621 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
622 rc = ldap_delete_s(ads->ld, hostnameDN);
623 ads_memfree(ads, hostnameDN);
624 if (rc != LDAP_SUCCESS) {
625 return ADS_ERROR(rc);
628 status = ads_find_machine_acct(ads, &res, host);
629 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
630 DEBUG(0, ("Failed to remove host account.\n"));
640 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
641 const char *hostname,
642 const char *password)
645 char *host = strdup(hostname);
650 asprintf(&principal, "%s@%s", host, ads->realm);
652 status = krb5_set_password(ads->kdc_server, principal, password);
661 pull the first entry from a ADS result
663 void *ads_first_entry(ADS_STRUCT *ads, void *res)
665 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
669 pull the next entry from a ADS result
671 void *ads_next_entry(ADS_STRUCT *ads, void *res)
673 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
677 pull a single string from a ADS result
679 char *ads_pull_string(ADS_STRUCT *ads,
680 TALLOC_CTX *mem_ctx, void *msg, const char *field)
685 values = ldap_get_values(ads->ld, msg, field);
686 if (!values) return NULL;
689 ret = talloc_strdup(mem_ctx, values[0]);
691 ldap_value_free(values);
696 pull a single uint32 from a ADS result
698 BOOL ads_pull_uint32(ADS_STRUCT *ads,
699 void *msg, const char *field, uint32 *v)
703 values = ldap_get_values(ads->ld, msg, field);
704 if (!values) return False;
706 ldap_value_free(values);
710 *v = atoi(values[0]);
711 ldap_value_free(values);
716 pull a single DOM_SID from a ADS result
718 BOOL ads_pull_sid(ADS_STRUCT *ads,
719 void *msg, const char *field, DOM_SID *sid)
721 struct berval **values;
724 values = ldap_get_values_len(ads->ld, msg, field);
726 if (!values) return False;
729 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
732 ldap_value_free_len(values);
737 pull an array of DOM_SIDs from a ADS result
738 return the count of SIDs pulled
740 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
741 void *msg, const char *field, DOM_SID **sids)
743 struct berval **values;
747 values = ldap_get_values_len(ads->ld, msg, field);
749 if (!values) return 0;
751 for (i=0; values[i]; i++) /* nop */ ;
753 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
756 for (i=0; values[i]; i++) {
757 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
761 ldap_value_free_len(values);
766 /* find the update serial number - this is the core of the ldap cache */
767 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
769 const char *attrs[] = {"highestCommittedUSN", NULL};
773 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
774 if (!ADS_ERR_OK(status)) return status;
776 if (ads_count_replies(ads, res) != 1) {
777 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
780 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
781 ads_msgfree(ads, res);
786 /* find the servers name and realm - this can be done before authentication
787 The ldapServiceName field on w2k looks like this:
788 vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
790 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
792 const char *attrs[] = {"ldapServiceName", NULL};
798 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
799 if (!ADS_ERR_OK(status)) return status;
801 values = ldap_get_values(ads->ld, res, "ldapServiceName");
802 if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
804 p = strchr(values[0], ':');
806 ldap_value_free(values);
808 return ADS_ERROR(LDAP_DECODING_ERROR);
811 SAFE_FREE(ads->ldap_server_name);
813 ads->ldap_server_name = strdup(p+1);
814 p = strchr(ads->ldap_server_name, '$');
815 if (!p || p[1] != '@') {
816 ldap_value_free(values);
818 SAFE_FREE(ads->ldap_server_name);
819 return ADS_ERROR(LDAP_DECODING_ERROR);
824 SAFE_FREE(ads->server_realm);
825 SAFE_FREE(ads->bind_path);
827 ads->server_realm = strdup(p+2);
828 ads->bind_path = ads_build_dn(ads->server_realm);
830 /* in case the realm isn't configured in smb.conf */
831 if (!ads->realm || !ads->realm[0]) {
832 SAFE_FREE(ads->realm);
833 ads->realm = strdup(ads->server_realm);
836 DEBUG(3,("got ldap server name %s@%s\n",
837 ads->ldap_server_name, ads->realm));
844 find the list of trusted domains
846 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
847 int *num_trusts, char ***names, DOM_SID **sids)
849 const char *attrs[] = {"flatName", "securityIdentifier", NULL};
856 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
857 if (!ADS_ERR_OK(status)) return status;
859 count = ads_count_replies(ads, res);
861 ads_msgfree(ads, res);
862 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
865 (*names) = talloc(mem_ctx, sizeof(char *) * count);
866 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
867 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
869 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
870 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
871 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
876 ads_msgfree(ads, res);
883 /* find the domain sid for our domain */
884 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
886 const char *attrs[] = {"objectSid", NULL};
890 rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
892 if (!ADS_ERR_OK(rc)) return rc;
893 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
894 return ADS_ERROR_SYSTEM(ENOENT);
896 ads_msgfree(ads, res);