#include "k5-int.h"
#include <syslog.h>
#include "kdc_util.h"
#include "extern.h"
#include "adm_proto.h"
#include "realm_data.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
static krb5_error_code make_too_big_error(kdc_realm_t *realm, krb5_data **out);
struct dispatch_state {
loop_respond_fn respond;
void *arg;
krb5_data *request;
int is_tcp;
kdc_realm_t *active_realm;
krb5_context kdc_err_context;
};
static void
finish_dispatch(struct dispatch_state *state, krb5_error_code code,
krb5_data *response)
{
loop_respond_fn oldrespond = state->respond;
void *oldarg = state->arg;
if (state->is_tcp == 0 && response &&
response->length > (unsigned int)max_dgram_reply_size) {
krb5_free_data(NULL, response);
response = NULL;
code = make_too_big_error(state->active_realm, &response);
if (code)
krb5_klog_syslog(LOG_ERR, "error constructing "
"KRB_ERR_RESPONSE_TOO_BIG error: %s",
error_message(code));
}
free(state);
(*oldrespond)(oldarg, code, response);
}
static void
finish_dispatch_cache(void *arg, krb5_error_code code, krb5_data *response)
{
struct dispatch_state *state = arg;
krb5_context kdc_err_context = state->kdc_err_context;
#ifndef NOCACHE
if (code != KRB5KDC_ERR_DISCARD)
kdc_remove_lookaside(kdc_err_context, state->request);
if (code == 0 && response != NULL)
kdc_insert_lookaside(kdc_err_context, state->request, response);
#endif
finish_dispatch(state, code, response);
}
void
dispatch(void *cb, const struct sockaddr *local_addr,
const struct sockaddr *remote_addr, krb5_data *pkt, int is_tcp,
verto_ctx *vctx, loop_respond_fn respond, void *arg)
{
krb5_error_code retval;
krb5_kdc_req *req = NULL;
krb5_data *response = NULL;
struct dispatch_state *state;
struct server_handle *handle = cb;
krb5_context kdc_err_context = handle->kdc_err_context;
state = k5alloc(sizeof(*state), &retval);
if (state == NULL) {
(*respond)(arg, retval, NULL);
return;
}
state->respond = respond;
state->arg = arg;
state->request = pkt;
state->is_tcp = is_tcp;
state->kdc_err_context = kdc_err_context;
#ifndef NOCACHE
if (kdc_check_lookaside(kdc_err_context, pkt, &response)) {
const char *name = 0;
char buf[128];
k5_print_addr(remote_addr, buf, sizeof(buf));
if (name == 0)
name = "[unknown address type]";
if (response)
krb5_klog_syslog(LOG_INFO,
"DISPATCH: repeated (retransmitted?) request "
"from %s, resending previous response", name);
else
krb5_klog_syslog(LOG_INFO,
"DISPATCH: repeated (retransmitted?) request "
"from %s during request processing, dropping "
"repeated request", name);
finish_dispatch(state, response ? 0 : KRB5KDC_ERR_DISCARD, response);
return;
}
kdc_insert_lookaside(kdc_err_context, pkt, NULL);
#endif
if (krb5_is_tgs_req(pkt))
retval = decode_krb5_tgs_req(pkt, &req);
else if (krb5_is_as_req(pkt))
retval = decode_krb5_as_req(pkt, &req);
else
retval = KRB5KRB_AP_ERR_MSG_TYPE;
if (retval)
goto done;
state->active_realm = setup_server_realm(handle, req->server);
if (state->active_realm == NULL) {
retval = KRB5KDC_ERR_WRONG_REALM;
goto done;
}
if (krb5_is_tgs_req(pkt)) {
retval = process_tgs_req(req, pkt, remote_addr, state->active_realm,
&response);
req = NULL;
} else if (krb5_is_as_req(pkt)) {
process_as_req(req, pkt, local_addr, remote_addr, state->active_realm,
vctx, finish_dispatch_cache, state);
return;
}
done:
krb5_free_kdc_req(kdc_err_context, req);
finish_dispatch_cache(state, retval, response);
}
static krb5_error_code
make_too_big_error(kdc_realm_t *realm, krb5_data **out)
{
krb5_context context = realm->realm_context;
krb5_error errpkt;
krb5_error_code retval;
krb5_data *scratch;
*out = NULL;
memset(&errpkt, 0, sizeof(errpkt));
retval = krb5_us_timeofday(context, &errpkt.stime, &errpkt.susec);
if (retval)
return retval;
errpkt.error = KRB_ERR_RESPONSE_TOO_BIG;
errpkt.server = realm->realm_tgsprinc;
errpkt.client = NULL;
errpkt.text.length = 0;
errpkt.text.data = 0;
errpkt.e_data.length = 0;
errpkt.e_data.data = 0;
scratch = malloc(sizeof(*scratch));
if (scratch == NULL)
return ENOMEM;
retval = krb5_mk_error(context, &errpkt, scratch);
if (retval) {
free(scratch);
return retval;
}
*out = scratch;
return 0;
}
krb5_context get_context(void *handle)
{
struct server_handle *sh = handle;
return sh->kdc_err_context;
}