2 Unix SMB/Netbios implementation.
4 ads (active directory) utility library
5 Copyright (C) Andrew Tridgell 2001
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 /* return a dn of the form "dc=AA,dc=BB,dc=CC" from a
27 realm of the form AA.BB.CC
31 return a string for an error from a ads routine
33 char *ads_errstr(int rc)
35 return ldap_err2string(rc);
39 this is a minimal interact function, just enough for SASL to talk
40 GSSAPI/kerberos to W2K
41 Error handling is a bit of a problem. I can't see how to get Cyrus-sasl
42 to give sensible errors
44 static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in)
46 sasl_interact_t *interact = in;
48 while (interact->id != SASL_CB_LIST_END) {
49 interact->result = strdup("");
50 interact->len = strlen(interact->result);
58 connect to the LDAP server
60 int ads_connect(ADS_STRUCT *ads)
62 int version = LDAP_VERSION3;
65 ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
69 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
71 rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL,
80 do a general ADS search
82 int ads_search(ADS_STRUCT *ads, void **res,
87 return ldap_search_s(ads->ld, ads->bind_path,
88 LDAP_SCOPE_SUBTREE, exp, (char **)attrs, 0, (LDAPMessage **)res);
92 do a search on a specific DistinguishedName
94 int ads_search_dn(ADS_STRUCT *ads, void **res,
99 return ldap_search_s(ads->ld, dn,
100 LDAP_SCOPE_BASE, "(objectclass=*)", (char **)attrs, 0, (LDAPMessage **)res);
105 find a machine account given a hostname
107 int ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
112 /* the easiest way to find a machine account anywhere in the tree
113 is to look for hostname$ */
114 asprintf(&exp, "(samAccountName=%s$)", host);
115 ret = ads_search(ads, res, exp, NULL);
122 a convenient routine for adding a generic LDAP record
124 int ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ...)
131 #define MAX_MOD_VALUES 10
133 /* count the number of attributes */
134 va_start(ap, new_dn);
135 for (i=0; va_arg(ap, char *); i++) {
136 /* skip the values */
137 while (va_arg(ap, char *)) ;
141 mods = malloc(sizeof(LDAPMod *) * (i+1));
143 va_start(ap, new_dn);
144 for (i=0; (name=va_arg(ap, char *)); i++) {
147 values = (char **)malloc(sizeof(char *) * (MAX_MOD_VALUES+1));
148 for (j=0; (value=va_arg(ap, char *)) && j < MAX_MOD_VALUES; j++) {
152 mods[i] = malloc(sizeof(LDAPMod));
153 mods[i]->mod_type = name;
154 mods[i]->mod_op = LDAP_MOD_ADD;
155 mods[i]->mod_values = values;
160 ret = ldap_add_s(ads->ld, new_dn, mods);
162 for (i=0; mods[i]; i++) {
163 free(mods[i]->mod_values);
172 add a machine account to the ADS server
174 static int ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname)
177 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
179 asprintf(&host_spn, "HOST/%s", hostname);
180 asprintf(&host_upn, "%s@%s", host_spn, ads->realm);
181 asprintf(&new_dn, "cn=%s,cn=Computers,%s", hostname, ads->bind_path);
182 asprintf(&samAccountName, "%s$", hostname);
183 asprintf(&controlstr, "%u",
184 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
185 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY);
187 ret = ads_gen_add(ads, new_dn,
188 "cn", hostname, NULL,
189 "sAMAccountName", samAccountName, NULL,
191 "top", "person", "organizationalPerson",
192 "user", "computer", NULL,
193 "userPrincipalName", host_upn, NULL,
194 "servicePrincipalName", host_spn, NULL,
195 "dNSHostName", hostname, NULL,
196 "userAccountControl", controlstr, NULL,
197 "operatingSystem", "Samba", NULL,
198 "operatingSystemVersion", VERSION, NULL,
204 free(samAccountName);
211 dump a binary result from ldap
213 static void dump_binary(const char *field, struct berval **values)
216 for (i=0; values[i]; i++) {
217 printf("%s: ", field);
218 for (j=0; j<values[i]->bv_len; j++) {
219 printf("%02X", (unsigned char)values[i]->bv_val[j]);
226 dump a string result from ldap
228 static void dump_string(const char *field, struct berval **values)
231 for (i=0; values[i]; i++) {
232 printf("%s: %s\n", field, values[i]->bv_val);
237 dump a record from LDAP on stdout
240 void ads_dump(ADS_STRUCT *ads, void *res)
247 void (*handler)(const char *, struct berval **);
249 {"objectGUID", dump_binary},
250 {"objectSid", dump_binary},
254 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
255 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b);
257 field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
258 struct berval **values;
261 values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
263 for (i=0; handlers[i].name; i++) {
264 if (StrCaseCmp(handlers[i].name, field) == 0) {
265 handlers[i].handler(field, values);
269 if (!handlers[i].name) {
270 dump_string(field, values);
272 ldap_value_free_len(values);
282 count how many replies are in a LDAPMessage
284 int ads_count_replies(ADS_STRUCT *ads, void *res)
286 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
290 join a machine to a realm, creating the machine account
291 and setting the machine password
293 int ads_join_realm(ADS_STRUCT *ads, const char *hostname)
299 /* hostname must be lowercase */
300 host = strdup(hostname);
303 rc = ads_find_machine_acct(ads, (void **)&res, host);
304 if (rc == LDAP_SUCCESS && ads_count_replies(ads, res) == 1) {
305 DEBUG(0, ("Host account for %s already exists\n", host));
309 rc = ads_add_machine_acct(ads, host);
310 if (rc != LDAP_SUCCESS) {
311 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(rc)));
315 rc = ads_find_machine_acct(ads, (void **)&res, host);
316 if (rc != LDAP_SUCCESS || ads_count_replies(ads, res) != 1) {
317 DEBUG(0, ("Host account test failed\n"));
318 /* hmmm, we need NTSTATUS */
328 delete a machine from the realm
330 int ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
334 char *hostnameDN, *host;
336 /* hostname must be lowercase */
337 host = strdup(hostname);
340 rc = ads_find_machine_acct(ads, &res, host);
341 if (rc != LDAP_SUCCESS || ads_count_replies(ads, res) != 1) {
342 DEBUG(0, ("Host account for %s does not exist.\n", host));
346 hostnameDN = ldap_get_dn(ads->ld, (LDAPMessage *)res);
347 rc = ldap_delete_s(ads->ld, hostnameDN);
348 ldap_memfree(hostnameDN);
349 if (rc != LDAP_SUCCESS) {
350 DEBUG(0, ("ldap_delete_s: %s\n", ads_errstr(rc)));
354 rc = ads_find_machine_acct(ads, &res, host);
355 if (rc == LDAP_SUCCESS && ads_count_replies(ads, res) == 1 ) {
356 DEBUG(0, ("Failed to remove host account.\n"));
357 /*hmmm, we need NTSTATUS */
367 NTSTATUS ads_set_machine_password(ADS_STRUCT *ads,
368 const char *hostname,
369 const char *password)
372 char *host = strdup(hostname);
374 ret = krb5_set_password(ads->kdc_server, host, ads->realm, password);
381 return a RFC2254 binary string representation of a buffer
385 char *ads_binary_string(char *buf, int len)
389 const char *hex = "0123456789ABCDEF";
390 s = malloc(len * 3 + 1);
392 for (j=i=0;i<len;i++) {
394 s[j+1] = hex[((unsigned char)buf[i]) >> 4];
395 s[j+2] = hex[((unsigned char)buf[i]) & 0xF];
403 return the binary string representation of a DOM_SID
406 char *ads_sid_binstring(DOM_SID *sid)
409 int len = sid_size(sid);
411 if (!buf) return NULL;
412 sid_linearize(buf, len, sid);
413 s = ads_binary_string(buf, len);
419 pull the first entry from a ADS result
421 void *ads_first_entry(ADS_STRUCT *ads, void *res)
423 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
427 pull the next entry from a ADS result
429 void *ads_next_entry(ADS_STRUCT *ads, void *res)
431 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
435 pull a single string from a ADS result
437 char *ads_pull_string(ADS_STRUCT *ads,
438 TALLOC_CTX *mem_ctx, void *msg, const char *field)
443 values = ldap_get_values(ads->ld, msg, field);
445 if (!values || !values[0]) return NULL;
447 ret = talloc_strdup(mem_ctx, values[0]);
448 ldap_value_free(values);
453 pull a single uint32 from a ADS result
455 BOOL ads_pull_uint32(ADS_STRUCT *ads,
456 void *msg, const char *field, uint32 *v)
460 values = ldap_get_values(ads->ld, msg, field);
462 if (!values || !values[0]) return False;
464 *v = atoi(values[0]);
465 ldap_value_free(values);
470 pull a single DOM_SID from a ADS result
472 BOOL ads_pull_sid(ADS_STRUCT *ads,
473 void *msg, const char *field, DOM_SID *sid)
475 struct berval **values;
478 values = ldap_get_values_len(ads->ld, msg, field);
480 if (!values || !values[0]) return False;
482 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
484 ldap_value_free_len(values);
489 pull an array of DOM_SIDs from a ADS result
490 return the count of SIDs pulled
492 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
493 void *msg, const char *field, DOM_SID **sids)
495 struct berval **values;
499 values = ldap_get_values_len(ads->ld, msg, field);
501 if (!values) return 0;
503 for (i=0; values[i]; i++) /* nop */ ;
505 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
508 for (i=0; values[i]; i++) {
509 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
513 ldap_value_free_len(values);
518 /* find the update serial number - this is the core of the ldap cache */
519 BOOL ads_USN(ADS_STRUCT *ads, uint32 *usn)
521 const char *attrs[] = {"highestCommittedUSN", NULL};
525 rc = ldap_search_s(ads->ld, ads->bind_path,
526 LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0, (LDAPMessage **)&res);
527 if (rc || ads_count_replies(ads, res) != 1) return False;
528 return ads_pull_uint32(ads, res, "highestCommittedUSN", usn);