From 21312c7da3d3287910af9ccae28e4e955548f1a9 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Jan 11 2012 17:04:12 +0000 Subject: ipa-kdb: initialize module functions Initialize module also on ipadb_create invocation. This is what kdb5_util expects. --- diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am index 3007624..309618a 100644 --- a/daemons/ipa-kdb/Makefile.am +++ b/daemons/ipa-kdb/Makefile.am @@ -8,6 +8,7 @@ INCLUDES = \ -DLIBDIR=\""$(libdir)"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ -DDATADIR=\""$(datadir)"\" \ + -DLDAPIDIR=\""$(localstatedir)/run"\" \ $(AM_CFLAGS) \ $(LDAP_CFLAGS) \ $(KRB5_CFLAGS) \ diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c index ba0bd2f..aa0c154 100644 --- a/daemons/ipa-kdb/ipa_kdb.c +++ b/daemons/ipa-kdb/ipa_kdb.c @@ -20,35 +20,369 @@ * along with this program. If not, see . */ -#include +#include "ipa_kdb.h" + +struct ipadb_context *ipadb_get_context(krb5_context kcontext) +{ + void *db_ctx; + krb5_error_code kerr; + + kerr = krb5_db_get_context(kcontext, &db_ctx); + if (kerr != 0) { + return NULL; + } + + return (struct ipadb_context *)db_ctx; +} + +static void ipadb_context_free(krb5_context kcontext, + struct ipadb_context **ctx) +{ + if (*ctx != NULL) { + free((*ctx)->uri); + /* ldap free lcontext */ + if ((*ctx)->lcontext) { + ldap_unbind_ext_s((*ctx)->lcontext, NULL, NULL); + } + krb5_free_default_realm(kcontext, (*ctx)->realm); + free(*ctx); + *ctx = NULL; + } +} + +#define LDAPI_URI_PREFIX "ldapi://" +#define LDAPI_PATH_PREFIX "%2fslapd-" +#define SOCKET_SUFFIX ".socket" +#define APPEND_PATH_PART(pos, part) \ + do { \ + int partlen = strlen(part); \ + strncpy(pos, part, partlen + 1); \ + p += partlen; \ + } while (0) + +static char *ipadb_realm_to_ldapi_uri(char *realm) +{ + char *uri = NULL; + char *p; + const char *q; + int len; + + /* uri length, assume worst case for LDAPIDIR */ + len = strlen(LDAPI_URI_PREFIX) + strlen(LDAPIDIR) * 3 + + strlen(LDAPI_PATH_PREFIX) + strlen(realm) + + strlen(SOCKET_SUFFIX) + 1; + + /* worst case they are all '/' to escape */ + uri = malloc(len); + if (!uri) { + return NULL; + } + p = uri; + + APPEND_PATH_PART(p, LDAPI_URI_PREFIX); + + /* copy path and escape '/' to '%2f' */ + for (q = LDAPIDIR; *q; q++) { + if (*q == '/') { + strncpy(p, "%2f", 3); + p += 3; + } else { + *p = *q; + p++; + } + } + + APPEND_PATH_PART(p, LDAPI_PATH_PREFIX); + + /* copy realm and convert '.' to '-' */ + for (q = realm; *q; q++) { + if (*q == '.') { + *p = '-'; + } else { + *p = *q; + } + p++; + } + + /* terminate string */ + APPEND_PATH_PART(p, SOCKET_SUFFIX); + + return uri; +} + +/* in IPA the base is always derived from the realm name */ +static char *ipadb_get_base_from_realm(krb5_context kcontext) +{ + krb5_error_code kerr; + char *realm = NULL; + char *base = NULL; + char *tmp; + size_t bi, ri; + size_t len; + + kerr = krb5_get_default_realm(kcontext, &realm); + if (kerr != 0) { + return NULL; + } + + bi = 3; + len = strlen(realm) + 3 + 1; + + base = malloc(len); + if (!base) { + goto done; + } + strcpy(base, "dc="); + + /* convert EXAMPLE.COM in dc=example,dc=com */ + for (ri = 0; realm[ri]; ri++) { + if (realm[ri] == '.') { + len += 4; + tmp = realloc(base, len); + if (!tmp) { + free(base); + base = NULL; + goto done; + } + base = tmp; + strcpy(&base[bi], ",dc="); + bi += 4; + } else { + base[bi] = tolower(realm[ri]); + bi++; + } + } + base[bi] = '\0'; + +done: + krb5_free_default_realm(kcontext, realm); + return base; +} + +int ipadb_get_connection(struct ipadb_context *ipactx) +{ + struct berval **vals = NULL; + struct timeval tv = { 5, 0 }; + LDAPMessage *res = NULL; + LDAPMessage *first; + krb5_key_salt_tuple *kst; + int n_kst; + int ret; + int v3; + int i; + char **cvals = NULL; + int c = 0; + + if (!ipactx->uri) { + return EINVAL; + } + + /* free existing conneciton if any */ + if (ipactx->lcontext) { + ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL); + ipactx->lcontext = NULL; + } + + ret = ldap_initialize(&ipactx->lcontext, ipactx->uri); + if (ret != LDAP_SUCCESS) { + goto done; + } + + /* make sure we talk LDAPv3 */ + v3 = LDAP_VERSION3; + ret = ldap_set_option(ipactx->lcontext, LDAP_OPT_PROTOCOL_VERSION, &v3); + if (ret != LDAP_OPT_SUCCESS) { + goto done; + } + + ret = ldap_set_option(ipactx->lcontext, LDAP_OPT_NETWORK_TIMEOUT, &tv); + if (ret != LDAP_OPT_SUCCESS) { + goto done; + } + + ret = ldap_set_option(ipactx->lcontext, LDAP_OPT_TIMEOUT, &tv); + if (ret != LDAP_OPT_SUCCESS) { + goto done; + } + + ret = ldap_sasl_bind_s(ipactx->lcontext, + NULL, "EXTERNAL", + NULL, NULL, NULL, NULL); + if (ret != LDAP_SUCCESS) { + goto done; + } + + /* TODO: search rootdse */ + + ret = ipadb_simple_search(ipactx, + ipactx->realm_base, LDAP_SCOPE_BASE, + "(objectclass=*)", NULL, &res); + if (ret) { + goto done; + } + + first = ldap_first_entry(ipactx->lcontext, res); + if (!first) { + goto done; + } + + vals = ldap_get_values_len(ipactx->lcontext, first, + "krbSupportedEncSaltTypes"); + if (!vals || !vals[0]) { + goto done; + } + + for (c = 0; vals[c]; c++) /* count */ ; + cvals = calloc(c, sizeof(char *)); + if (!cvals) { + ret = ENOMEM; + goto done; + } + for (i = 0; i < c; i++) { + cvals[i] = strndup(vals[i]->bv_val, vals[i]->bv_len); + if (!cvals[i]) { + ret = ENOMEM; + goto done; + } + } + + ret = parse_bval_key_salt_tuples(ipactx->kcontext, + (const char * const *)cvals, c, + &kst, &n_kst); + if (ret) { + goto done; + } + + if (ipactx->supp_encs) { + free(ipactx->supp_encs); + } + ipactx->supp_encs = kst; + ipactx->n_supp_encs = n_kst; + + ret = 0; + +done: + if (ret) { + if (ipactx->lcontext) { + ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL); + ipactx->lcontext = NULL; + } + if (ret == LDAP_SERVER_DOWN) { + return ETIMEDOUT; + } + return EIO; + } + + ldap_value_free_len(vals); + for (i = 0; i < c; i++) { + free(cvals[i]); + } + free(cvals); + + return 0; +} + +/* INTERFACE */ static krb5_error_code ipadb_init_library(void) { - return KRB5_PLUGIN_OP_NOTSUPP; + return 0; } static krb5_error_code ipadb_fini_library(void) { - return KRB5_PLUGIN_OP_NOTSUPP; + return 0; } static krb5_error_code ipadb_init_module(krb5_context kcontext, char *conf_section, char **db_args, int mode) { - return KRB5_PLUGIN_OP_NOTSUPP; + struct ipadb_context *ipactx; + krb5_error_code kerr; + int ret; + int i; + + /* make sure the context is freed to avoid leaking it */ + ipactx = ipadb_get_context(kcontext); + ipadb_context_free(kcontext, &ipactx); + + /* only check for unsupported 'temporary' value for now */ + for (i = 0; db_args != NULL && db_args[i] != NULL; i++) { + + if (strncmp(db_args[i], "temporary", 9) == 0) { + krb5_set_error_message(kcontext, EINVAL, + "Plugin requires -update argument!"); + return EINVAL; + } + } + + ipactx = calloc(1, sizeof(struct ipadb_context)); + if (!ipactx) { + return ENOMEM; + } + + ipactx->kcontext = kcontext; + + kerr = krb5_get_default_realm(kcontext, &ipactx->realm); + if (kerr != 0) { + ret = EINVAL; + goto fail; + } + + ipactx->uri = ipadb_realm_to_ldapi_uri(ipactx->realm); + if (!ipactx->uri) { + ret = ENOMEM; + goto fail; + } + + ipactx->base = ipadb_get_base_from_realm(kcontext); + if (!ipactx->base) { + ret = ENOMEM; + goto fail; + } + + ret = asprintf(&ipactx->realm_base, "cn=%s,cn=kerberos,%s", + ipactx->realm, ipactx->base); + if (ret == -1) { + ret = ENOMEM; + goto fail; + } + + ret = ipadb_get_connection(ipactx); + if (ret != 0) { + /* not a fatal failure, as the LDAP server may be temporarily down */ + /* TODO: spam syslog with this error */ + } + + kerr = krb5_db_set_context(kcontext, ipactx); + if (kerr != 0) { + ret = EACCES; + goto fail; + } + + return 0; + +fail: + ipadb_context_free(kcontext, &ipactx); + return ret; } static krb5_error_code ipadb_fini_module(krb5_context kcontext) { - return KRB5_PLUGIN_OP_NOTSUPP; + struct ipadb_context *ipactx; + + ipactx = ipadb_get_context(kcontext); + ipadb_context_free(kcontext, &ipactx); + + return 0; } static krb5_error_code ipadb_create(krb5_context kcontext, char *conf_section, char **db_args) { - return KRB5_PLUGIN_OP_NOTSUPP; + return ipadb_init_module(kcontext, conf_section, db_args, 0); } static krb5_error_code ipadb_get_age(krb5_context kcontext, diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h new file mode 100644 index 0000000..f91498a --- /dev/null +++ b/daemons/ipa-kdb/ipa_kdb.h @@ -0,0 +1,43 @@ +/* + * MIT Kerberos KDC database backend for FreeIPA + * + * Authors: Simo Sorce + * + * Copyright (C) 2011 Simo Sorce, Red Hat + * see file 'COPYING' for use and warranty information + * + * 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 3 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, see . + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + +#include +#include +#include + +struct ipadb_context { + char *uri; + char *base; + char *realm; + char *realm_base; + LDAP *lcontext; + krb5_context kcontext; + krb5_key_salt_tuple *supp_encs; + int n_supp_encs; +}; + +struct ipadb_context *ipadb_get_context(krb5_context kcontext); +int ipadb_get_connection(struct ipadb_context *ipactx);