Blob Blame History Raw
From 8cb263f039da9e616e907d25701593dca22b11ed Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Mon, 1 Aug 2016 12:52:07 +0200
Subject: [PATCH 22/36] KCM: Initial responder build and packaging
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Adds the initial build of the Kerberos Cache Manager responder (KCM).

This is a deamon that is capable of holding and storing Kerberos
ccaches. When KCM is used, the kerberos libraries (invoked through e.g.
kinit) are referred to as a 'client' and the KCM deamon is referred to
as 'server'.

At the moment, only the Heimdal implementation of Kerberos implements the
KCM server:
    https://www.h5l.org/manual/HEAD/info/heimdal/Credential-cache-server-_002d-KCM.html
This patch adds a KCM server to SSSD.

In MIT, only the 'client-side' support was added:
    http://k5wiki.kerberos.org/wiki/Projects/KCM_client
This page also describes the protocol between the client and the server.

The client is capable of talking to the server over either UNIX sockets
(Linux, most Unixes) or Mach RPC (macOS). Our server only implements the
UNIX sockets way and should be socket-activated by systemd, although can
in theory be also ran explicitly.

The KCM server only builds if the configuration option "--with-kcm" is
enabled. It is packaged in a new subpackage sssd-kcm in order to allow
distributions to enable the KCM credential caches by installing this
subpackage only, without the rest of the SSSD. The sssd-kcm subpackage
also includes a krb5.conf.d snippet that allows the admin to just uncomment
the KCM defaults and instructs them to start the socket.

The server can be configured in sssd.conf in the "[kcm]" section.
By default, the server only listens on the same socket path the Heimdal
server uses, which is "/var/run/.heim_org.h5l.kcm-socket". This is,
however, configurable.

The file src/responder/kcm/kcm.h is more or less directly imported from
the MIT Kerberos tree, with an additional sentinel code and some
comments. Not all KCM operations are implemented, only those that also
the MIT client implements. That said, this KCM server should also be
usable with a Heimdal client, although no special testing was with this
hybrid.

The patch also adds several error codes that will be used in later
patches.

Related to:
    https://pagure.io/SSSD/sssd/issue/2887

Reviewed-by: Michal Židek <mzidek@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
---
 Makefile.am                          |  53 ++++++++
 configure.ac                         |  10 +-
 contrib/kcm_default_ccache           |  12 ++
 contrib/sssd.spec.in                 |  41 ++++++
 src/conf_macros.m4                   |  16 +++
 src/confdb/confdb.h                  |   3 +
 src/config/cfg_rules.ini             |  19 +++
 src/external/libcurl.m4              |   6 +-
 src/responder/kcm/kcm.c              | 254 +++++++++++++++++++++++++++++++++++
 src/responder/kcm/kcm.h              |  97 +++++++++++++
 src/responder/kcm/kcmsrv_cmd.c       |  65 +++++++++
 src/responder/kcm/kcmsrv_pvt.h       |  58 ++++++++
 src/sysv/systemd/sssd-kcm.service.in |   9 ++
 src/sysv/systemd/sssd-kcm.socket.in  |  10 ++
 src/util/util_errors.c               |   5 +
 src/util/util_errors.h               |   5 +
 16 files changed, 658 insertions(+), 5 deletions(-)
 create mode 100644 contrib/kcm_default_ccache
 create mode 100644 src/responder/kcm/kcm.c
 create mode 100644 src/responder/kcm/kcm.h
 create mode 100644 src/responder/kcm/kcmsrv_cmd.c
 create mode 100644 src/responder/kcm/kcmsrv_pvt.h
 create mode 100644 src/sysv/systemd/sssd-kcm.service.in
 create mode 100644 src/sysv/systemd/sssd-kcm.socket.in

diff --git a/Makefile.am b/Makefile.am
index 7516338bc6fd95045d20db8155a0c82fd7003358..4248536e90370c1aab59549a9c18408ef314e6d4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -87,6 +87,7 @@ sudolibdir = @sudolibpath@
 polkitdir = @polkitdir@
 pamconfdir = $(sysconfdir)/pam.d
 systemtap_tapdir = @tapset_dir@
+krb5sysincludedir = $(sysconfdir)/krb5.conf.d
 
 if HAVE_SYSTEMD_UNIT
 ifp_exec_cmd = $(sssdlibexecdir)/sssd_ifp --uid 0 --gid 0 --debug-to-files --dbus-activated
