diff options
Diffstat (limited to 'daemons/ipa-otpd/stdio.c')
-rw-r--r-- | daemons/ipa-otpd/stdio.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/daemons/ipa-otpd/stdio.c b/daemons/ipa-otpd/stdio.c new file mode 100644 index 000000000..ac51c78d3 --- /dev/null +++ b/daemons/ipa-otpd/stdio.c @@ -0,0 +1,205 @@ +/* + * 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 reads and writes RADIUS packets on STDIN/STDOUT. + * + * Incoming requests are placed into a "query" queue to look up the user's + * configuration from LDAP (query.c). + */ + +#include "internal.h" + +static const struct otpd_queue *const queues[] = { + &ctx.stdio.responses, + &ctx.query.requests, + &ctx.query.responses, + &ctx.bind.requests, + &ctx.bind.responses, + NULL +}; + +/* Read a RADIUS request from stdin. */ +void otpd_on_stdin_readable(verto_ctx *vctx, verto_ev *ev) +{ + static char _buffer[KRAD_PACKET_SIZE_MAX]; + static krb5_data buffer = { .data = _buffer, .length = 0 }; + (void)vctx; + + const krad_packet *dup; + const krb5_data *data; + struct otpd_queue_iter *iter; + struct otpd_queue_item *item; + krad_packet *req; + ssize_t pktlen; + int i; + + pktlen = krad_packet_bytes_needed(&buffer); + if (pktlen < 0) { + otpd_log_err(EBADMSG, "Received a malformed packet"); + goto shutdown; + } + + /* Read the item. */ + i = read(verto_get_fd(ev), buffer.data + buffer.length, pktlen); + if (i < 1) { + /* On EOF, shutdown gracefully. */ + if (i == 0) { + fprintf(stderr, "Socket closed, shutting down...\n"); + verto_break(ctx.vctx); + return; + } + + if (errno != EAGAIN && errno != EINTR) { + otpd_log_err(errno, "Error receiving packet"); + goto shutdown; + } + + return; + } + + /* If we have a partial read or just the header, try again. */ + buffer.length += i; + pktlen = krad_packet_bytes_needed(&buffer); + if (pktlen > 0) + return; + + /* Create the iterator. */ + i = otpd_queue_iter_new(queues, &iter); + if (i != 0) { + otpd_log_err(i, "Unable to create iterator"); + goto shutdown; + } + + /* Decode the item. */ + i = krad_packet_decode_request(ctx.kctx, SECRET, &buffer, + otpd_queue_iter_func, iter, &dup, &req); + buffer.length = 0; + if (i == EAGAIN) + return; + else if (i != 0) { + otpd_log_err(i, "Unable to decode item"); + goto shutdown; + } + + /* Drop duplicate requests. */ + if (dup != NULL) { + krad_packet_free(req); + return; + } + + /* Ensure the packet has the User-Name attribute. */ + data = krad_packet_get_attr(req, krad_attr_name2num("User-Name"), 0); + if (data == NULL) { + krad_packet_free(req); + return; + } + + /* Create the new queue item. */ + i = otpd_queue_item_new(req, &item); + if (i != 0) { + krad_packet_free(req); + return; + } + + /* Push it to the query queue. */ + otpd_queue_push(&ctx.query.requests, item); + verto_set_flags(ctx.query.io, VERTO_EV_FLAG_PERSIST | + VERTO_EV_FLAG_IO_ERROR | + VERTO_EV_FLAG_IO_READ | + VERTO_EV_FLAG_IO_WRITE); + + otpd_log_req(req, "request received"); + return; + +shutdown: + verto_break(ctx.vctx); + ctx.exitstatus = 1; +} + +/* Send a RADIUS response to stdout. */ +void otpd_on_stdout_writable(verto_ctx *vctx, verto_ev *ev) +{ + const krb5_data *data; + struct otpd_queue_item *item; + int i; + (void)vctx; + + item = otpd_queue_peek(&ctx.stdio.responses); + if (item == NULL) { + verto_set_flags(ctx.stdio.writer, VERTO_EV_FLAG_PERSIST | + VERTO_EV_FLAG_IO_ERROR | + VERTO_EV_FLAG_IO_READ); + return; + } + + /* If no response has been generated thus far, send Access-Reject. */ + if (item->rsp == NULL) { + item->sent = 0; + i = krad_packet_new_response(ctx.kctx, SECRET, + krad_code_name2num("Access-Reject"), + NULL, item->req, &item->rsp); + if (i != 0) { + otpd_log_err(errno, "Unable to craft response"); + goto shutdown; + } + } + + /* Send the packet. */ + data = krad_packet_encode(item->rsp); + i = write(verto_get_fd(ev), data->data + item->sent, + data->length - item->sent); + if (i < 0) { + switch (errno) { +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || EAGAIN - EWOULDBLOCK != 0) + case EWOULDBLOCK: +#endif +#if defined(EAGAIN) + case EAGAIN: +#endif + case ENOBUFS: + case EINTR: + /* In this case, we just need to try again. */ + return; + default: + /* Unrecoverable. */ + break; + } + + otpd_log_err(errno, "Error writing to stdout!"); + goto shutdown; + } + + /* If the packet was completely sent, free the response. */ + item->sent += i; + if (item->sent == data->length) { + otpd_log_req(item->req, "response sent: %s", + krad_code_num2name(krad_packet_get_code(item->rsp))); + otpd_queue_item_free(otpd_queue_pop(&ctx.stdio.responses)); + } + + return; + +shutdown: + verto_break(ctx.vctx); + ctx.exitstatus = 1; +} |