diff options
Diffstat (limited to 'source3/libads/ldap.c')
-rw-r--r-- | source3/libads/ldap.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c new file mode 100644 index 0000000000..2853dbbaa3 --- /dev/null +++ b/source3/libads/ldap.c @@ -0,0 +1,324 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + ads (active directory) utility library + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#ifdef HAVE_ADS + +/* return a dn of the form "dc=AA,dc=BB,dc=CC" from a + realm of the form AA.BB.CC + caller must free +*/ +static char *ads_build_dn(const char *realm) +{ + char *p, *r; + int numdots = 0; + char *ret; + int len; + + r = strdup(realm); + + if (!r || !*r) return r; + + for (p=r; *p; p++) { + if (*p == '.') numdots++; + } + + len = (numdots+1)*4 + strlen(r) + 1; + +ret = malloc(len); + strlcpy(ret,"dc=", len); + p=strtok(r,"."); + strlcat(ret, p, len); + + while ((p=strtok(NULL,"."))) { + strlcat(ret,",dc=", len); + strlcat(ret, p, len); + } + + free(r); + + return ret; +} + +/* + return a string for an error from a ads routine +*/ +char *ads_errstr(int rc) +{ + return ldap_err2string(rc); +} + +/* + initialise a ADS_STRUCT, ready for some ads_ ops +*/ +ADS_STRUCT *ads_init(const char *realm, + const char *ldap_server, + const char *bind_path) +{ + ADS_STRUCT *ads; + + ads = (ADS_STRUCT *)xmalloc(sizeof(*ads)); + memset(ads, 0, sizeof(*ads)); + + ads->realm = realm? strdup(realm) : NULL; + ads->ldap_server = ldap_server? strdup(ldap_server) : NULL; + ads->bind_path = bind_path? strdup(bind_path) : NULL; + ads->ldap_port = LDAP_PORT; + + if (!ads->bind_path) { + ads->bind_path = ads_build_dn(ads->realm); + } + + return ads; +} + + +/* + this is a minimal interact function, just enough for SASL to talk + GSSAPI/kerberos to W2K +*/ +static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in) +{ + sasl_interact_t *interact = in; + + while (interact->id != SASL_CB_LIST_END) { + interact->result = strdup(""); + interact->len = 0; + interact++; + } + + return LDAP_SUCCESS; +} + +/* + connect to the LDAP server +*/ +int ads_connect(ADS_STRUCT *ads) +{ + int version = LDAP_VERSION3; + int rc; + + ads->ld = ldap_open(ads->ldap_server, ads->ldap_port); + if (!ads->ld) { + return errno; + } + ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version); + + rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL, 0, + sasl_interact, NULL); + + return rc; +} + + +/* + find a machine account given a hostname +*/ +int ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host) +{ + int ret; + char *exp; + + /* the easiest way to find a machine account anywhere in the tree + is to look for hostname$ */ + asprintf(&exp, "(samAccountName=%s$)", host); + *res = NULL; + ret = ldap_search_s(ads->ld, ads->bind_path, + LDAP_SCOPE_SUBTREE, exp, NULL, 0, (LDAPMessage **)res); + free(exp); + return ret; +} + + +/* + a convenient routine for adding a generic LDAP record +*/ +int ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ...) +{ + int i; + va_list ap; + LDAPMod **mods; + char *name, *value; + int ret; +#define MAX_MOD_VALUES 10 + + /* count the number of attributes */ + va_start(ap, new_dn); + for (i=0; va_arg(ap, char *); i++) { + /* skip the values */ + while (va_arg(ap, char *)) ; + } + va_end(ap); + + mods = malloc(sizeof(LDAPMod *) * (i+1)); + + va_start(ap, new_dn); + for (i=0; (name=va_arg(ap, char *)); i++) { + char **values; + int j; + values = (char **)malloc(sizeof(char *) * (MAX_MOD_VALUES+1)); + for (j=0; (value=va_arg(ap, char *)) && j < MAX_MOD_VALUES; j++) { + values[j] = value; + } + values[j] = NULL; + mods[i] = malloc(sizeof(LDAPMod)); + mods[i]->mod_type = name; + mods[i]->mod_op = LDAP_MOD_ADD; + mods[i]->mod_values = values; + } + mods[i] = NULL; + va_end(ap); + + ret = ldap_add_s(ads->ld, new_dn, mods); + + for (i=0; mods[i]; i++) { + free(mods[i]->mod_values); + free(mods[i]); + } + free(mods); + + return ret; +} + +/* + add a machine account to the ADS server +*/ +static int ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname) +{ + int ret; + char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr; + + asprintf(&host_spn, "HOST/%s", hostname); + asprintf(&host_upn, "%s@%s", host_spn, ads->realm); + asprintf(&new_dn, "cn=%s,cn=Computers,%s", hostname, ads->bind_path); + asprintf(&samAccountName, "%s$", hostname); + asprintf(&controlstr, "%u", + UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT | + UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY); + + ret = ads_gen_add(ads, new_dn, + "cn", hostname, NULL, + "sAMAccountName", samAccountName, NULL, + "objectClass", + "top", "person", "organizationalPerson", + "user", "computer", NULL, + "userPrincipalName", host_upn, NULL, + "servicePrincipalName", host_spn, NULL, + "dNSHostName", hostname, NULL, + "userAccountControl", controlstr, NULL, + "operatingSystem", "Samba", NULL, + "operatingSystemVersion", VERSION, NULL, + NULL); + + free(host_spn); + free(host_upn); + free(new_dn); + free(samAccountName); + free(controlstr); + + return ret; +} + +/* + dump a record from LDAP on stdout + used for debugging +*/ +void ads_dump(ADS_STRUCT *ads, void *res) +{ + char *field; + LDAPMessage *msg; + BerElement *b; + char *this_dn; + + for (msg = ldap_first_entry(ads->ld, (LDAPMessage *)res); + msg; msg = ldap_next_entry(ads->ld, msg)) { + this_dn = ldap_get_dn(ads->ld, (LDAPMessage *)res); + if (this_dn) { + printf("Dumping: %s\n", this_dn); + } + ldap_memfree(this_dn); + + for (field = ldap_first_attribute(ads->ld, msg, &b); + field; + field = ldap_next_attribute(ads->ld, msg, b)) { + char **values, **p; + values = ldap_get_values(ads->ld, msg, field); + for (p = values; *p; p++) { + printf("%s: %s\n", field, *p); + } + ldap_value_free(values); + ldap_memfree(field); + } + + ber_free(b, 1); + printf("\n"); + } +} + +/* + count how many replies are in a LDAPMessage +*/ +int ads_count_replies(ADS_STRUCT *ads, void *res) +{ + return ldap_count_entries(ads->ld, (LDAPMessage *)res); +} + +/* + join a machine to a realm, creating the machine account + and setting the machine password +*/ +int ads_join_realm(ADS_STRUCT *ads, const char *hostname) +{ + int rc; + LDAPMessage *res; + char *principal; + + rc = ads_find_machine_acct(ads, (void **)&res, hostname); + if (rc == LDAP_SUCCESS && ads_count_replies(ads, res) == 1) { + DEBUG(0, ("Host account for %s already exists\n", hostname)); + goto set_password; + } + + rc = ads_add_machine_acct(ads, hostname); + if (rc != LDAP_SUCCESS) { + DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(rc))); + return rc; + } + + rc = ads_find_machine_acct(ads, (void **)&res, hostname); + if (rc != LDAP_SUCCESS || ads_count_replies(ads, res) != 1) { + DEBUG(0, ("Host account test failed\n")); + /* hmmm, we need NTSTATUS */ + return -1; + } + +set_password: + asprintf(&principal, "HOST/%s@%s", hostname, ads->realm); +#if 0 + krb5_set_principal_password(principal, ads->ldap_server, hostname, ads->realm); +#endif + free(principal); + + return LDAP_SUCCESS; +} + +#endif |