summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/krb5/send_to_kdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/krb5/send_to_kdc.c')
-rw-r--r--source4/heimdal/lib/krb5/send_to_kdc.c177
1 files changed, 160 insertions, 17 deletions
diff --git a/source4/heimdal/lib/krb5/send_to_kdc.c b/source4/heimdal/lib/krb5/send_to_kdc.c
index 6c70244327..c1a4df2b01 100644
--- a/source4/heimdal/lib/krb5/send_to_kdc.c
+++ b/source4/heimdal/lib/krb5/send_to_kdc.c
@@ -33,7 +33,7 @@
#include "krb5_locl.h"
-RCSID("$Id: send_to_kdc.c 19973 2007-01-17 17:19:52Z lha $");
+RCSID("$Id: send_to_kdc.c 21062 2007-06-12 17:58:57Z lha $");
struct send_to_kdc {
krb5_send_to_kdc_func func;
@@ -413,26 +413,16 @@ krb5_sendto_kdc_flags(krb5_context context,
int flags)
{
krb5_error_code ret;
- krb5_krbhst_handle handle;
- int type;
-
- if ((flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc)
- type = KRB5_KRBHST_ADMIN;
- else
- type = KRB5_KRBHST_KDC;
+ krb5_sendto_ctx ctx;
- if (send_data->length > context->large_msg_size)
- flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
-
- ret = krb5_krbhst_init_flags(context, *realm, type, flags, &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, send_data, handle, receive);
- krb5_krbhst_free(context, handle);
- if (ret == KRB5_KDC_UNREACH)
- krb5_set_error_string(context,
- "unable to reach any KDC in realm %s", *realm);
+ ret = krb5_sendto_context(context, ctx, send_data, *realm, receive);
+ krb5_sendto_ctx_free(context, ctx);
return ret;
}
@@ -458,4 +448,157 @@ krb5_set_send_to_kdc_func(krb5_context context,
return 0;
}
+struct krb5_sendto_ctx {
+ 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);
+ if (ret)
+ krb5_data_free(receive);
+ if (freectx)
+ krb5_sendto_ctx_free(context, ctx);
+ return ret;
+}
+
+krb5_error_code
+_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
+ const krb5_data *reply, int *action)
+{
+ 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;
+}