@@ -186,6 +187,11 @@ endif
 if BUILD_SECRETS
 sssdlibexec_PROGRAMS += sssd_secrets
 endif
+if BUILD_KCM
+sssdlibexec_PROGRAMS += sssd_kcm
+dist_krb5sysinclude_DATA = contrib/kcm_default_ccache
+endif
+
 
 if BUILD_PAC_RESPONDER
     sssdlibexec_PROGRAMS += sssd_pac
@@ -703,6 +709,8 @@ dist_noinst_HEADERS = \
     src/responder/secrets/secsrv_private.h \
     src/responder/secrets/secsrv_local.h \
     src/responder/secrets/secsrv_proxy.h \
+    src/responder/kcm/kcm.h \
+    src/responder/kcm/kcmsrv_pvt.h \
     src/sbus/sbus_client.h \
     src/sbus/sssd_dbus.h \
     src/sbus/sssd_dbus_meta.h \
@@ -1476,6 +1484,24 @@ sssd_secrets_LDADD = \
     $(NULL)
 endif
 
+if BUILD_KCM
+sssd_kcm_SOURCES = \
+    src/responder/kcm/kcm.c \
+    src/responder/kcm/kcmsrv_cmd.c \
+    src/util/sss_sockets.c \
+    $(SSSD_RESPONDER_OBJ) \
+    $(NULL)
+sssd_kcm_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(KRB5_CFLAGS) \
+    $(NULL)
+sssd_kcm_LDADD = \
+    $(KRB5_LIBS) \
+    $(SSSD_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    $(NULL)
+endif
+
 sssd_be_SOURCES = \
     src/providers/data_provider_be.c \
     src/providers/data_provider_req.c \
@@ -4259,6 +4285,12 @@ if BUILD_SUDO
         src/sysv/systemd/sssd-sudo.service \
         $(NULL)
 endif
+if BUILD_KCM
+    systemdunit_DATA += \
+        src/sysv/systemd/sssd-kcm.socket \
+        src/sysv/systemd/sssd-kcm.service \
+        $(NULL)
+endif
 if WITH_JOURNALD
     systemdconf_DATA += \
         src/sysv/systemd/journal.conf
@@ -4350,6 +4382,12 @@ EXTRA_DIST += \
     src/sysv/systemd/sssd-sudo.service.in \
     $(NULL)
 endif
+if BUILD_KCM
+EXTRA_DIST += \
+    src/sysv/systemd/sssd-kcm.socket.in \
+    src/sysv/systemd/sssd-kcm.service.in \
+    $(NULL)
+endif
 
 src/sysv/systemd/sssd.service: src/sysv/systemd/sssd.service.in Makefile
 	@$(MKDIR_P) src/sysv/systemd/
@@ -4433,6 +4471,16 @@ src/sysv/systemd/sssd-sudo.service: src/sysv/systemd/sssd-sudo.service.in Makefi
 	$(replace_script)
 endif
 
+if BUILD_KCM
+src/sysv/systemd/sssd-kcm.socket: src/sysv/systemd/sssd-kcm.socket.in Makefile
+	@$(MKDIR_P) src/sysv/systemd/
+	$(replace_script)
+
+src/sysv/systemd/sssd-kcm.service: src/sysv/systemd/sssd-kcm.service.in Makefile
+	@$(MKDIR_P) src/sysv/systemd/
+	$(replace_script)
+endif
+
 SSSD_USER_DIRS = \
     $(DESTDIR)$(dbpath) \
     $(DESTDIR)$(keytabdir) \
@@ -4596,6 +4644,9 @@ install-data-hook:
 if BUILD_SAMBA
 	mv $(DESTDIR)/$(winbindplugindir)/winbind_idmap_sss.so $(DESTDIR)/$(winbindplugindir)/sss.so
 endif
+if BUILD_KCM
+	$(MKDIR_P) $(DESTDIR)/$(krb5sysincludedir)
+endif
 
 uninstall-hook:
 	if [ -f $(abs_builddir)/src/config/.files2 ]; then \
@@ -4670,6 +4721,8 @@ endif
 	rm -f $(builddir)/src/sysv/systemd/sssd-sudo.service
 	rm -f $(builddir)/src/sysv/systemd/sssd-secrets.socket
 	rm -f $(builddir)/src/sysv/systemd/sssd-secrets.service
+	rm -f $(builddir)/src/sysv/systemd/sssd-kcm.socket
+	rm -f $(builddir)/src/sysv/systemd/sssd-kcm.service
 	rm -f $(builddir)/src/sysv/systemd/journal.conf
 
 CLEANFILES += *.X */*.X */*/*.X
diff --git a/configure.ac b/configure.ac
index dd1012015a5fea9f25e5b5199b4868fbc0bc14c4..c363d48a806cc1998e85779a92b6b59b0e2a5c9c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -155,6 +155,7 @@ WITH_SSSD_USER
 SSSD_RUNSTATEDIR
 WITH_SECRETS
 WITH_SECRETS_DB_PATH
+WITH_KCM
 
 m4_include([src/external/pkg.m4])
 m4_include([src/external/libpopt.m4])
@@ -193,13 +194,20 @@ m4_include([src/external/libresolv.m4])
 m4_include([src/external/intgcheck.m4])
 m4_include([src/external/systemtap.m4])
 m4_include([src/external/service.m4])
-m4_include([src/external/libcurl.m4])
 
 if test x$with_secrets = xyes; then
     m4_include([src/external/libhttp_parser.m4])
     m4_include([src/external/libjansson.m4])
 fi
 
+if test x$with_kcm = xyes; then
+    m4_include([src/external/libcurl.m4])
+fi
+# This variable is defined by external/libcurl.m4, but conditionals
+# must be always evaluated
+AM_CONDITIONAL([BUILD_WITH_LIBCURL],
+               [test x"$have_curlopt_unix_sockpath" = xyes])
+
 WITH_UNICODE_LIB
 if test x$unicode_lib = xlibunistring; then
     m4_include([src/external/libunistring.m4])
diff --git a/contrib/kcm_default_ccache b/contrib/kcm_default_ccache
new file mode 100644
index 0000000000000000000000000000000000000000..ac88fca86b60b19f772912b5d9d14595a96d101d
--- /dev/null
+++ b/contrib/kcm_default_ccache
@@ -0,0 +1,12 @@
+# This file should normally be installed by your distribution into a
+# directory that is included from the Kerberos configuration file (/etc/krb5.conf)
+# On Fedora/RHEL/CentOS, this is /etc/krb5.conf.d/
+#
+# To enable the KCM credential cache, uncomment the following lines and
+# enable the KCM socket and the service:
+#   systemctl enable sssd-kcm.socket
+#   systemctl start sssd-kcm.socket
+#   systemctl enable sssd-kcm.service
+
+#[libdefaults]
+#    default_ccache_name = KCM:
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 28ebe07a26a3112210b092b7831e7f6aae061c8d..5c7c2af521a84ef2ca6cca7b2d6cd1f9b3057056 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -112,6 +112,13 @@
     %global enable_systemtap_opt --enable-systemtap
 %endif
 
+%if (0%{?fedora} >= 23 || 0%{?rhel} >= 7)
+    %global with_kcm 1
+    %global with_kcm_option --with-kcm
+%else
+    %global with_kcm_option --without-kcm
+%endif
+
 Name: @PACKAGE_NAME@
 Version: @PACKAGE_VERSION@
 Release: 0@PRERELEASE_VERSION@%{?dist}
@@ -677,6 +684,18 @@ Requires: libsss_certmap = %{version}-%{release}
 %description -n libsss_certmap-devel
 Library to map certificates to users based on rules
 
+%if (0%{?with_kcm} == 1)
+%package kcm
+Summary: An implementation of a Kerberos KCM server
+Group:  Applications/System
+License: GPLv3+
+Requires: sssd-common = %{version}-%{release}
+
+%description kcm
+An implementation of a Kerberos KCM server. Use this package if you want to
+use the KCM: Kerberos credentials cache.
+%endif
+
 %prep
 %setup -q -n %{name}-%{version}
 
@@ -706,6 +725,7 @@ autoreconf -ivf
     %{?with_python3_option} \
     %{?enable_polkit_rules_option} \
     %{?enable_systemtap_opt} \
+    %{?with_kcm_option} \
     %{?experimental}
 
 make %{?_smp_mflags} all
@@ -1178,6 +1198,15 @@ done
 %{_libdir}/libsss_certmap.so
 %{_libdir}/pkgconfig/sss_certmap.pc
 
+%if (0%{?with_kcm} == 1)
+%files kcm
+%{_libexecdir}/%{servicename}/sssd_kcm
+%dir %{_sysconfdir}/krb5.conf.d
+%config(noreplace) %{_sysconfdir}/krb5.conf.d/kcm_default_ccache
+%{_unitdir}/sssd-kcm.socket
+%{_unitdir}/sssd-kcm.service
+%endif
+
 %pre common
 getent group sssd >/dev/null || groupadd -r sssd
 getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd
@@ -1274,6 +1303,18 @@ fi
 
 %postun -n libsss_simpleifp -p /sbin/ldconfig
 
+%if (0%{?with_kcm} == 1)
+%post kcm
+%systemd_post sssd-kcm.socket
+
+%preun kcm
+%systemd_preun sssd-kcm.socket
+
+%postun kcm
+%systemd_postun_with_restart sssd-kcm.socket
+%systemd_postun_with_restart sssd-kcm.service
+%endif
+
 %changelog
 * Mon Mar 15 2010 Stephen Gallagher <sgallagh@redhat.com> - @PACKAGE_VERSION@-0@PRERELEASE_VERSION@
 - Automated build of the SSSD
diff --git a/src/conf_macros.m4 b/src/conf_macros.m4
index 749e7694f4dd7086468e461194ef274be2094236..420997229cb3c244afd8fb21b074e43a21de0eda 100644
--- a/src/conf_macros.m4
+++ b/src/conf_macros.m4
@@ -887,6 +887,22 @@ AC_DEFUN([WITH_SECRETS],
     AM_CONDITIONAL([BUILD_SECRETS], [test x"$with_secrets" = xyes])
   ])
 
+AC_DEFUN([WITH_KCM],
+  [ AC_ARG_WITH([kcm],
+                [AC_HELP_STRING([--with-kcm],
+                                [Whether to build with KCM server support [yes]]
+                               )
+                ],
+                [with_kcm=$withval],
+                with_kcm=yes
+               )
+
+    if test x"$with_kcm" = xyes; then
+        AC_DEFINE(BUILD_KCM, 1, [whether to build with KCM server support])
+    fi
+    AM_CONDITIONAL([BUILD_KCM], [test x"$with_kcm" = xyes])
+  ])
+
 AC_DEFUN([WITH_SECRETS_DB_PATH],
   [ AC_ARG_WITH([secrets-db-path],
                 [AC_HELP_STRING([--with-secrets-db-path=PATH],
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index c05b1cee45ece748bf8e2b1e1ecf3dc28979e48b..c443e869a7a6782265b42c4ad122867c4e3dd8e0 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -231,6 +231,9 @@
 #define CONFDB_SEC_MAX_SECRETS "max_secrets"
 #define CONFDB_SEC_MAX_PAYLOAD_SIZE "max_payload_size"
 
+/* KCM Service */
+#define CONFDB_KCM_CONF_ENTRY "config/kcm"
+#define CONFDB_KCM_SOCKET "socket_path"
 
 struct confdb_ctx;
 struct config_file_ctx;
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index c287328828cae2f0ad8a5a105f1c2b3e05353021..5e789c51658c51c0af1338d23d6c0f30f40bf119 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -9,6 +9,7 @@ section = ssh
 section = pac
 section = ifp
 section = secrets
+section = kcm
 section_re = ^secrets/users/[0-9]\+$
 section_re = ^domain/.*$
 
@@ -262,6 +263,24 @@ option = forward_headers
 option = username
 option = password
 
+# KCM responder
+[rule/allowed_kcm_options]
+validator = ini_allowed_options
+section_re = ^kcm$
+
+option = timeout
+option = debug
+option = debug_level
+option = debug_timestamps
+option = debug_microseconds
+option = debug_to_files
+option = command
+option = reconnection_retries
+option = fd_limit
+option = client_idle_timeout
+option = description
+option = socket_path
+
 [rule/allowed_domain_options]
 validator = ini_allowed_options
 section_re = ^domain/.*$
diff --git a/src/external/libcurl.m4 b/src/external/libcurl.m4
index 3bc303ca4e1dea8a04117e32b8c4466b80d885b1..b420b04ad806bd1251f086b773ffe480d39f8bd3 100644
--- a/src/external/libcurl.m4
+++ b/src/external/libcurl.m4
@@ -9,8 +9,8 @@ AS_IF([test x$enable_libcurl = xyes],
       [PKG_CHECK_MODULES([CURL],
                          [libcurl],
                          [found_libcurl=yes],
-                         [AC_MSG_WARN([
-The libcurl development library was not found. Some features will be disabled.])
+                         [AC_MSG_ERROR([
+The libcurl development library was not found.])
       ])])
 
 AS_IF([test x"$found_libcurl" = xyes],
@@ -32,7 +32,5 @@ AS_IF([test x"$found_libcurl" = xyes],
 AC_SUBST(CURL_LIBS)
 AC_SUBST(CURL_CFLAGS)
 
-AM_CONDITIONAL([BUILD_WITH_LIBCURL],
-               [test x"$have_curlopt_unix_sockpath" = xyes])
 AM_COND_IF([BUILD_WITH_LIBCURL],
            [AC_DEFINE_UNQUOTED(HAVE_LIBCURL, 1, [Build with libcurl support])])
diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
new file mode 100644
index 0000000000000000000000000000000000000000..90a6999c5e39d48a1a2ea8168d171612a65077d5
--- /dev/null
+++ b/src/responder/kcm/kcm.c
@@ -0,0 +1,254 @@
+/*
+   SSSD
+
+   KCM Server - the mainloop and server setup
+
+   Copyright (C) Red Hat, 2016
+
+   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/>.
+*/
+
+#include "config.h"
+
+#include <popt.h>
+#include <krb5/krb5.h>
+
+#include "responder/kcm/kcm.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/common/responder.h"
+#include "util/util.h"
+
+#define DEFAULT_KCM_FD_LIMIT 2048
+
+#ifndef SSS_KCM_SOCKET_NAME
+#define SSS_KCM_SOCKET_NAME DEFAULT_KCM_SOCKET_PATH
+#endif
+
+static int kcm_responder_ctx_destructor(void *ptr)
+{
+    struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx);
+
+    /* mark that we are shutting down the responder, so it is propagated
+     * into underlying contexts that are freed right before rctx */
+    DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n");
+    rctx->shutting_down = true;
+
+    return 0;
+}
+
+static int kcm_get_config(struct kcm_ctx *kctx)
+{
+    int ret;
+    char *sock_name;
+
+    ret = confdb_get_int(kctx->rctx->cdb,
+                         CONFDB_KCM_CONF_ENTRY,
+                         CONFDB_SERVICE_FD_LIMIT,
+                         DEFAULT_KCM_FD_LIMIT,
+                         &kctx->fd_limit);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to get file descriptors limit\n");
+        goto done;
+    }
+
+    ret = confdb_get_int(kctx->rctx->cdb,
+                         kctx->rctx->confdb_service_path,
+                         CONFDB_RESPONDER_CLI_IDLE_TIMEOUT,
+                         CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT,
+                         &kctx->rctx->client_idle_timeout);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot get the client idle timeout [%d]: %s\n",
+               ret, strerror(ret));
+        goto done;
+    }
+
+    /* Ensure that the client timeout is at least ten seconds */
+    if (kctx->rctx->client_idle_timeout < 10) {
+        kctx->rctx->client_idle_timeout = 10;
+    }
+
+    ret = confdb_get_string(kctx->rctx->cdb,
+                            kctx->rctx,
+                            kctx->rctx->confdb_service_path,
+                            CONFDB_KCM_SOCKET,
+                            SSS_KCM_SOCKET_NAME,
+                            &sock_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot get the client idle timeout [%d]: %s\n",
+               ret, strerror(ret));
+        goto done;
+    }
+    kctx->rctx->sock_name = sock_name;
+
+    ret = EOK;
+
+done:
+    return ret;
+}
+
+static int kcm_data_destructor(void *ptr)
+{
+    struct kcm_resp_ctx *kcm_data = talloc_get_type(ptr, struct kcm_resp_ctx);
+
+    if (kcm_data != NULL) {
+        krb5_free_context(kcm_data->k5c);
+    }
+    return 0;
+}
+
+static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx)
+{
+    struct kcm_resp_ctx *kcm_data;
+    krb5_error_code kret;
+
+    kcm_data = talloc_zero(mem_ctx, struct kcm_resp_ctx);
+    if (kcm_data == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm data\n");
+        return NULL;
+    }
+
+    kret = krb5_init_context(&kcm_data->k5c);
+    if (kret != EOK) {
+        talloc_free(kcm_data);
+        return NULL;
+    }
+    talloc_set_destructor((TALLOC_CTX*)kcm_data, kcm_data_destructor);
+
+    return kcm_data;
+}
+
+static int kcm_process_init(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct confdb_ctx *cdb)
+{
+    struct resp_ctx *rctx;
+    struct kcm_ctx *kctx;
+    int ret;
+
+    rctx = talloc_zero(mem_ctx, struct resp_ctx);
+    if (rctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n");
+        return ENOMEM;
+    }
+    rctx->ev = ev;
+    rctx->cdb = cdb;
+    rctx->confdb_service_path = CONFDB_KCM_CONF_ENTRY;
+    rctx->shutting_down = false;
+    rctx->lfd = -1;
+    rctx->priv_lfd = -1;
+
+    talloc_set_destructor((TALLOC_CTX*)rctx, kcm_responder_ctx_destructor);
+
+    kctx = talloc_zero(rctx, struct kcm_ctx);
+    if (kctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm_ctx\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    kctx->rctx = rctx;
+    kctx->rctx->pvt_ctx = kctx;
+
+    ret = kcm_get_config(kctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM config\n");
+        goto fail;
+    }
+
+    kctx->kcm_data = kcm_data_setup(kctx);
+    if (kctx->kcm_data == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "fatal error initializing responder data\n");
+        ret = EIO;
+        goto fail;
+    }
+
+    /* Set up file descriptor limits */
+    responder_set_fd_limit(kctx->fd_limit);
+
+    ret = activate_unix_sockets(rctx, kcm_connection_setup);
+    if (ret != EOK) goto fail;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "KCM Initialization complete\n");
+
+    return EOK;
+
+fail:
+    talloc_free(rctx);
+    return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+    int opt;
+    poptContext pc;
+    struct main_context *main_ctx;
+    int ret;
+    uid_t uid;
+    gid_t gid;
+
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_MAIN_OPTS
+        SSSD_SERVER_OPTS(uid, gid)
+        POPT_TABLEEND
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    umask(DFL_RSP_UMASK);
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                  poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+
+    poptFreeContext(pc);
+
+    DEBUG_INIT(debug_level);
+
+    /* set up things like debug, signals, daemonization, etc... */
+    debug_log_file = "sssd_kcm";
+
+    ret = server_setup("sssd[kcm]", 0, uid, gid, CONFDB_KCM_CONF_ENTRY,
+                       &main_ctx);
+    if (ret != EOK) return 2;
+
+    ret = die_if_parent_died();
+    if (ret != EOK) {
+        /* This is not fatal, don't return */
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Could not set up to exit when parent process does\n");
+    }
+
+    ret = kcm_process_init(main_ctx,
+                           main_ctx->event_ctx,
+                           main_ctx->confdb_ctx);
+    if (ret != EOK) return 3;
+
+    /* loop on main */
+    server_loop(main_ctx);
+
+    return 0;
+}
diff --git a/src/responder/kcm/kcm.h b/src/responder/kcm/kcm.h
new file mode 100644
index 0000000000000000000000000000000000000000..1ea7e9bbca754dca2eeb72a08830fa2f95713b4f
--- /dev/null
+++ b/src/responder/kcm/kcm.h
@@ -0,0 +1,97 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/kcm.h - Kerberos cache manager protocol declarations */
+/*
+ * Copyright (C) 2014 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef KCM_H
+#define KCM_H
+
+#define KCM_PROTOCOL_VERSION_MAJOR 2
+#define KCM_PROTOCOL_VERSION_MINOR 0
+
+#define KCM_UUID_LEN 16
+
+/* This should ideally be in RUNSTATEDIR, but Heimdal uses a hardcoded
+ * /var/run, and we need to use the same default path. */
+#define DEFAULT_KCM_SOCKET_PATH "/var/run/.heim_org.h5l.kcm-socket"
+#define DEFAULT_KCM_MACH_SERVICE "org.h5l.kcm"
+
+/*
+ * All requests begin with:
+ *   major version (1 bytes)
+ *   minor version (1 bytes)
+ *   opcode (16-bit big-endian)
+ *
+ * All replies begin with a 32-bit big-endian reply code.
+ *
+ * Parameters are appended to the request or reply with no delimiters.  Flags
+ * and time offsets are stored as 32-bit big-endian integers.  Names are
+ * marshalled as zero-terminated strings.  Principals and credentials are
+ * marshalled in the v4 FILE ccache format.  UUIDs are 16 bytes.  UUID lists
+ * are not delimited, so nothing can come after them.
+ */
+
+/* Opcodes without comments are currently unused in the MIT client
+ * implementation. */
+typedef enum kcm_opcode {
+    KCM_OP_NOOP,
+    KCM_OP_GET_NAME,
+    KCM_OP_RESOLVE,
+    KCM_OP_GEN_NEW,             /* 0x3                 () -> (name)      */
+    KCM_OP_INITIALIZE,          /* 0x4      (name, princ) -> ()          */
+    KCM_OP_DESTROY,             /* 0x4             (name) -> ()          */
+    KCM_OP_STORE,               /* 0x6       (name, cred) -> ()          */
+    KCM_OP_RETRIEVE,
+    KCM_OP_GET_PRINCIPAL,       /* 0x8             (name) -> (princ)     */
+    KCM_OP_GET_CRED_UUID_LIST,  /* 0x9             (name) -> (uuid, ...) */
+    KCM_OP_GET_CRED_BY_UUID,    /* 0xa       (name, uuid) -> (cred)      */
+    KCM_OP_REMOVE_CRED,         /* (name, flags, credtag) -> ()          */
+    KCM_OP_SET_FLAGS,
+    KCM_OP_CHOWN,
+    KCM_OP_CHMOD,
+    KCM_OP_GET_INITIAL_TICKET,
+    KCM_OP_GET_TICKET,
+    KCM_OP_MOVE_CACHE,
+    KCM_OP_GET_CACHE_UUID_LIST, /* 0x12                () -> (uuid, ...) */
+    KCM_OP_GET_CACHE_BY_UUID,   /* 0x13            (uuid) -> (name)      */
+    KCM_OP_GET_DEFAULT_CACHE,   /* 0x14                () -> (name)      */
+    KCM_OP_SET_DEFAULT_CACHE,   /* 0x15            (name) -> ()          */
+    KCM_OP_GET_KDC_OFFSET,      /* 0x16            (name) -> (offset)    */
+    KCM_OP_SET_KDC_OFFSET,      /* 0x17    (name, offset) -> ()          */
+    KCM_OP_ADD_NTLM_CRED,
+    KCM_OP_HAVE_NTLM_CRED,
+    KCM_OP_DEL_NTLM_CRED,
+    KCM_OP_DO_NTLM_AUTH,
+    KCM_OP_GET_NTLM_USER_LIST,
+
+    KCM_OP_SENTINEL,            /* SSSD addition, not in the MIT header */
+} kcm_opcode;
+
+#endif /* KCM_H */
diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
new file mode 100644
index 0000000000000000000000000000000000000000..e9a03cbd41169c93e00b0630dc1e05e205881ec9
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_cmd.c
@@ -0,0 +1,65 @@
+/*
+   SSSD
+
+   KCM Server - the KCM server request and reply parsing and dispatching
+
+   Copyright (C) Red Hat, 2016
+
+   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/>.
+*/
+
+#include "config.h"
+#include "util/util.h"
+#include "responder/common/responder.h"
+
+struct kcm_proto_ctx {
+    void *unused;
+};
+
+static void kcm_fd_handler(struct tevent_context *ev,
+                           struct tevent_fd *fde,
+                           uint16_t flags, void *ptr)
+{
+    errno_t ret;
+    struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx);
+
+    /* Always reset the idle timer on any activity */
+    ret = reset_client_idle_timer(cctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not create idle timer for client. "
+               "This connection may not auto-terminate\n");
+        /* Non-fatal, continue */
+    }
+}
+
+int kcm_connection_setup(struct cli_ctx *cctx)
+{
+    struct kcm_proto_ctx *protocol_ctx;
+
+    protocol_ctx = talloc_zero(cctx, struct kcm_proto_ctx);
+    if (protocol_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    cctx->protocol_ctx = protocol_ctx;
+    cctx->cfd_handler = kcm_fd_handler;
+    return EOK;
+}
+
+/* Dummy, not used here but required to link to other responder files */
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+    return NULL;
+}
diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
new file mode 100644
index 0000000000000000000000000000000000000000..a7c9d062c17f09986d894064176c3a461d396ac0
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_pvt.h
@@ -0,0 +1,58 @@
+/*
+   SSSD
+
+   KCM Server - private header file
+
+   Copyright (C) Red Hat, 2016
+
+   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/>.
+*/
+
+#ifndef __KCMSRV_PVT_H__
+#define __KCMSRV_PVT_H__
+
+#include "config.h"
+
+#include <sys/types.h>
+#include "responder/common/responder.h"
+
+/* KCM IO structure */
+struct kcm_data {
+    uint8_t *data;
+    size_t length;
+};
+
+/* To avoid leaking the sssd-specific responder data to other
+ * modules, the ccache databases and other KCM specific data
+ * are kept separately
+ */
+struct kcm_resp_ctx {
+    krb5_context k5c;
+};
+
+/* responder context that contains both the responder data,
+ * like the ccaches and the sssd-specific stuff like the
+ * generic responder ctx
+ */
+struct kcm_ctx {
+    struct resp_ctx *rctx;
+    int fd_limit;
+    char *socket_path;
+
+    struct kcm_resp_ctx *kcm_data;
+};
+
+int kcm_connection_setup(struct cli_ctx *cctx);
+
+#endif /* __KCMSRV_PVT_H__ */
diff --git a/src/sysv/systemd/sssd-kcm.service.in b/src/sysv/systemd/sssd-kcm.service.in
new file mode 100644
index 0000000000000000000000000000000000000000..1e2bee12dc3bedd17d41b86f91c9b2b52d985c40
--- /dev/null
+++ b/src/sysv/systemd/sssd-kcm.service.in
@@ -0,0 +1,9 @@
+[Unit]
+Description=SSSD Kerberos Cache Manager
+Documentation=man:sssd-kcm(5)
+
+[Install]
+Also=sssd-kcm.socket
+
+[Service]
+ExecStart=@libexecdir@/sssd/sssd_kcm --uid 0 --gid 0 --debug-to-files
diff --git a/src/sysv/systemd/sssd-kcm.socket.in b/src/sysv/systemd/sssd-kcm.socket.in
new file mode 100644
index 0000000000000000000000000000000000000000..80ec1c0c8f190e83de0b603df8e90aa49d2ec181
--- /dev/null
+++ b/src/sysv/systemd/sssd-kcm.socket.in
@@ -0,0 +1,10 @@
+[Unit]
+Description=SSSD Secrets Service responder socket
+Documentation=man:sssd-kcm(8)
+Requires=sssd-secrets.socket
+
+[Socket]
+ListenStream=@localstatedir@/run/.heim_org.h5l.kcm-socket
+
+[Install]
+WantedBy=sockets.target
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 17388c997db5315c2491af1021e75aff07632488..23cfdf9c6116a2c8e569a041e8289b65a112fd08 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -40,6 +40,7 @@ struct err_string error_to_str[] = {
     { "Credentials are expired, old ccache was removed" }, /* ERR_CREDS_EXPIRED_CCACHE */
     { "Failure setting user credentials"}, /* ERR_CREDS_INVALID */
     { "No cached credentials available" }, /* ERR_NO_CACHED_CREDS */
+    { "No matching credentials found" }, /* ERR_NO_MATCHING_CREDS */
     { "Cached credentials are expired" }, /* ERR_CACHED_CREDS_EXPIRED */
     { "Authentication Denied" }, /* ERR_AUTH_DENIED */
     { "Authentication Failed" }, /* ERR_AUTH_FAILED */
@@ -104,6 +105,10 @@ struct err_string error_to_str[] = {
     { "The secret payload size is too large" }, /* ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE */
     { "No authentication methode available" }, /* ERR_NO_AUTH_METHOD_AVAILABLE */
     { "Smartcard authentication not supported" }, /* ERR_SC_AUTH_NOT_SUPPORTED */
+    { "Malformed input KCM packet" }, /* ERR_KCM_MALFORMED_IN_PKT */
+    { "KCM operation not implemented" }, /* ERR_KCM_OP_NOT_IMPLEMENTED */
+    { "End of credential cache reached" }, /* ERR_KCM_CC_END */
+    { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */
     { "ERR_LAST" } /* ERR_LAST */
 };
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 7aacad26084a3a2af6333988f07db865f6a4d299..387d481616db1ed5e22b73fae82632a582fae946 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -62,6 +62,7 @@ enum sssd_errors {
     ERR_CREDS_EXPIRED_CCACHE,
     ERR_CREDS_INVALID,
     ERR_NO_CACHED_CREDS,
+    ERR_NO_MATCHING_CREDS,
     ERR_CACHED_CREDS_EXPIRED,
     ERR_AUTH_DENIED,
     ERR_AUTH_FAILED,
@@ -126,6 +127,10 @@ enum sssd_errors {
     ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE,
     ERR_NO_AUTH_METHOD_AVAILABLE,
     ERR_SC_AUTH_NOT_SUPPORTED,
+    ERR_KCM_MALFORMED_IN_PKT,
+    ERR_KCM_OP_NOT_IMPLEMENTED,
+    ERR_KCM_CC_END,
+    ERR_KCM_WRONG_CCNAME_FORMAT,
     ERR_LAST            /* ALWAYS LAST */
 };
 
-- 
2.9.3