summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2017-02-02 12:32:13 +0100
committerSumit Bose <sbose@redhat.com>2017-02-23 13:05:17 +0100
commit2c91c3790f4a0489b578b9c19b730af708973854 (patch)
tree0609ea6a3a01ad203eaf5740e4ff7dc332410f7c
parenta4c09fea01c3382ec17e2095df6fd79a73f71f1e (diff)
downloadfreeipa-certauth.tar.gz
freeipa-certauth.tar.xz
freeipa-certauth.zip
WIP: IPA certauth plugincertauth
-rw-r--r--daemons/ipa-kdb/Makefile.am7
-rw-r--r--daemons/ipa-kdb/ipa_kdb.c2
-rw-r--r--daemons/ipa-kdb/ipa_kdb.exports1
-rw-r--r--daemons/ipa-kdb/ipa_kdb.h17
-rw-r--r--daemons/ipa-kdb/ipa_kdb_certauth.c380
-rw-r--r--freeipa.spec.in1
6 files changed, 405 insertions, 3 deletions
diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am
index 6a2caa063..79ef57146 100644
--- a/daemons/ipa-kdb/Makefile.am
+++ b/daemons/ipa-kdb/Makefile.am
@@ -17,7 +17,6 @@ AM_CPPFLAGS = \
$(KRB5_CFLAGS) \
$(WARN_CFLAGS) \
$(NDRPAC_CFLAGS) \
- $(NSS_CFLAGS) \
$(NULL)
plugindir = $(libdir)/krb5/plugins/kdb
@@ -37,6 +36,7 @@ ipadb_la_SOURCES = \
ipa_kdb_mspac_private.h \
ipa_kdb_delegation.c \
ipa_kdb_audit_as.c \
+ ipa_kdb_certauth.c \
$(NULL)
ipadb_la_LDFLAGS = \
@@ -49,7 +49,7 @@ ipadb_la_LIBADD = \
$(LDAP_LIBS) \
$(NDRPAC_LIBS) \
$(UNISTRING_LIBS) \
- $(NSS_LIBS) \
+ -lsss_certmap_openssl \
$(top_builddir)/util/libutil.la \
$(NULL)
@@ -69,6 +69,7 @@ ipa_kdb_tests_SOURCES = \
ipa_kdb_mspac.c \
ipa_kdb_delegation.c \
ipa_kdb_audit_as.c \
+ ipa_kdb_certauth.c \
$(NULL)
ipa_kdb_tests_CFLAGS = $(CMOCKA_CFLAGS)
ipa_kdb_tests_LDADD = \
@@ -77,7 +78,7 @@ ipa_kdb_tests_LDADD = \
$(LDAP_LIBS) \
$(NDRPAC_LIBS) \
$(UNISTRING_LIBS) \
- $(NSS_LIBS) \
+ -lsss_certmap_openssl \
$(top_builddir)/util/libutil.la \
-lkdb5 \
-lsss_idmap \
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index e74ab5627..8f594490c 100644
--- a/daemons/ipa-kdb/ipa_kdb.c
+++ b/daemons/ipa-kdb/ipa_kdb.c
@@ -66,6 +66,8 @@ static void ipadb_context_free(krb5_context kcontext,
}
free(cfg->authz_data);
+ ipa_certauth_free_moddata(&((*ctx)->certauth_moddata));
+
free(*ctx);
*ctx = NULL;
}
diff --git a/daemons/ipa-kdb/ipa_kdb.exports b/daemons/ipa-kdb/ipa_kdb.exports
index d2c3f3024..27ce92d2e 100644
--- a/daemons/ipa-kdb/ipa_kdb.exports
+++ b/daemons/ipa-kdb/ipa_kdb.exports
@@ -3,6 +3,7 @@ EXPORTED {
# public symbols
global:
kdb_function_table;
+ certauth_ipakdb_initvt;
# everything else is local
local:
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
index bf9c1b009..2f0a2e67f 100644
--- a/daemons/ipa-kdb/ipa_kdb.h
+++ b/daemons/ipa-kdb/ipa_kdb.h
@@ -40,6 +40,7 @@
#include <arpa/inet.h>
#include <endian.h>
#include <unistd.h>
+#include <krb5/certauth_plugin.h>
#include "ipa_krb5.h"
#include "ipa_pwd.h"
@@ -110,6 +111,7 @@ struct ipadb_context {
krb5_key_salt_tuple *def_encs;
int n_def_encs;
struct ipadb_mspac *mspac;
+ krb5_certauth_moddata certauth_moddata;
/* Don't access this directly, use ipadb_get_global_config(). */
struct ipadb_global_config config;
@@ -320,3 +322,18 @@ ipadb_get_global_config(struct ipadb_context *ipactx);
int ipadb_get_enc_salt_types(struct ipadb_context *ipactx, LDAPMessage *entry,
char *attr, krb5_key_salt_tuple **enc_salt_types,
int *n_enc_salt_types);
+
+/* CERTAUTH PLUGIN */
+
+krb5_error_code certauth_ipakdb_initvt(krb5_context context,
+ int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata);
+
+/* from k5-int.h */
+#define PLUGIN_INTERFACE_CERTAUTH 10
+/* Register a plugin module of type interface_id and name modname. */
+krb5_error_code
+k5_plugin_register(krb5_context context, int interface_id, const char *modname,
+ krb5_plugin_initvt_fn module);
diff --git a/daemons/ipa-kdb/ipa_kdb_certauth.c b/daemons/ipa-kdb/ipa_kdb_certauth.c
new file mode 100644
index 000000000..359afd351
--- /dev/null
+++ b/daemons/ipa-kdb/ipa_kdb_certauth.c
@@ -0,0 +1,380 @@
+/** BEGIN COPYRIGHT BLOCK
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * In the following paragraph, "GPL" means the GNU General Public
+ * License, version 3 or any later version, and "Non-GPL Code" means
+ * code that is governed neither by the GPL nor a license
+ * compatible with the GPL.
+ *
+ * You may link the code of this Program with Non-GPL Code and convey
+ * linked combinations including the two, provided that such Non-GPL
+ * Code only links to the code of this Program through those well
+ * defined interfaces identified in the file named EXCEPTION found in
+ * the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline
+ * functions from the Approved Interfaces without causing the resulting
+ * work to be covered by the GPL. Only the copyright holders of this
+ * Program may make changes or additions to the list of Approved
+ * Interfaces.
+ *
+ * Authors:
+ * Sumit Bose <sbose@redhat.com>
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <errno.h>
+//#include <krb5/certauth_plugin.h>
+#include <syslog.h>
+#include <sss_certmap.h>
+
+#include "util/ipa_krb5.h"
+#include "ipa_kdb.h"
+
+#define IPA_OC_CERTMAP_RULE "ipaCertMapRule"
+#define IPA_CERTMAP_MAPRULE "ipaCertMapMapRule"
+#define IPA_CERTMAP_MATCHRULE "ipaCertMapMatchRule"
+#define IPA_CERTMAP_PRIORITY "ipaCertMapPriority"
+#define IPA_ENABLED_FLAG "ipaEnabledFlag"
+#define IPA_TRUE_VALUE "TRUE"
+#define IPA_ASSOCIATED_DOMAIN "associatedDomain"
+
+#define OBJECTCLASS "objectClass"
+
+#define CERTMAP_FILTER "(&("OBJECTCLASS"="IPA_OC_CERTMAP_RULE")" \
+ "("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))"
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
+#endif
+
+
+struct krb5_certauth_moddata_st {
+ char *local_domain;
+ struct sss_certmap_ctx *sss_certmap_ctx;
+ struct ipadb_context *ipactx;
+};
+
+void ipa_certmap_debug(void *private,
+ const char *file, long line,
+ const char *function,
+ const char *format, ...)
+{
+ va_list ap;
+ char str[255] = { 0 };
+
+ va_start(ap, format);
+ vsnprintf(str, sizeof(str)-1, format, ap);
+ va_end(ap);
+ krb5_klog_syslog(LOG_ERR, str);
+}
+
+void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata)
+{
+ if (moddata == NULL || *moddata == NULL) {
+ return;
+ }
+
+ free((*moddata)->local_domain);
+ (*moddata)->local_domain = NULL;
+ sss_certmap_free_ctx((*moddata)->sss_certmap_ctx);
+ (*moddata)->sss_certmap_ctx = NULL;
+
+ free(*moddata);
+
+ return;
+}
+
+static krb5_error_code ipa_certauth_authorize(krb5_context context,
+ krb5_certauth_moddata moddata,
+ krb5_data cert,
+ krb5_principal princ,
+ void *db_entry,
+ char ***authinds_out,
+ krb5_boolean *status)
+{
+ char *cert_filter = NULL;
+ char **domains = NULL;
+ int ret;
+ size_t c;
+ char *principal = NULL;
+ LDAPMessage *res = NULL;
+ krb5_error_code kerr;
+ LDAPMessage *lentry;
+
+ if (status == NULL
+ || moddata == NULL || moddata->sss_certmap_ctx == NULL) {
+ return EINVAL;
+ }
+
+ ret = krb5_unparse_name(context, princ, &principal);
+ if (ret != 0) {
+ goto done;
+ }
+ krb5_klog_syslog(LOG_ERR, "Doing certauth authorize for [%s]", principal);
+
+ ret = sss_certmap_get_search_filter(moddata->sss_certmap_ctx,
+ (uint8_t *) cert.data, cert.length,
+ &cert_filter, &domains);
+ if (ret != 0) {
+ if (ret == ENOENT) {
+ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
+ }
+ goto done;
+ }
+ krb5_klog_syslog(LOG_ERR, "Got cert filter [%s]", cert_filter);
+
+ /* If there are no domains assigned the rule will apply to the local
+ * domain only. */
+ if (domains != NULL) {
+
+ if (moddata->local_domain == NULL) {
+ /* We don't know our own domain name, in general this should not
+ * happen. But to be fault tolerant we allow matching rule which
+ * do not have a domain assigned. */
+
+ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
+ goto done;
+ }
+
+ for (c = 0; domains[c] != NULL; c++) {
+ if (strcasecmp(domains[c], moddata->local_domain) == 0) {
+ break;
+ }
+ }
+
+ /* Our domain was not in the list */
+ if (domains[c] == NULL) {
+ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
+ goto done;
+ }
+ }
+
+ kerr = ipadb_fetch_principals_with_extra_filter(moddata->ipactx,
+ KRB5_KDB_FLAG_ALIAS_OK,
+ principal,
+ cert_filter,
+ &res);
+ if (kerr != 0) {
+ krb5_klog_syslog(LOG_ERR, "Search failed [%d]", kerr);
+ ret = kerr;
+ goto done;
+ }
+
+ kerr = ipadb_find_principal(context, KRB5_KDB_FLAG_ALIAS_OK, res,
+ &principal, &lentry);
+ if (kerr == KRB5_KDB_NOENTRY) {
+ krb5_klog_syslog(LOG_ERR, "No matching entry found");
+ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
+ goto done;
+ } else if (kerr != 0) {
+ krb5_klog_syslog(LOG_ERR, "ipadb_find_principal failed [%d]", kerr);
+ ret = kerr;
+ goto done;
+ }
+
+ /* TODO: add more tests ? */
+
+ ret = 0;
+ *status = TRUE;
+
+done:
+ sss_certmap_free_filter_and_domains(cert_filter, domains);
+ krb5_free_unparsed_name(context, principal);
+ ldap_msgfree(res);
+
+ return ret;
+}
+
+static krb5_error_code ipa_certauth_req_init(krb5_context kcontext, void *opts,
+ krb5_certauth_moddata *moddata_out)
+{
+ int ret;
+ struct sss_certmap_ctx *ctx = NULL;
+ struct ipadb_context *ipactx;
+ krb5_error_code kerr;
+ char *basedn = NULL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *le;
+ LDAP *lc;
+ size_t c;
+ uint32_t prio;
+ char *map_rule = NULL;
+ char *match_rule = NULL;
+ char **domains = NULL;
+
+ const char *certmap_attrs[] = { OBJECTCLASS,
+ IPA_CERTMAP_PRIORITY,
+ IPA_CERTMAP_MATCHRULE,
+ IPA_CERTMAP_MAPRULE,
+ IPA_ASSOCIATED_DOMAIN,
+ IPA_ENABLED_FLAG,
+ NULL};
+
+
+ krb5_klog_syslog(LOG_ERR, "IPA certauth plugin loaded.");
+
+ ipactx = ipadb_get_context(kcontext);
+ if (ipactx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ if (ipactx->certauth_moddata == NULL) {
+ ret = asprintf(&basedn, "cn=certmap,%s", ipactx->base);
+ if (ret == -1) {
+ return ENOMEM;
+ }
+
+ kerr = ipadb_simple_search(ipactx,basedn, LDAP_SCOPE_SUBTREE,
+ CERTMAP_FILTER, discard_const(certmap_attrs),
+ &result);
+ if (kerr != 0 && kerr != KRB5_KDB_NOENTRY) {
+ goto done;
+ }
+
+ ret = sss_certmap_init(NULL, ipa_certmap_debug, NULL, &ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (kerr == KRB5_KDB_NOENTRY) {
+ ret = sss_certmap_add_rule(ctx, SSS_CERTMAP_MIN_PRIO,
+ NULL, NULL, NULL);
+ if (ret != 0) {
+ goto done;
+ }
+ } else {
+ lc = ipactx->lcontext;
+
+ for (le = ldap_first_entry(lc, result); le;
+ le = ldap_next_entry(lc, le)) {
+ prio = SSS_CERTMAP_MIN_PRIO;
+ ret = ipadb_ldap_attr_to_uint32(lc, le, IPA_CERTMAP_PRIORITY,
+ &prio);
+ if (ret != 0 && ret != ENOENT) {
+ goto done;
+ }
+
+ free(map_rule);
+ map_rule = NULL;
+ ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MAPRULE,
+ &map_rule);
+ if (ret != 0 && ret != ENOENT) {
+ goto done;
+ }
+
+ free(match_rule);
+ match_rule = NULL;
+ ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MATCHRULE,
+ &match_rule);
+ if (ret != 0 && ret != ENOENT) {
+ goto done;
+ }
+
+ if (domains != NULL) {
+ for (c = 0; domains[c] != NULL; c++) {
+ free(domains[c]);
+ }
+ free(domains);
+ domains = NULL;
+ }
+ ret = ipadb_ldap_attr_to_strlist(lc, le, IPA_ASSOCIATED_DOMAIN,
+ &domains);
+ if (ret != 0 && ret != ENOENT) {
+ goto done;
+ }
+
+ ret = sss_certmap_add_rule(ctx, prio, match_rule, map_rule,
+ (const char **) domains);
+ if (ret != 0) {
+ goto done;
+ }
+ }
+ }
+
+ ipactx->certauth_moddata = calloc(1,
+ sizeof(struct krb5_certauth_moddata_st));
+ if (ipactx->certauth_moddata == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (ipactx->realm != NULL) {
+ ipactx->certauth_moddata->local_domain = strdup(ipactx->realm);
+ if (ipactx->certauth_moddata->local_domain == NULL) {
+ free(ipactx->certauth_moddata);
+ ipactx->certauth_moddata = NULL;
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ipactx->certauth_moddata->sss_certmap_ctx = ctx;
+ ipactx->certauth_moddata->ipactx = ipactx;
+
+ }
+
+ *moddata_out = ipactx->certauth_moddata;
+
+ ret = 0;
+
+done:
+ ldap_msgfree(result);
+ free(basedn);
+ free(map_rule);
+ free(match_rule);
+ if (domains != NULL) {
+ for (c = 0; domains[c] != NULL; c++) {
+ free(domains[c]);
+ }
+ free(domains);
+ domains = NULL;
+ }
+
+ if (ret != 0) {
+ sss_certmap_free_ctx(ctx);
+ }
+
+ return ret;
+}
+
+static void ipa_certauth_req_fini(krb5_context context,
+ krb5_certauth_moddata *moddata_out)
+{
+ krb5_klog_syslog(LOG_ERR, "IPA certauth plugin un-loaded.");
+ return;
+}
+
+
+krb5_error_code certauth_ipakdb_initvt(krb5_context context,
+ int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_certauth_vtable vt;
+
+ if (maj_ver != 1) {
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ }
+
+ vt = (krb5_certauth_vtable) vtable;
+
+ vt->name = "ipakdb";
+ vt->authorize = ipa_certauth_authorize;
+ vt->req_init = ipa_certauth_req_init;
+ vt->req_fini = ipa_certauth_req_fini;
+ return 0;
+}
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 5c835ca29..cb1b8eb8b 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -101,6 +101,7 @@ BuildRequires: nss-devel
BuildRequires: openssl-devel
BuildRequires: libuuid-devel
BuildRequires: libsss_idmap-devel
+BuildRequires: libsss_certmap-devel
# 1.14.0: sss_nss_getnamebycert (https://fedorahosted.org/sssd/ticket/2897)
BuildRequires: libsss_nss_idmap-devel >= 1.14.0
BuildRequires: rhino