freeipa/daemons/ipa-otpd/bind.c
Nathaniel McCallum 0756ce7d53 Properly handle LDAP socket closures in ipa-otpd
In at least one case, when an LDAP socket closes, a read event is fired
rather than an error event. Without this patch, ipa-otpd silently
ignores this event and enters a state where all bind auths fail.

To remedy this problem, we pass error events along the same path as read
events. Should the actual read fail, we exit.

https://bugzilla.redhat.com/show_bug.cgi?id=1377858
https://fedorahosted.org/freeipa/ticket/6368

Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
2016-10-06 10:35:25 +02:00

146 lines
4.4 KiB
C

/*
* 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"
#include "../ipa-slapi-plugins/ipa-pwd-extop/otpctrl.h"
static void on_bind_writable(verto_ctx *vctx, verto_ev *ev)
{
LDAPControl control = { OTP_REQUIRED_OID, {}, true };
LDAPControl *ctrls[] = { &control, NULL };
struct otpd_queue *push = &ctx.stdio.responses;
const krb5_data *data;
struct berval cred;
struct otpd_queue_item *item;
int i;
(void)vctx;
item = otpd_queue_pop(&ctx.bind.requests);
if (item == NULL) {
verto_set_flags(ctx.bind.io, VERTO_EV_FLAG_PERSIST |
VERTO_EV_FLAG_IO_ERROR |
VERTO_EV_FLAG_IO_READ);
return;
}
if (item->user.dn == NULL)
goto error;
data = krad_packet_get_attr(item->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), item->user.dn, LDAP_SASL_SIMPLE,
&cred, ctrls, NULL, &item->msgid);
if (i != LDAP_SUCCESS) {
otpd_log_err(errno, "Unable to initiate bind: %s", ldap_err2string(i));
verto_break(ctx.vctx);
ctx.exitstatus = 1;
}
otpd_log_req(item->req, "bind start: %s", item->user.dn);
push = &ctx.bind.responses;
error:
otpd_queue_push(push, item);
}
static void on_bind_readable(verto_ctx *vctx, verto_ev *ev)
{
const char *errstr = "error";
LDAPMessage *results;
struct otpd_queue_item *item = 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);
otpd_log_err(EIO, "IO error received on bind socket");
verto_break(ctx.vctx);
ctx.exitstatus = 1;
return;
}
item = otpd_queue_pop_msgid(&ctx.bind.responses, ldap_msgid(results));
if (item == NULL) {
ldap_msgfree(results);
return;
}
item->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;
}
item->sent = 0;
i = krad_packet_new_response(ctx.kctx, SECRET,
krad_code_name2num("Access-Accept"),
NULL, item->req, &item->rsp);
if (i != 0) {
errstr = krb5_get_error_message(ctx.kctx, i);
goto error;
}
error:
if (item != NULL)
otpd_log_req(item->req, "bind end: %s",
item->rsp != NULL ? "success" : errstr);
ldap_msgfree(results);
otpd_queue_push(&ctx.stdio.responses, item);
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 otpd_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 | VERTO_EV_FLAG_IO_ERROR))
on_bind_readable(vctx, ev);
}