diff options
Diffstat (limited to 'crypto/heimdal/lib/krb5/send_to_kdc.c')
-rw-r--r-- | crypto/heimdal/lib/krb5/send_to_kdc.c | 261 |
1 files changed, 230 insertions, 31 deletions
diff --git a/crypto/heimdal/lib/krb5/send_to_kdc.c b/crypto/heimdal/lib/krb5/send_to_kdc.c index 94dae30..2582a61 100644 --- a/crypto/heimdal/lib/krb5/send_to_kdc.c +++ b/crypto/heimdal/lib/krb5/send_to_kdc.c @@ -33,7 +33,12 @@ #include "krb5_locl.h" -RCSID("$Id: send_to_kdc.c,v 1.48 2002/03/27 09:32:50 joda Exp $"); +RCSID("$Id: send_to_kdc.c 21934 2007-08-27 14:21:04Z lha $"); + +struct send_to_kdc { + krb5_send_to_kdc_func func; + void *data; +}; /* * send the data in `req' on the socket `fd' (which is datagram iff udp) @@ -78,7 +83,7 @@ recv_loop (int fd, krb5_data_free (rep); return -1; } - if(nbytes == 0) + if(nbytes <= 0) return 0; if (limit) @@ -157,6 +162,15 @@ send_and_recv_tcp(int fd, return 0; } +int +_krb5_send_and_recv_tcp(int fd, + time_t tmout, + const krb5_data *req, + krb5_data *rep) +{ + return send_and_recv_tcp(fd, tmout, req, rep); +} + /* * `send_and_recv' tailored for the HTTP protocol. */ @@ -198,6 +212,7 @@ send_and_recv_http(int fd, s[rep->length] = 0; p = strstr(s, "\r\n\r\n"); if(p == NULL) { + krb5_data_zero(rep); free(s); return -1; } @@ -205,12 +220,14 @@ send_and_recv_http(int fd, rep->data = s; rep->length -= p - s; if(rep->length < 4) { /* remove length */ + krb5_data_zero(rep); free(s); return -1; } rep->length -= 4; _krb5_get_int(p, &rep_len, 4); if (rep_len != rep->length) { + krb5_data_zero(rep); free(s); return -1; } @@ -304,28 +321,40 @@ send_via_proxy (krb5_context context, * in `receive'. */ -krb5_error_code +krb5_error_code KRB5_LIB_FUNCTION krb5_sendto (krb5_context context, const krb5_data *send_data, krb5_krbhst_handle handle, krb5_data *receive) { - krb5_error_code ret = 0; + krb5_error_code ret; int fd; int i; + krb5_data_zero(receive); + for (i = 0; i < context->max_retries; ++i) { krb5_krbhst_info *hi; while (krb5_krbhst_next(context, handle, &hi) == 0) { - int ret; struct addrinfo *ai, *a; + if (context->send_to_kdc) { + struct send_to_kdc *s = context->send_to_kdc; + + ret = (*s->func)(context, s->data, + hi, send_data, receive); + if (ret == 0 && receive->length != 0) + goto out; + continue; + } + if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) { - if (send_via_proxy (context, hi, send_data, receive)) - continue; - else + if (send_via_proxy (context, hi, send_data, receive) == 0) { + ret = 0; goto out; + } + continue; } ret = krb5_krbhst_get_addrinfo(context, hi, &ai); @@ -367,39 +396,209 @@ out: return ret; } -krb5_error_code -krb5_sendto_kdc2(krb5_context context, - const krb5_data *send_data, - const krb5_realm *realm, - krb5_data *receive, - krb5_boolean master) +krb5_error_code KRB5_LIB_FUNCTION +krb5_sendto_kdc(krb5_context context, + const krb5_data *send_data, + const krb5_realm *realm, + krb5_data *receive) { - krb5_error_code ret; - krb5_krbhst_handle handle; - int type; + return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0); +} - if (master || context->use_admin_kdc) - type = KRB5_KRBHST_ADMIN; - else - type = KRB5_KRBHST_KDC; +krb5_error_code KRB5_LIB_FUNCTION +krb5_sendto_kdc_flags(krb5_context context, + const krb5_data *send_data, + const krb5_realm *realm, + krb5_data *receive, + int flags) +{ + krb5_error_code ret; + krb5_sendto_ctx ctx; - ret = krb5_krbhst_init(context, *realm, type, &handle); + ret = krb5_sendto_ctx_alloc(context, &ctx); if (ret) return ret; + krb5_sendto_ctx_add_flags(ctx, flags); + krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL); + + ret = krb5_sendto_context(context, ctx, send_data, *realm, receive); + krb5_sendto_ctx_free(context, ctx); + return ret; +} + +krb5_error_code KRB5_LIB_FUNCTION +krb5_set_send_to_kdc_func(krb5_context context, + krb5_send_to_kdc_func func, + void *data) +{ + free(context->send_to_kdc); + if (func == NULL) { + context->send_to_kdc = NULL; + return 0; + } + + context->send_to_kdc = malloc(sizeof(*context->send_to_kdc)); + if (context->send_to_kdc == NULL) { + krb5_set_error_string(context, "Out of memory"); + return ENOMEM; + } - ret = krb5_sendto(context, send_data, handle, receive); - krb5_krbhst_free(context, handle); + context->send_to_kdc->func = func; + context->send_to_kdc->data = data; + return 0; +} + +struct krb5_sendto_ctx_data { + int flags; + int type; + krb5_sendto_ctx_func func; + void *data; +}; + +krb5_error_code KRB5_LIB_FUNCTION +krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx) +{ + *ctx = calloc(1, sizeof(**ctx)); + if (*ctx == NULL) { + krb5_set_error_string(context, "out of memory"); + return ENOMEM; + } + return 0; +} + +void KRB5_LIB_FUNCTION +krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags) +{ + ctx->flags |= flags; +} + +int KRB5_LIB_FUNCTION +krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx) +{ + return ctx->flags; +} + +void KRB5_LIB_FUNCTION +krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type) +{ + ctx->type = type; +} + + +void KRB5_LIB_FUNCTION +krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx, + krb5_sendto_ctx_func func, + void *data) +{ + ctx->func = func; + ctx->data = data; +} + +void KRB5_LIB_FUNCTION +krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + free(ctx); +} + +krb5_error_code KRB5_LIB_FUNCTION +krb5_sendto_context(krb5_context context, + krb5_sendto_ctx ctx, + const krb5_data *send_data, + const krb5_realm realm, + krb5_data *receive) +{ + krb5_error_code ret; + krb5_krbhst_handle handle = NULL; + int type, freectx = 0; + int action; + + krb5_data_zero(receive); + + if (ctx == NULL) { + freectx = 1; + ret = krb5_sendto_ctx_alloc(context, &ctx); + if (ret) + return ret; + } + + type = ctx->type; + if (type == 0) { + if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc) + type = KRB5_KRBHST_ADMIN; + else + type = KRB5_KRBHST_KDC; + } + + if (send_data->length > context->large_msg_size) + ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG; + + /* loop until we get back a appropriate response */ + + do { + action = KRB5_SENDTO_DONE; + + krb5_data_free(receive); + + if (handle == NULL) { + ret = krb5_krbhst_init_flags(context, realm, type, + ctx->flags, &handle); + if (ret) { + if (freectx) + krb5_sendto_ctx_free(context, ctx); + return ret; + } + } + + ret = krb5_sendto(context, send_data, handle, receive); + if (ret) + break; + if (ctx->func) { + ret = (*ctx->func)(context, ctx, ctx->data, receive, &action); + if (ret) + break; + } + if (action != KRB5_SENDTO_CONTINUE) { + krb5_krbhst_free(context, handle); + handle = NULL; + } + } while (action != KRB5_SENDTO_DONE); + if (handle) + krb5_krbhst_free(context, handle); if (ret == KRB5_KDC_UNREACH) - krb5_set_error_string(context, - "unable to reach any KDC in realm %s", *realm); + krb5_set_error_string(context, + "unable to reach any KDC in realm %s", realm); + if (ret) + krb5_data_free(receive); + if (freectx) + krb5_sendto_ctx_free(context, ctx); return ret; } krb5_error_code -krb5_sendto_kdc(krb5_context context, - const krb5_data *send_data, - const krb5_realm *realm, - krb5_data *receive) +_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data, + const krb5_data *reply, int *action) { - return krb5_sendto_kdc2(context, send_data, realm, receive, FALSE); + krb5_error_code ret; + KRB_ERROR error; + + if(krb5_rd_error(context, reply, &error)) + return 0; + + ret = krb5_error_from_rd_error(context, &error, NULL); + krb5_free_error_contents(context, &error); + + switch(ret) { + case KRB5KRB_ERR_RESPONSE_TOO_BIG: { + if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG) + break; + krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG); + *action = KRB5_SENDTO_RESTART; + break; + } + case KRB5KDC_ERR_SVC_UNAVAILABLE: + *action = KRB5_SENDTO_CONTINUE; + break; + } + return 0; } |