Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add support for krb5 1.12's OTP-Over-RADIUS (freeipa#3556)
MIT krb5 1.12 is adding support for OTP tokens over RADIUS. This
can be accomplished in a zero configuration environment via a
well known UNIX domain socket. This patch adds a schema
(70ipaotp.ldif) which provides OTP support to FreeIPA and a
daemon (ipa-otpd) that listens for krb5 OTP requests. However,
this commit does not provide a UI for managing tokens.
  • Loading branch information
Nathaniel McCallum committed Apr 9, 2013
1 parent 6ff20ca commit 0d126d4
Show file tree
Hide file tree
Showing 19 changed files with 1,921 additions and 61 deletions.
1 change: 1 addition & 0 deletions daemons/Makefile.am
Expand Up @@ -16,6 +16,7 @@ SUBDIRS = \
ipa-kdb \
ipa-slapi-plugins \
ipa-sam \
ipa-otpd \
$(NULL)

DISTCLEANFILES = \
Expand Down
97 changes: 37 additions & 60 deletions daemons/configure.ac
Expand Up @@ -79,63 +79,17 @@ dnl ---------------------------------------------------------------------------
dnl - Check for KRB5
dnl ---------------------------------------------------------------------------

KRB5_LIBS=
AC_CHECK_HEADER(krb5.h, [], [AC_MSG_ERROR([krb5.h not found])])

krb5_impl=mit

if test "x$ac_cv_header_krb5_h" = "xyes" ; then
dnl lazy check for Heimdal Kerberos
AC_CHECK_HEADERS(heim_err.h)
if test $ac_cv_header_heim_err_h = yes ; then
krb5_impl=heimdal
else
krb5_impl=mit
fi

if test "x$krb5_impl" = "xmit"; then
AC_CHECK_LIB(k5crypto, main,
[krb5crypto=k5crypto],
[krb5crypto=crypto])

AC_CHECK_LIB(krb5, main,
[have_krb5=yes
KRB5_LIBS="-lkrb5 -l$krb5crypto -lcom_err"],
[have_krb5=no],
[-l$krb5crypto -lcom_err])

elif test "x$krb5_impl" = "xheimdal"; then
AC_CHECK_LIB(des, main,
[krb5crypto=des],
[krb5crypto=crypto])

AC_CHECK_LIB(krb5, main,
[have_krb5=yes
KRB5_LIBS="-lkrb5 -l$krb5crypto -lasn1 -lroken -lcom_err"],
[have_krb5=no],
[-l$krb5crypto -lasn1 -lroken -lcom_err])

AC_DEFINE(HAVE_HEIMDAL_KERBEROS, 1,
[define if you have HEIMDAL Kerberos])

else
have_krb5=no
AC_MSG_WARN([Unrecognized Kerberos5 Implementation])
fi

if test "x$have_krb5" = "xyes" ; then
ol_link_krb5=yes

AC_DEFINE(HAVE_KRB5, 1,
[define if you have Kerberos V])

else
AC_MSG_ERROR([Required Kerberos 5 support not available])
fi

fi

AC_CHECK_HEADER(krad.h, [], [AC_MSG_ERROR([krad.h not found])])
AC_CHECK_LIB(krb5, main, [], [AC_MSG_ERROR([libkrb5 not found])])
AC_CHECK_LIB(k5crypto, main, [krb5crypto=k5crypto], [krb5crypto=crypto])
AC_CHECK_LIB(krad, main, [], [AC_MSG_ERROR([libkrad not found])])
KRB5_LIBS="-lkrb5 -l$krb5crypto -lcom_err"
KRAD_LIBS="-lkrad"
krb5kdcdir="${localstatedir}/kerberos/krb5kdc"
AC_SUBST(KRB5_LIBS)
AC_SUBST(KRAD_LIBS)
AC_SUBST(krb5kdcdir)

dnl ---------------------------------------------------------------------------
dnl - Check for Mozilla LDAP and OpenLDAP SDK
Expand Down Expand Up @@ -252,6 +206,11 @@ AC_CHECK_LIB([wbclient],
[$SAMBA40EXTRA_LIBPATH])
AC_SUBST(WBCLIENT_LIBS)

