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
30 static char *ads_build_dn(const char *realm)
39 if (!r || !*r) return r;
42 if (*p == '.') numdots++;
45 len = (numdots+1)*4 + strlen(r) + 1;
48 strlcpy(ret,"dc=", len);
52 while ((p=strtok(NULL,"."))) {
53 strlcat(ret,",dc=", len);
63 return a string for an error from a ads routine
65 char *ads_errstr(int rc)
67 return ldap_err2string(rc);
71 find the ldap server from DNS
72 this won't work till we add a DNS packet parser. Talk about a
73 lousy resolv interface!
75 static char *find_ldap_server(ADS_STRUCT *ads)
79 if (ldap_domain2hostlist(ads->realm, &list) == LDAP_SUCCESS) {
81 p = strchr(list, ':');
90 initialise a ADS_STRUCT, ready for some ads_ ops
92 ADS_STRUCT *ads_init(const char *realm,
93 const char *ldap_server,
94 const char *bind_path)
98 ads = (ADS_STRUCT *)malloc(sizeof(*ads));
99 if (!ads) return NULL;
100 memset(ads, 0, sizeof(*ads));
102 ads->realm = realm? strdup(realm) : NULL;
103 ads->ldap_server = ldap_server? strdup(ldap_server) : NULL;
104 ads->bind_path = bind_path? strdup(bind_path) : NULL;
105 ads->ldap_port = LDAP_PORT;
108 ads->realm = lp_realm();
110 if (!ads->bind_path) {
111 ads->bind_path = ads_build_dn(ads->realm);
113 if (!ads->ldap_server) {
114 ads->ldap_server = find_ldap_server(ads);
116 if (!ads->kdc_server) {
117 /* assume its the same as LDAP */
118 ads->kdc_server = ads->ldap_server? strdup(ads->ldap_server) : NULL;
125 free the memory used by the ADS structure initialized with 'ads_init(...)'
127 void ads_destroy(ADS_STRUCT *ads)
129 if (ads->ld) ldap_unbind(ads->ld);
130 SAFE_FREE(ads->realm);
131 SAFE_FREE(ads->ldap_server);
132 SAFE_FREE(ads->kdc_server);
133 SAFE_FREE(ads->bind_path);
139 this is a minimal interact function, just enough for SASL to talk
140 GSSAPI/kerberos to W2K
141 Error handling is a bit of a problem. I can't see how to get Cyrus-sasl
142 to give sensible errors
144 static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in)
146 sasl_interact_t *interact = in;
148 while (interact->id != SASL_CB_LIST_END) {
149 interact->result = strdup("");
150 interact->len = strlen(interact->result);
158 connect to the LDAP server
160 int ads_connect(ADS_STRUCT *ads)
162 int version = LDAP_VERSION3;
165 ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
169 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
171 rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL,
173 sasl_interact, NULL);
180 find a machine account given a hostname
182 int ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
187 /* the easiest way to find a machine account anywhere in the tree
188 is to look for hostname$ */
189 asprintf(&exp, "(samAccountName=%s$)", host);
191 ret = ldap_search_s(ads->ld, ads->bind_path,
192 LDAP_SCOPE_SUBTREE, exp, NULL, 0, (LDAPMessage **)res);
199 a convenient routine for adding a generic LDAP record
201 int ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ...)
208 #define MAX_MOD_VALUES 10
210 /* count the number of attributes */
211 va_start(ap, new_dn);
212 for (i=0; va_arg(ap, char *); i++) {
213 /* skip the values */
214 while (va_arg(ap, char *)) ;
218 mods = malloc(sizeof(LDAPMod *) * (i+1));
220 va_start(ap, new_dn);
221 for (i=0; (name=va_arg(ap, char *)); i++) {
224 values = (char **)malloc(sizeof(char *) * (MAX_MOD_VALUES+1));
225 for (j=0; (value=va_arg(ap, char *)) && j < MAX_MOD_VALUES; j++) {
229 mods[i] = malloc(sizeof(LDAPMod));
230 mods[i]->mod_type = name;
231 mods[i]->mod_op = LDAP_MOD_ADD;
232 mods[i]->mod_values = values;
237 ret = ldap_add_s(ads->ld, new_dn, mods);
239 for (i=0; mods[i]; i++) {
240 free(mods[i]->mod_values);
249 add a machine account to the ADS server
251 static int ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname)
254 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
256 asprintf(&host_spn, "HOST/%s", hostname);
257 asprintf(&host_upn, "%s@%s", host_spn, ads->realm);
258 asprintf(&new_dn, "cn=%s,cn=Computers,%s", hostname, ads->bind_path);
259 asprintf(&samAccountName, "%s$", hostname);
260 asprintf(&controlstr, "%u",
261 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
262 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY);
264 ret = ads_gen_add(ads, new_dn,
265 "cn", hostname, NULL,
266 "sAMAccountName", samAccountName, NULL,
268 "top", "person", "organizationalPerson",
269 "user", "computer", NULL,
270 "userPrincipalName", host_upn, NULL,
271 "servicePrincipalName", host_spn, NULL,
272 "dNSHostName", hostname, NULL,
273 "userAccountControl", controlstr, NULL,
274 "operatingSystem", "Samba", NULL,
275 "operatingSystemVersion", VERSION, NULL,
281 free(samAccountName);
288 dump a record from LDAP on stdout
291 void ads_dump(ADS_STRUCT *ads, void *res)
298 for (msg = ldap_first_entry(ads->ld, (LDAPMessage *)res);
299 msg; msg = ldap_next_entry(ads->ld, msg)) {
300 this_dn = ldap_get_dn(ads->ld, (LDAPMessage *)res);
302 printf("Dumping: %s\n", this_dn);
304 ldap_memfree(this_dn);
306 for (field = ldap_first_attribute(ads->ld, msg, &b);
308 field = ldap_next_attribute(ads->ld, msg, b)) {
310 values = ldap_get_values(ads->ld, msg, field);
311 for (p = values; *p; p++) {
312 printf("%s: %s\n", field, *p);
314 ldap_value_free(values);
324 count how many replies are in a LDAPMessage
326 int ads_count_replies(ADS_STRUCT *ads, void *res)
328 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
332 join a machine to a realm, creating the machine account
333 and setting the machine password
335 int ads_join_realm(ADS_STRUCT *ads, const char *hostname)
340 rc = ads_find_machine_acct(ads, (void **)&res, hostname);
341 if (rc == LDAP_SUCCESS && ads_count_replies(ads, res) == 1) {
342 DEBUG(0, ("Host account for %s already exists\n", hostname));
346 rc = ads_add_machine_acct(ads, hostname);
347 if (rc != LDAP_SUCCESS) {
348 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(rc)));
352 rc = ads_find_machine_acct(ads, (void **)&res, hostname);
353 if (rc != LDAP_SUCCESS || ads_count_replies(ads, res) != 1) {
354 DEBUG(0, ("Host account test failed\n"));
355 /* hmmm, we need NTSTATUS */
363 delete a machine from the realm
365 int ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
371 rc = ads_find_machine_acct(ads, &res, hostname);
372 if (rc != LDAP_SUCCESS || ads_count_replies(ads, res) != 1) {
373 DEBUG(0, ("Host account for %s does not exist.\n", hostname));
377 hostnameDN = ldap_get_dn(ads->ld, (LDAPMessage *)res);
378 rc = ldap_delete_s(ads->ld, hostnameDN);
379 ldap_memfree(hostnameDN);
380 if (rc != LDAP_SUCCESS) {
381 DEBUG(0, ("ldap_delete_s: %s\n", ads_errstr(rc)));
385 rc = ads_find_machine_acct(ads, &res, hostname);
386 if (rc == LDAP_SUCCESS && ads_count_replies(ads, res) == 1 ) {
387 DEBUG(0, ("Failed to remove host account.\n"));
388 /*hmmm, we need NTSTATUS */
396 NTSTATUS ads_set_machine_password(ADS_STRUCT *ads,
397 const char *hostname,
398 const char *password)
400 return krb5_set_password(ads->kdc_server, hostname, ads->realm, password);