dnl ---------------------------------------------------------------------------
dnl Check for libverto
dnl ---------------------------------------------------------------------------
PKG_CHECK_MODULES([LIBVERTO], [libverto])

dnl ---------------------------------------------------------------------------
dnl - Check for check unit test framework http://check.sourceforge.net/
dnl ---------------------------------------------------------------------------
Expand All @@ -268,6 +227,20 @@ PKG_CHECK_MODULES([DIRSRV], [dirsrv >= 1.3.0])
dnl -- sss_idmap is needed by the extdom exop --
PKG_CHECK_MODULES([SSSIDMAP], [sss_idmap])

dnl ---------------------------------------------------------------------------
dnl - Check for systemd unit directory
dnl ---------------------------------------------------------------------------
PKG_CHECK_EXISTS([systemd], [], [AC_MSG_ERROR([systemd not found])])
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])

dnl ---------------------------------------------------------------------------
dnl - Check for program paths
dnl ---------------------------------------------------------------------------
AC_PATH_PROG(UNLINK, unlink, [AC_MSG_ERROR([unlink not found])])

dnl ---------------------------------------------------------------------------
dnl - Set the data install directory since we don't use pkgdatadir
dnl ---------------------------------------------------------------------------
Expand Down Expand Up @@ -332,6 +305,7 @@ AC_CONFIG_FILES([
Makefile
ipa-kdb/Makefile
ipa-sam/Makefile
ipa-otpd/Makefile
ipa-slapi-plugins/Makefile
ipa-slapi-plugins/ipa-cldap/Makefile
ipa-slapi-plugins/ipa-enrollment/Makefile
Expand All @@ -352,19 +326,22 @@ echo "
IPA Server $VERSION
========================

prefix: ${prefix}
exec_prefix: ${exec_prefix}
prefix: ${prefix}
exec_prefix: ${exec_prefix}
libdir: ${libdir}
bindir: ${bindir}
sbindir: ${sbindir}
sysconfdir: ${sysconfdir}
localstatedir: ${localstatedir}
datadir: ${datadir}
source code location: ${srcdir}
compiler: ${CC}
cflags: ${CFLAGS}
krb5kdcdir: ${krb5kdcdir}
systemdsystemunitdir: ${systemdsystemunitdir}
source code location: ${srcdir}
compiler: ${CC}
cflags: ${CFLAGS}
LDAP libs: ${LDAP_LIBS}
KRB5 libs: ${KRB5_LIBS}
KRAD libs: ${KRAD_LIBS}
OpenSSL libs: ${SSL_LIBS}
Maintainer mode: ${USE_MAINTAINER_MODE}
"
21 changes: 21 additions & 0 deletions daemons/ipa-otpd/Makefile.am
@@ -0,0 +1,21 @@
CFLAGS := $(CFLAGS) @LDAP_CFLAGS@ @LIBVERTO_CFLAGS@
LDFLAGS := $(LDFLAGS) @LDAP_LIBS@ @LIBVERTO_LIBS@ @KRB5_LIBS@ @KRAD_LIBS@

noinst_HEADERS = internal.h
libexec_PROGRAMS = ipa-otpd
dist_noinst_DATA = ipa-otpd.socket.in ipa-otpd@.service.in test.py
systemdsystemunit_DATA = ipa-otpd.socket ipa-otpd@.service

ipa_otpd_SOURCES = bind.c forward.c main.c parse.c query.c queue.c stdio.c

%.socket: %.socket.in
@sed -e 's|@krb5kdcdir[@]|$(krb5kdcdir)|g' \
-e 's|@UNLINK[@]|@UNLINK@|g' \
$< > $@

%.service: %.service.in
@sed -e 's|@libexecdir[@]|$(libexecdir)|g' \
-e 's|@sysconfdir[@]|$(sysconfdir)|g' \
$< > $@

CLEANFILES = $(systemdsystemunit_DATA)
147 changes: 147 additions & 0 deletions daemons/ipa-otpd/bind.c
@@ -0,0 +1,147 @@
/*
* FreeIPA 2FA companion daemon
*
* Authors: Nathaniel McCallum <npmccallum@redhat.com>
*
* Copyright (C) 2013 Nathaniel McCallum, 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 <http://www.gnu.org/licenses/>.
*/

/*
* This file takes requests from query.c and performs an LDAP bind on behalf
* of the user. The results are placed in the stdout queue (stdio.c).
*/

#include "internal.h"

static void
on_bind_writable(verto_ctx *vctx, verto_ev *ev)
{
queue *push = &ctx.stdio.responses;
const krb5_data *data;
struct berval cred;
item *itm;
int i;
(void)vctx;

itm = queue_pop(&ctx.bind.requests);
if (itm == NULL) {
verto_set_flags(ctx.bind.io, VERTO_EV_FLAG_PERSIST |
VERTO_EV_FLAG_IO_ERROR |
VERTO_EV_FLAG_IO_READ);
return;
}

if (itm->user.dn == NULL)
goto error;

data = krad_packet_get_attr(itm->req,
krad_attr_name2num("User-Password"), 0);
if (data == NULL)
goto error;

cred.bv_val = data->data;
cred.bv_len = data->length;
i = ldap_sasl_bind(verto_get_private(ev), itm->user.dn, LDAP_SASL_SIMPLE,
&cred, NULL, NULL, &itm->msgid);
if (i != LDAP_SUCCESS) {
log_err(errno, "Unable to initiate bind: %s", ldap_err2string(i));
verto_break(ctx.vctx);
ctx.exitstatus = 1;
}

log_req(itm->req, "bind start: %s", itm->user.dn);
push = &ctx.bind.responses;

error:
queue_push(push, itm);
}

static void
on_bind_readable(verto_ctx *vctx, verto_ev *ev)
{
const char *errstr = "error";
LDAPMessage *results;
item *itm = NULL;
int i, rslt;
(void)vctx;

rslt = ldap_result(verto_get_private(ev), LDAP_RES_ANY, 0, NULL, &results);
if (rslt != LDAP_RES_BIND) {
if (rslt <= 0)
results = NULL;
ldap_msgfree(results);
return;
}

itm = queue_pop_msgid(&ctx.bind.responses, ldap_msgid(results));
if (itm == NULL) {
ldap_msgfree(results);
return;
}
itm->msgid = -1;

rslt = ldap_parse_result(verto_get_private(ev), results, &i,
NULL, NULL, NULL, NULL, 0);
if (rslt != LDAP_SUCCESS) {
errstr = ldap_err2string(rslt);
goto error;
}

rslt = i;
if (rslt != LDAP_SUCCESS) {
errstr = ldap_err2string(rslt);
goto error;
}

itm->sent = 0;
i = krad_packet_new_response(ctx.kctx, SECRET,
krad_code_name2num("Access-Accept"),
NULL, itm->req, &itm->rsp);
if (i != 0) {
errstr = krb5_get_error_message(ctx.kctx, i);
goto error;
}

error:
if (itm != NULL)
log_req(itm->req, "bind end: %s",
itm->rsp != NULL ? "success" : errstr);

ldap_msgfree(results);
queue_push(&ctx.stdio.responses, itm);
verto_set_flags(ctx.stdio.writer, VERTO_EV_FLAG_PERSIST |
VERTO_EV_FLAG_IO_ERROR |
VERTO_EV_FLAG_IO_READ |
VERTO_EV_FLAG_IO_WRITE);
}

void
on_bind_io(verto_ctx *vctx, verto_ev *ev)
{
verto_ev_flag flags;

flags = verto_get_fd_state(ev);
if (flags & VERTO_EV_FLAG_IO_WRITE)
on_bind_writable(vctx, ev);
if (flags & VERTO_EV_FLAG_IO_READ)
on_bind_readable(vctx, ev);
if (flags & VERTO_EV_FLAG_IO_ERROR) {
log_err(EIO, "IO error received on bind socket");
verto_break(ctx.vctx);
ctx.exitstatus = 1;
}
}

0 comments on commit 0d126d4

Please sign in to comment.