summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/krb5
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/krb5')
-rw-r--r--source4/heimdal/lib/krb5/acache.c1033
-rw-r--r--source4/heimdal/lib/krb5/add_et_list.c58
-rw-r--r--source4/heimdal/lib/krb5/addr_families.c1484
-rw-r--r--source4/heimdal/lib/krb5/appdefault.c142
-rw-r--r--source4/heimdal/lib/krb5/asn1_glue.c64
-rw-r--r--source4/heimdal/lib/krb5/auth_context.c521
-rw-r--r--source4/heimdal/lib/krb5/build_ap_req.c76
-rw-r--r--source4/heimdal/lib/krb5/build_auth.c202
-rw-r--r--source4/heimdal/lib/krb5/cache.c1239
-rw-r--r--source4/heimdal/lib/krb5/changepw.c862
-rw-r--r--source4/heimdal/lib/krb5/codec.c196
-rw-r--r--source4/heimdal/lib/krb5/config_file.c773
-rw-r--r--source4/heimdal/lib/krb5/config_file_netinfo.c180
-rw-r--r--source4/heimdal/lib/krb5/constants.c43
-rw-r--r--source4/heimdal/lib/krb5/context.c1066
-rw-r--r--source4/heimdal/lib/krb5/convert_creds.c204
-rw-r--r--source4/heimdal/lib/krb5/copy_host_realm.c77
-rw-r--r--source4/heimdal/lib/krb5/crc.c71
-rw-r--r--source4/heimdal/lib/krb5/creds.c273
-rw-r--r--source4/heimdal/lib/krb5/crypto.c4603
-rw-r--r--source4/heimdal/lib/krb5/data.c224
-rw-r--r--source4/heimdal/lib/krb5/eai_to_heim_errno.c114
-rw-r--r--source4/heimdal/lib/krb5/error_string.c251
-rw-r--r--source4/heimdal/lib/krb5/expand_hostname.c161
-rw-r--r--source4/heimdal/lib/krb5/fcache.c947
-rw-r--r--source4/heimdal/lib/krb5/free.c53
-rw-r--r--source4/heimdal/lib/krb5/free_host_realm.c54
-rw-r--r--source4/heimdal/lib/krb5/generate_seq_number.c62
-rw-r--r--source4/heimdal/lib/krb5/generate_subkey.c72
-rw-r--r--source4/heimdal/lib/krb5/get_cred.c1423
-rw-r--r--source4/heimdal/lib/krb5/get_default_principal.c115
-rw-r--r--source4/heimdal/lib/krb5/get_default_realm.c84
-rw-r--r--source4/heimdal/lib/krb5/get_for_creds.c459
-rw-r--r--source4/heimdal/lib/krb5/get_host_realm.c259
-rw-r--r--source4/heimdal/lib/krb5/get_in_tkt.c1069
-rw-r--r--source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c99
-rw-r--r--source4/heimdal/lib/krb5/get_port.c54
-rw-r--r--source4/heimdal/lib/krb5/heim_err.et44
-rw-r--r--source4/heimdal/lib/krb5/heim_threads.h175
-rw-r--r--source4/heimdal/lib/krb5/init_creds.c442
-rw-r--r--source4/heimdal/lib/krb5/init_creds_pw.c1602
-rw-r--r--source4/heimdal/lib/krb5/k524_err.et20
-rw-r--r--source4/heimdal/lib/krb5/kcm.c1148
-rw-r--r--source4/heimdal/lib/krb5/keyblock.c134
-rw-r--r--source4/heimdal/lib/krb5/keytab.c533
-rw-r--r--source4/heimdal/lib/krb5/keytab_any.c256
-rw-r--r--source4/heimdal/lib/krb5/keytab_file.c702
-rw-r--r--source4/heimdal/lib/krb5/keytab_keyfile.c422
-rw-r--r--source4/heimdal/lib/krb5/keytab_memory.c234
-rw-r--r--source4/heimdal/lib/krb5/krb5-v4compat.h132
-rw-r--r--source4/heimdal/lib/krb5/krb5.h810
-rw-r--r--source4/heimdal/lib/krb5/krb5_ccapi.h230
-rw-r--r--source4/heimdal/lib/krb5/krb5_err.et268
-rw-r--r--source4/heimdal/lib/krb5/krb5_locl.h299
-rw-r--r--source4/heimdal/lib/krb5/krb_err.et63
-rw-r--r--source4/heimdal/lib/krb5/krbhst.c1014
-rw-r--r--source4/heimdal/lib/krb5/locate_plugin.h66
-rw-r--r--source4/heimdal/lib/krb5/log.c473
-rw-r--r--source4/heimdal/lib/krb5/mcache.c478
-rw-r--r--source4/heimdal/lib/krb5/misc.c86
-rw-r--r--source4/heimdal/lib/krb5/mit_glue.c369
-rw-r--r--source4/heimdal/lib/krb5/mk_error.c92
-rw-r--r--source4/heimdal/lib/krb5/mk_priv.c155
-rw-r--r--source4/heimdal/lib/krb5/mk_rep.c126
-rw-r--r--source4/heimdal/lib/krb5/mk_req.c116
-rw-r--r--source4/heimdal/lib/krb5/mk_req_ext.c165
-rw-r--r--source4/heimdal/lib/krb5/n-fold.c137
-rw-r--r--source4/heimdal/lib/krb5/pac.c1026
-rw-r--r--source4/heimdal/lib/krb5/padata.c66
-rw-r--r--source4/heimdal/lib/krb5/pkinit.c2097
-rw-r--r--source4/heimdal/lib/krb5/plugin.c265
-rw-r--r--source4/heimdal/lib/krb5/principal.c1328
-rw-r--r--source4/heimdal/lib/krb5/prompter_posix.c74
-rw-r--r--source4/heimdal/lib/krb5/rd_cred.c341
-rw-r--r--source4/heimdal/lib/krb5/rd_error.c123
-rw-r--r--source4/heimdal/lib/krb5/rd_priv.c186
-rw-r--r--source4/heimdal/lib/krb5/rd_rep.c124
-rw-r--r--source4/heimdal/lib/krb5/rd_req.c891
-rw-r--r--source4/heimdal/lib/krb5/replay.c317
-rw-r--r--source4/heimdal/lib/krb5/send_to_kdc.c654
-rw-r--r--source4/heimdal/lib/krb5/send_to_kdc_plugin.h58
-rw-r--r--source4/heimdal/lib/krb5/set_default_realm.c90
-rw-r--r--source4/heimdal/lib/krb5/store-int.h47
-rw-r--r--source4/heimdal/lib/krb5/store.c1035
-rw-r--r--source4/heimdal/lib/krb5/store_emem.c147
-rw-r--r--source4/heimdal/lib/krb5/store_fd.c98
-rw-r--r--source4/heimdal/lib/krb5/store_mem.c150
-rw-r--r--source4/heimdal/lib/krb5/ticket.c274
-rw-r--r--source4/heimdal/lib/krb5/time.c131
-rw-r--r--source4/heimdal/lib/krb5/transited.c505
-rw-r--r--source4/heimdal/lib/krb5/v4_glue.c940
-rw-r--r--source4/heimdal/lib/krb5/version.c43
-rw-r--r--source4/heimdal/lib/krb5/warn.c211
93 files changed, 40679 insertions, 0 deletions
diff --git a/source4/heimdal/lib/krb5/acache.c b/source4/heimdal/lib/krb5/acache.c
new file mode 100644
index 0000000000..fb38abedfd
--- /dev/null
+++ b/source4/heimdal/lib/krb5/acache.c
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <krb5_ccapi.h>
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+RCSID("$Id$");
+
+/* XXX should we fetch these for each open ? */
+static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static cc_initialize_func init_func;
+
+#ifdef HAVE_DLOPEN
+static void *cc_handle;
+#endif
+
+typedef struct krb5_acc {
+ char *cache_name;
+ cc_context_t context;
+ cc_ccache_t ccache;
+} krb5_acc;
+
+static krb5_error_code acc_close(krb5_context, krb5_ccache);
+
+#define ACACHE(X) ((krb5_acc *)(X)->data.data)
+
+static const struct {
+ cc_int32 error;
+ krb5_error_code ret;
+} cc_errors[] = {
+ { ccErrBadName, KRB5_CC_BADNAME },
+ { ccErrCredentialsNotFound, KRB5_CC_NOTFOUND },
+ { ccErrCCacheNotFound, KRB5_FCC_NOFILE },
+ { ccErrContextNotFound, KRB5_CC_NOTFOUND },
+ { ccIteratorEnd, KRB5_CC_END },
+ { ccErrNoMem, KRB5_CC_NOMEM },
+ { ccErrServerUnavailable, KRB5_CC_NOSUPP },
+ { ccErrInvalidCCache, KRB5_CC_BADNAME },
+ { ccNoError, 0 }
+};
+
+static krb5_error_code
+translate_cc_error(krb5_context context, cc_int32 error)
+{
+ int i;
+ krb5_clear_error_string(context);
+ for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
+ if (cc_errors[i].error == error)
+ return cc_errors[i].ret;
+ return KRB5_FCC_INTERNAL;
+}
+
+static krb5_error_code
+init_ccapi(krb5_context context)
+{
+ const char *lib;
+
+ HEIMDAL_MUTEX_lock(&acc_mutex);
+ if (init_func) {
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ krb5_clear_error_string(context);
+ return 0;
+ }
+
+ lib = krb5_config_get_string(context, NULL,
+ "libdefaults", "ccapi_library",
+ NULL);
+ if (lib == NULL) {
+#ifdef __APPLE__
+ lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
+#else
+ lib = "/usr/lib/libkrb5_cc.so";
+#endif
+ }
+
+#ifdef HAVE_DLOPEN
+
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+
+ cc_handle = dlopen(lib, RTLD_LAZY);
+ if (cc_handle == NULL) {
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ "Failed to load %s", lib);
+ return KRB5_CC_NOSUPP;
+ }
+
+ init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ if (init_func == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ "Failed to find cc_initialize"
+ "in %s: %s", lib, dlerror());
+ dlclose(cc_handle);
+ return KRB5_CC_NOSUPP;
+ }
+
+ return 0;
+#else
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ krb5_set_error_message(context, KRB5_CC_NOSUPP, "no support for shared object");
+ return KRB5_CC_NOSUPP;
+#endif
+}
+
+static krb5_error_code
+make_cred_from_ccred(krb5_context context,
+ const cc_credentials_v5_t *incred,
+ krb5_creds *cred)
+{
+ krb5_error_code ret;
+ unsigned int i;
+
+ memset(cred, 0, sizeof(*cred));
+
+ ret = krb5_parse_name(context, incred->client, &cred->client);
+ if (ret)
+ goto fail;
+
+ ret = krb5_parse_name(context, incred->server, &cred->server);
+ if (ret)
+ goto fail;
+
+ cred->session.keytype = incred->keyblock.type;
+ cred->session.keyvalue.length = incred->keyblock.length;
+ cred->session.keyvalue.data = malloc(incred->keyblock.length);
+ if (cred->session.keyvalue.data == NULL)
+ goto nomem;
+ memcpy(cred->session.keyvalue.data, incred->keyblock.data,
+ incred->keyblock.length);
+
+ cred->times.authtime = incred->authtime;
+ cred->times.starttime = incred->starttime;
+ cred->times.endtime = incred->endtime;
+ cred->times.renew_till = incred->renew_till;
+
+ ret = krb5_data_copy(&cred->ticket,
+ incred->ticket.data,
+ incred->ticket.length);
+ if (ret)
+ goto nomem;
+
+ ret = krb5_data_copy(&cred->second_ticket,
+ incred->second_ticket.data,
+ incred->second_ticket.length);
+ if (ret)
+ goto nomem;
+
+ cred->authdata.val = NULL;
+ cred->authdata.len = 0;
+
+ cred->addresses.val = NULL;
+ cred->addresses.len = 0;
+
+ for (i = 0; incred->authdata && incred->authdata[i]; i++)
+ ;
+
+ if (i) {
+ cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
+ if (cred->authdata.val == NULL)
+ goto nomem;
+ cred->authdata.len = i;
+ for (i = 0; i < cred->authdata.len; i++) {
+ cred->authdata.val[i].ad_type = incred->authdata[i]->type;
+ ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
+ incred->authdata[i]->data,
+ incred->authdata[i]->length);
+ if (ret)
+ goto nomem;
+ }
+ }
+
+ for (i = 0; incred->addresses && incred->addresses[i]; i++)
+ ;
+
+ if (i) {
+ cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
+ if (cred->addresses.val == NULL)
+ goto nomem;
+ cred->addresses.len = i;
+
+ for (i = 0; i < cred->addresses.len; i++) {
+ cred->addresses.val[i].addr_type = incred->addresses[i]->type;
+ ret = krb5_data_copy(&cred->addresses.val[i].address,
+ incred->addresses[i]->data,
+ incred->addresses[i]->length);
+ if (ret)
+ goto nomem;
+ }
+ }
+
+ cred->flags.i = 0;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
+ cred->flags.b.forwardable = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
+ cred->flags.b.forwarded = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
+ cred->flags.b.proxiable = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
+ cred->flags.b.proxy = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
+ cred->flags.b.may_postdate = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
+ cred->flags.b.postdated = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
+ cred->flags.b.invalid = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
+ cred->flags.b.renewable = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
+ cred->flags.b.initial = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
+ cred->flags.b.pre_authent = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
+ cred->flags.b.hw_authent = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
+ cred->flags.b.transited_policy_checked = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
+ cred->flags.b.ok_as_delegate = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
+ cred->flags.b.anonymous = 1;
+
+ return 0;
+
+nomem:
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+
+fail:
+ krb5_free_cred_contents(context, cred);
+ return ret;
+}
+
+static void
+free_ccred(cc_credentials_v5_t *cred)
+{
+ int i;
+
+ if (cred->addresses) {
+ for (i = 0; cred->addresses[i] != 0; i++) {
+ if (cred->addresses[i]->data)
+ free(cred->addresses[i]->data);
+ free(cred->addresses[i]);
+ }
+ free(cred->addresses);
+ }
+ if (cred->server)
+ free(cred->server);
+ if (cred->client)
+ free(cred->client);
+ memset(cred, 0, sizeof(*cred));
+}
+
+static krb5_error_code
+make_ccred_from_cred(krb5_context context,
+ const krb5_creds *incred,
+ cc_credentials_v5_t *cred)
+{
+ krb5_error_code ret;
+ int i;
+
+ memset(cred, 0, sizeof(*cred));
+
+ ret = krb5_unparse_name(context, incred->client, &cred->client);
+ if (ret)
+ goto fail;
+
+ ret = krb5_unparse_name(context, incred->server, &cred->server);
+ if (ret)
+ goto fail;
+
+ cred->keyblock.type = incred->session.keytype;
+ cred->keyblock.length = incred->session.keyvalue.length;
+ cred->keyblock.data = incred->session.keyvalue.data;
+
+ cred->authtime = incred->times.authtime;
+ cred->starttime = incred->times.starttime;
+ cred->endtime = incred->times.endtime;
+ cred->renew_till = incred->times.renew_till;
+
+ cred->ticket.length = incred->ticket.length;
+ cred->ticket.data = incred->ticket.data;
+
+ cred->second_ticket.length = incred->second_ticket.length;
+ cred->second_ticket.data = incred->second_ticket.data;
+
+ /* XXX this one should also be filled in */
+ cred->authdata = NULL;
+
+ cred->addresses = calloc(incred->addresses.len + 1,
+ sizeof(cred->addresses[0]));
+ if (cred->addresses == NULL) {
+
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < incred->addresses.len; i++) {
+ cc_data *addr;
+ addr = malloc(sizeof(*addr));
+ if (addr == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ addr->type = incred->addresses.val[i].addr_type;
+ addr->length = incred->addresses.val[i].address.length;
+ addr->data = malloc(addr->length);
+ if (addr->data == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ memcpy(addr->data, incred->addresses.val[i].address.data,
+ addr->length);
+ cred->addresses[i] = addr;
+ }
+ cred->addresses[i] = NULL;
+
+ cred->ticket_flags = 0;
+ if (incred->flags.b.forwardable)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
+ if (incred->flags.b.forwarded)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
+ if (incred->flags.b.proxiable)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
+ if (incred->flags.b.proxy)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
+ if (incred->flags.b.may_postdate)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
+ if (incred->flags.b.postdated)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
+ if (incred->flags.b.invalid)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
+ if (incred->flags.b.renewable)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
+ if (incred->flags.b.initial)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
+ if (incred->flags.b.pre_authent)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
+ if (incred->flags.b.hw_authent)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
+ if (incred->flags.b.transited_policy_checked)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
+ if (incred->flags.b.ok_as_delegate)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
+ if (incred->flags.b.anonymous)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
+
+ return 0;
+
+fail:
+ free_ccred(cred);
+
+ krb5_clear_error_string(context);
+ return ret;
+}
+
+static cc_int32
+get_cc_name(krb5_acc *a)
+{
+ cc_string_t name;
+ cc_int32 error;
+
+ error = (*a->ccache->func->get_name)(a->ccache, &name);
+ if (error)
+ return error;
+
+ a->cache_name = strdup(name->data);
+ (*name->func->release)(name);
+ if (a->cache_name == NULL)
+ return ccErrNoMem;
+ return ccNoError;
+}
+
+
+static const char*
+acc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+ int32_t error;
+
+ if (a->cache_name == NULL) {
+ krb5_error_code ret;
+ krb5_principal principal;
+ char *name;
+
+ ret = _krb5_get_default_principal_local(context, &principal);
+ if (ret)
+ return NULL;
+
+ ret = krb5_unparse_name(context, principal, &name);
+ krb5_free_principal(context, principal);
+ if (ret)
+ return NULL;
+
+ error = (*a->context->func->create_new_ccache)(a->context,
+ cc_credentials_v5,
+ name,
+ &a->ccache);
+ krb5_xfree(name);
+ if (error)
+ return NULL;
+
+ error = get_cc_name(a);
+ if (error)
+ return NULL;
+ }
+
+ return a->cache_name;
+}
+
+static krb5_error_code
+acc_alloc(krb5_context context, krb5_ccache *id)
+{
+ krb5_error_code ret;
+ cc_int32 error;
+ krb5_acc *a;
+
+ ret = init_ccapi(context);
+ if (ret)
+ return ret;
+
+ ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ a = ACACHE(*id);
+
+ error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
+ if (error) {
+ krb5_data_free(&(*id)->data);
+ return translate_cc_error(context, error);
+ }
+
+ a->cache_name = NULL;
+
+ return 0;
+}
+
+static krb5_error_code
+acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ krb5_error_code ret;
+ cc_int32 error;
+ krb5_acc *a;
+
+ ret = acc_alloc(context, id);
+ if (ret)
+ return ret;
+
+ a = ACACHE(*id);
+
+ error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
+ if (error == ccNoError) {
+ error = get_cc_name(a);
+ if (error != ccNoError) {
+ acc_close(context, *id);
+ *id = NULL;
+ return translate_cc_error(context, error);
+ }
+ } else if (error == ccErrCCacheNotFound) {
+ a->ccache = NULL;
+ a->cache_name = NULL;
+ error = 0;
+ } else {
+ *id = NULL;
+ return translate_cc_error(context, error);
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+acc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_error_code ret;
+ krb5_acc *a;
+
+ ret = acc_alloc(context, id);
+ if (ret)
+ return ret;
+
+ a = ACACHE(*id);
+
+ a->ccache = NULL;
+ a->cache_name = NULL;
+
+ return 0;
+}
+
+static krb5_error_code
+acc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_acc *a = ACACHE(id);
+ krb5_error_code ret;
+ int32_t error;
+ char *name;
+
+ ret = krb5_unparse_name(context, primary_principal, &name);
+ if (ret)
+ return ret;
+
+ if (a->cache_name == NULL) {
+ error = (*a->context->func->create_new_ccache)(a->context,
+ cc_credentials_v5,
+ name,
+ &a->ccache);
+ free(name);
+ if (error == ccNoError)
+ error = get_cc_name(a);
+ } else {
+ cc_credentials_iterator_t iter;
+ cc_credentials_t ccred;
+
+ error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
+ if (error) {
+ free(name);
+ return translate_cc_error(context, error);
+ }
+
+ while (1) {
+ error = (*iter->func->next)(iter, &ccred);
+ if (error)
+ break;
+ (*a->ccache->func->remove_credentials)(a->ccache, ccred);
+ (*ccred->func->release)(ccred);
+ }
+ (*iter->func->release)(iter);
+
+ error = (*a->ccache->func->set_principal)(a->ccache,
+ cc_credentials_v5,
+ name);
+ }
+
+ return translate_cc_error(context, error);
+}
+
+static krb5_error_code
+acc_close(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+
+ if (a->ccache) {
+ (*a->ccache->func->release)(a->ccache);
+ a->ccache = NULL;
+ }
+ if (a->cache_name) {
+ free(a->cache_name);
+ a->cache_name = NULL;
+ }
+ if (a->context) {
+ (*a->context->func->release)(a->context);
+ a->context = NULL;
+ }
+ krb5_data_free(&id->data);
+ return 0;
+}
+
+static krb5_error_code
+acc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+ cc_int32 error = 0;
+
+ if (a->ccache) {
+ error = (*a->ccache->func->destroy)(a->ccache);
+ a->ccache = NULL;
+ }
+ if (a->context) {
+ error = (a->context->func->release)(a->context);
+ a->context = NULL;
+ }
+ return translate_cc_error(context, error);
+}
+
+static krb5_error_code
+acc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_acc *a = ACACHE(id);
+ cc_credentials_union cred;
+ cc_credentials_v5_t v5cred;
+ krb5_error_code ret;
+ cc_int32 error;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ "No API credential found");
+ return KRB5_CC_NOTFOUND;
+ }
+
+ cred.version = cc_credentials_v5;
+ cred.credentials.credentials_v5 = &v5cred;
+
+ ret = make_ccred_from_cred(context,
+ creds,
+ &v5cred);
+ if (ret)
+ return ret;
+
+ error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
+ if (error)
+ ret = translate_cc_error(context, error);
+
+ free_ccred(&v5cred);
+
+ return ret;
+}
+
+static krb5_error_code
+acc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_acc *a = ACACHE(id);
+ krb5_error_code ret;
+ int32_t error;
+ cc_string_t name;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ "No API credential found");
+ return KRB5_CC_NOTFOUND;
+ }
+
+ error = (*a->ccache->func->get_principal)(a->ccache,
+ cc_credentials_v5,
+ &name);
+ if (error)
+ return translate_cc_error(context, error);
+
+ ret = krb5_parse_name(context, name->data, principal);
+
+ (*name->func->release)(name);
+ return ret;
+}
+
+static krb5_error_code
+acc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ cc_credentials_iterator_t iter;
+ krb5_acc *a = ACACHE(id);
+ int32_t error;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ "No API credential found");
+ return KRB5_CC_NOTFOUND;
+ }
+
+ error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
+ if (error) {
+ krb5_clear_error_string(context);
+ return ENOENT;
+ }
+ *cursor = iter;
+ return 0;
+}
+
+
+static krb5_error_code
+acc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ cc_credentials_iterator_t iter = *cursor;
+ cc_credentials_t cred;
+ krb5_error_code ret;
+ int32_t error;
+
+ while (1) {
+ error = (*iter->func->next)(iter, &cred);
+ if (error)
+ return translate_cc_error(context, error);
+ if (cred->data->version == cc_credentials_v5)
+ break;
+ (*cred->func->release)(cred);
+ }
+
+ ret = make_cred_from_ccred(context,
+ cred->data->credentials.credentials_v5,
+ creds);
+ (*cred->func->release)(cred);
+ return ret;
+}
+
+static krb5_error_code
+acc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ cc_credentials_iterator_t iter = *cursor;
+ (*iter->func->release)(iter);
+ return 0;
+}
+
+static krb5_error_code
+acc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ cc_credentials_iterator_t iter;
+ krb5_acc *a = ACACHE(id);
+ cc_credentials_t ccred;
+ krb5_error_code ret;
+ cc_int32 error;
+ char *client, *server;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ "No API credential found");
+ return KRB5_CC_NOTFOUND;
+ }
+
+ if (cred->client) {
+ ret = krb5_unparse_name(context, cred->client, &client);
+ if (ret)
+ return ret;
+ } else
+ client = NULL;
+
+ ret = krb5_unparse_name(context, cred->server, &server);
+ if (ret) {
+ free(client);
+ return ret;
+ }
+
+ error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
+ if (error) {
+ free(server);
+ free(client);
+ return translate_cc_error(context, error);
+ }
+
+ ret = KRB5_CC_NOTFOUND;
+ while (1) {
+ cc_credentials_v5_t *v5cred;
+
+ error = (*iter->func->next)(iter, &ccred);
+ if (error)
+ break;
+
+ if (ccred->data->version != cc_credentials_v5)
+ goto next;
+
+ v5cred = ccred->data->credentials.credentials_v5;
+
+ if (client && strcmp(v5cred->client, client) != 0)
+ goto next;
+
+ if (strcmp(v5cred->server, server) != 0)
+ goto next;
+
+ (*a->ccache->func->remove_credentials)(a->ccache, ccred);
+ ret = 0;
+ next:
+ (*ccred->func->release)(ccred);
+ }
+
+ (*iter->func->release)(iter);
+
+ if (ret)
+ krb5_set_error_message(context, ret,
+ "Can't find credential %s in cache", server);
+ free(server);
+ free(client);
+
+ return ret;
+}
+
+static krb5_error_code
+acc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0;
+}
+
+static int
+acc_get_version(krb5_context context,
+ krb5_ccache id)
+{
+ return 0;
+}
+
+struct cache_iter {
+ cc_context_t context;
+ cc_ccache_iterator_t iter;
+};
+
+static krb5_error_code
+acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct cache_iter *iter;
+ krb5_error_code ret;
+ cc_int32 error;
+
+ ret = init_ccapi(context);
+ if (ret)
+ return ret;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
+ if (error) {
+ free(iter);
+ return translate_cc_error(context, error);
+ }
+
+ error = (*iter->context->func->new_ccache_iterator)(iter->context,
+ &iter->iter);
+ if (error) {
+ free(iter);
+ krb5_clear_error_string(context);
+ return ENOENT;
+ }
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code
+acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct cache_iter *iter = cursor;
+ cc_ccache_t cache;
+ krb5_acc *a;
+ krb5_error_code ret;
+ int32_t error;
+
+ error = (*iter->iter->func->next)(iter->iter, &cache);
+ if (error)
+ return translate_cc_error(context, error);
+
+ ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
+ if (ret) {
+ (*cache->func->release)(cache);
+ return ret;
+ }
+
+ ret = acc_alloc(context, id);
+ if (ret) {
+ (*cache->func->release)(cache);
+ free(*id);
+ return ret;
+ }
+
+ a = ACACHE(*id);
+ a->ccache = cache;
+
+ error = get_cc_name(a);
+ if (error) {
+ acc_close(context, *id);
+ *id = NULL;
+ return translate_cc_error(context, error);
+ }
+ return 0;
+}
+
+static krb5_error_code
+acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct cache_iter *iter = cursor;
+
+ (*iter->iter->func->release)(iter->iter);
+ iter->iter = NULL;
+ (*iter->context->func->release)(iter->context);
+ iter->context = NULL;
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code
+acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_acc *afrom = ACACHE(from);
+ krb5_acc *ato = ACACHE(to);
+ int32_t error;
+
+ if (ato->ccache == NULL) {
+ cc_string_t name;
+
+ error = (*afrom->ccache->func->get_principal)(afrom->ccache,
+ cc_credentials_v5,
+ &name);
+ if (error)
+ return translate_cc_error(context, error);
+
+ error = (*ato->context->func->create_new_ccache)(ato->context,
+ cc_credentials_v5,
+ name->data,
+ &ato->ccache);
+ (*name->func->release)(name);
+ if (error)
+ return translate_cc_error(context, error);
+ }
+
+
+ error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
+ return translate_cc_error(context, error);
+}
+
+static krb5_error_code
+acc_get_default_name(krb5_context context, char **str)
+{
+ krb5_error_code ret;
+ cc_context_t cc;
+ cc_string_t name;
+ int32_t error;
+
+ ret = init_ccapi(context);
+ if (ret)
+ return ret;
+
+ error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
+ if (error)
+ return translate_cc_error(context, error);
+
+ error = (*cc->func->get_default_ccache_name)(cc, &name);
+ if (error) {
+ (*cc->func->release)(cc);
+ return translate_cc_error(context, error);
+ }
+
+ asprintf(str, "API:%s", name->data);
+ (*name->func->release)(name);
+ (*cc->func->release)(cc);
+
+ if (*str == NULL) {
+ krb5_set_error_message(context, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+static krb5_error_code
+acc_set_default(krb5_context context, krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+ cc_int32 error;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ "No API credential found");
+ return KRB5_CC_NOTFOUND;
+ }
+
+ error = (*a->ccache->func->set_default)(a->ccache);
+ if (error)
+ return translate_cc_error(context, error);
+
+ return 0;
+}
+
+/**
+ * Variable containing the API based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "API",
+ acc_get_name,
+ acc_resolve,
+ acc_gen_new,
+ acc_initialize,
+ acc_destroy,
+ acc_close,
+ acc_store_cred,
+ NULL, /* acc_retrieve */
+ acc_get_principal,
+ acc_get_first,
+ acc_get_next,
+ acc_end_get,
+ acc_remove_cred,
+ acc_set_flags,
+ acc_get_version,
+ acc_get_cache_first,
+ acc_get_cache_next,
+ acc_end_cache_get,
+ acc_move,
+ acc_get_default_name,
+ acc_set_default
+};
diff --git a/source4/heimdal/lib/krb5/add_et_list.c b/source4/heimdal/lib/krb5/add_et_list.c
new file mode 100644
index 0000000000..e61f775eef
--- /dev/null
+++ b/source4/heimdal/lib/krb5/add_et_list.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Add a specified list of error messages to the et list in context.
+ * Call func (probably a comerr-generated function) with a pointer to
+ * the current et_list.
+ *
+ * @param context A kerberos context.
+ * @param func The generated com_err et function.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_add_et_list (krb5_context context,
+ void (*func)(struct et_list **))
+{
+ (*func)(&context->et_list);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/addr_families.c b/source4/heimdal/lib/krb5/addr_families.c
new file mode 100644
index 0000000000..dcb9a97154
--- /dev/null
+++ b/source4/heimdal/lib/krb5/addr_families.c
@@ -0,0 +1,1484 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct addr_operations {
+ int af;
+ krb5_address_type atype;
+ size_t max_sockaddr_size;
+ krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *);
+ krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *);
+ void (*addr2sockaddr)(const krb5_address *, struct sockaddr *,
+ krb5_socklen_t *sa_size, int port);
+ void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int);
+ krb5_error_code (*h_addr2addr)(const char *, krb5_address *);
+ krb5_boolean (*uninteresting)(const struct sockaddr *);
+ void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int);
+ int (*print_addr)(const krb5_address *, char *, size_t);
+ int (*parse_addr)(krb5_context, const char*, krb5_address *);
+ int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*);
+ int (*free_addr)(krb5_context, krb5_address*);
+ int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*);
+ int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long,
+ krb5_address*, krb5_address*);
+};
+
+/*
+ * AF_INET - aka IPv4 implementation
+ */
+
+static krb5_error_code
+ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
+{
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+ unsigned char buf[4];
+
+ a->addr_type = KRB5_ADDRESS_INET;
+ memcpy (buf, &sin4->sin_addr, 4);
+ return krb5_data_copy(&a->address, buf, 4);
+}
+
+static krb5_error_code
+ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port)
+{
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+
+ *port = sin4->sin_port;
+ return 0;
+}
+
+static void
+ipv4_addr2sockaddr (const krb5_address *a,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin_family = AF_INET;
+ memcpy (&tmp.sin_addr, a->address.data, 4);
+ tmp.sin_port = port;
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static void
+ipv4_h_addr2sockaddr(const char *addr,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin_family = AF_INET;
+ tmp.sin_port = port;
+ tmp.sin_addr = *((const struct in_addr *)addr);
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static krb5_error_code
+ipv4_h_addr2addr (const char *addr,
+ krb5_address *a)
+{
+ unsigned char buf[4];
+
+ a->addr_type = KRB5_ADDRESS_INET;
+ memcpy(buf, addr, 4);
+ return krb5_data_copy(&a->address, buf, 4);
+}
+
+/*
+ * Are there any addresses that should be considered `uninteresting'?
+ */
+
+static krb5_boolean
+ipv4_uninteresting (const struct sockaddr *sa)
+{
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+
+ if (sin4->sin_addr.s_addr == INADDR_ANY)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
+{
+ struct sockaddr_in tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin_family = AF_INET;
+ tmp.sin_port = port;
+ tmp.sin_addr.s_addr = INADDR_ANY;
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static int
+ipv4_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ struct in_addr ia;
+
+ memcpy (&ia, addr->address.data, 4);
+
+ return snprintf (str, len, "IPv4:%s", inet_ntoa(ia));
+}
+
+static int
+ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr)
+{
+ const char *p;
+ struct in_addr a;
+
+ p = strchr(address, ':');
+ if(p) {
+ p++;
+ if(strncasecmp(address, "ip:", p - address) != 0 &&
+ strncasecmp(address, "ip4:", p - address) != 0 &&
+ strncasecmp(address, "ipv4:", p - address) != 0 &&
+ strncasecmp(address, "inet:", p - address) != 0)
+ return -1;
+ } else
+ p = address;
+#ifdef HAVE_INET_ATON
+ if(inet_aton(p, &a) == 0)
+ return -1;
+#elif defined(HAVE_INET_ADDR)
+ a.s_addr = inet_addr(p);
+ if(a.s_addr == INADDR_NONE)
+ return -1;
+#else
+ return -1;
+#endif
+ addr->addr_type = KRB5_ADDRESS_INET;
+ if(krb5_data_alloc(&addr->address, 4) != 0)
+ return -1;
+ _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length);
+ return 0;
+}
+
+static int
+ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr,
+ unsigned long len, krb5_address *low, krb5_address *high)
+{
+ unsigned long ia;
+ uint32_t l, h, m = 0xffffffff;
+
+ if (len > 32) {
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ "IPv4 prefix too large (%ld)", len);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ m = m << (32 - len);
+
+ _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length);
+
+ l = ia & m;
+ h = l | ~m;
+
+ low->addr_type = KRB5_ADDRESS_INET;
+ if(krb5_data_alloc(&low->address, 4) != 0)
+ return -1;
+ _krb5_put_int(low->address.data, l, low->address.length);
+
+ high->addr_type = KRB5_ADDRESS_INET;
+ if(krb5_data_alloc(&high->address, 4) != 0) {
+ krb5_free_address(context, low);
+ return -1;
+ }
+ _krb5_put_int(high->address.data, h, high->address.length);
+
+ return 0;
+}
+
+
+/*
+ * AF_INET6 - aka IPv6 implementation
+ */
+
+#ifdef HAVE_IPV6
+
+static krb5_error_code
+ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ unsigned char buf[4];
+
+ a->addr_type = KRB5_ADDRESS_INET;
+#ifndef IN6_ADDR_V6_TO_V4
+#ifdef IN6_EXTRACT_V4ADDR
+#define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x))
+#else
+#define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12])
+#endif
+#endif
+ memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4);
+ return krb5_data_copy(&a->address, buf, 4);
+ } else {
+ a->addr_type = KRB5_ADDRESS_INET6;
+ return krb5_data_copy(&a->address,
+ &sin6->sin6_addr,
+ sizeof(sin6->sin6_addr));
+ }
+}
+
+static krb5_error_code
+ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+
+ *port = sin6->sin6_port;
+ return 0;
+}
+
+static void
+ipv6_addr2sockaddr (const krb5_address *a,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in6 tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr));
+ tmp.sin6_port = port;
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static void
+ipv6_h_addr2sockaddr(const char *addr,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in6 tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_port = port;
+ tmp.sin6_addr = *((const struct in6_addr *)addr);
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static krb5_error_code
+ipv6_h_addr2addr (const char *addr,
+ krb5_address *a)
+{
+ a->addr_type = KRB5_ADDRESS_INET6;
+ return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr));
+}
+
+/*
+ *
+ */
+
+static krb5_boolean
+ipv6_uninteresting (const struct sockaddr *sa)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+ const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;
+
+ return
+ IN6_IS_ADDR_LINKLOCAL(in6)
+ || IN6_IS_ADDR_V4COMPAT(in6);
+}
+
+static void
+ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
+{
+ struct sockaddr_in6 tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_port = port;
+ tmp.sin6_addr = in6addr_any;
+ *sa_size = sizeof(tmp);
+}
+
+static int
+ipv6_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ char buf[128], buf2[3];
+#ifdef HAVE_INET_NTOP
+ if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL)
+#endif
+ {
+ /* XXX this is pretty ugly, but better than abort() */
+ int i;
+ unsigned char *p = addr->address.data;
+ buf[0] = '\0';
+ for(i = 0; i < addr->address.length; i++) {
+ snprintf(buf2, sizeof(buf2), "%02x", p[i]);
+ if(i > 0 && (i & 1) == 0)
+ strlcat(buf, ":", sizeof(buf));
+ strlcat(buf, buf2, sizeof(buf));
+ }
+ }
+ return snprintf(str, len, "IPv6:%s", buf);
+}
+
+static int
+ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr)
+{
+ int ret;
+ struct in6_addr in6;
+ const char *p;
+
+ p = strchr(address, ':');
+ if(p) {
+ p++;
+ if(strncasecmp(address, "ip6:", p - address) == 0 ||
+ strncasecmp(address, "ipv6:", p - address) == 0 ||
+ strncasecmp(address, "inet6:", p - address) == 0)
+ address = p;
+ }
+
+ ret = inet_pton(AF_INET6, address, &in6.s6_addr);
+ if(ret == 1) {
+ addr->addr_type = KRB5_ADDRESS_INET6;
+ ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr));
+ if (ret)
+ return -1;
+ memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr));
+ return 0;
+ }
+ return -1;
+}
+
+static int
+ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr,
+ unsigned long len, krb5_address *low, krb5_address *high)
+{
+ struct in6_addr addr, laddr, haddr;
+ uint32_t m;
+ int i, sub_len;
+
+ if (len > 128) {
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ "IPv6 prefix too large (%ld)", len);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+
+ if (inaddr->address.length != sizeof(addr)) {
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ "IPv6 addr bad length");
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+
+ memcpy(&addr, inaddr->address.data, inaddr->address.length);
+
+ for (i = 0; i < 16; i++) {
+ sub_len = min(8, len);
+
+ m = 0xff << (8 - sub_len);
+
+ laddr.s6_addr[i] = addr.s6_addr[i] & m;
+ haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m;
+
+ if (len > 8)
+ len -= 8;
+ else
+ len = 0;
+ }
+
+ low->addr_type = KRB5_ADDRESS_INET6;
+ if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0)
+ return -1;
+ memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr));
+
+ high->addr_type = KRB5_ADDRESS_INET6;
+ if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) {
+ krb5_free_address(context, low);
+ return -1;
+ }
+ memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr));
+
+ return 0;
+}
+
+#endif /* IPv6 */
+
+/*
+ * table
+ */
+
+#define KRB5_ADDRESS_ARANGE (-100)
+
+struct arange {
+ krb5_address low;
+ krb5_address high;
+};
+
+static int
+arange_parse_addr (krb5_context context,
+ const char *address, krb5_address *addr)
+{
+ char buf[1024], *p;
+ krb5_address low0, high0;
+ struct arange *a;
+ krb5_error_code ret;
+
+ if(strncasecmp(address, "RANGE:", 6) != 0)
+ return -1;
+
+ address += 6;
+
+ p = strrchr(address, '/');
+ if (p) {
+ krb5_addresses addrmask;
+ char *q;
+ long num;
+
+ if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf))
+ return -1;
+ buf[p - address] = '\0';
+ ret = krb5_parse_address(context, buf, &addrmask);
+ if (ret)
+ return ret;
+ if(addrmask.len != 1) {
+ krb5_free_addresses(context, &addrmask);
+ return -1;
+ }
+
+ address += p - address + 1;
+
+ num = strtol(address, &q, 10);
+ if (q == address || *q != '\0' || num < 0) {
+ krb5_free_addresses(context, &addrmask);
+ return -1;
+ }
+
+ ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num,
+ &low0, &high0);
+ krb5_free_addresses(context, &addrmask);
+ if (ret)
+ return ret;
+
+ } else {
+ krb5_addresses low, high;
+
+ strsep_copy(&address, "-", buf, sizeof(buf));
+ ret = krb5_parse_address(context, buf, &low);
+ if(ret)
+ return ret;
+ if(low.len != 1) {
+ krb5_free_addresses(context, &low);
+ return -1;
+ }
+
+ strsep_copy(&address, "-", buf, sizeof(buf));
+ ret = krb5_parse_address(context, buf, &high);
+ if(ret) {
+ krb5_free_addresses(context, &low);
+ return ret;
+ }
+
+ if(high.len != 1 && high.val[0].addr_type != low.val[0].addr_type) {
+ krb5_free_addresses(context, &low);
+ krb5_free_addresses(context, &high);
+ return -1;
+ }
+
+ ret = krb5_copy_address(context, &high.val[0], &high0);
+ if (ret == 0) {
+ ret = krb5_copy_address(context, &low.val[0], &low0);
+ if (ret)
+ krb5_free_address(context, &high0);
+ }
+ krb5_free_addresses(context, &low);
+ krb5_free_addresses(context, &high);
+ if (ret)
+ return ret;
+ }
+
+ krb5_data_alloc(&addr->address, sizeof(*a));
+ addr->addr_type = KRB5_ADDRESS_ARANGE;
+ a = addr->address.data;
+
+ if(krb5_address_order(context, &low0, &high0) < 0) {
+ a->low = low0;
+ a->high = high0;
+ } else {
+ a->low = high0;
+ a->high = low0;
+ }
+ return 0;
+}
+
+static int
+arange_free (krb5_context context, krb5_address *addr)
+{
+ struct arange *a;
+ a = addr->address.data;
+ krb5_free_address(context, &a->low);
+ krb5_free_address(context, &a->high);
+ krb5_data_free(&addr->address);
+ return 0;
+}
+
+
+static int
+arange_copy (krb5_context context, const krb5_address *inaddr,
+ krb5_address *outaddr)
+{
+ krb5_error_code ret;
+ struct arange *i, *o;
+
+ outaddr->addr_type = KRB5_ADDRESS_ARANGE;
+ ret = krb5_data_alloc(&outaddr->address, sizeof(*o));
+ if(ret)
+ return ret;
+ i = inaddr->address.data;
+ o = outaddr->address.data;
+ ret = krb5_copy_address(context, &i->low, &o->low);
+ if(ret) {
+ krb5_data_free(&outaddr->address);
+ return ret;
+ }
+ ret = krb5_copy_address(context, &i->high, &o->high);
+ if(ret) {
+ krb5_free_address(context, &o->low);
+ krb5_data_free(&outaddr->address);
+ return ret;
+ }
+ return 0;
+}
+
+static int
+arange_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ struct arange *a;
+ krb5_error_code ret;
+ size_t l, size, ret_len;
+
+ a = addr->address.data;
+
+ l = strlcpy(str, "RANGE:", len);
+ ret_len = l;
+ if (l > len)
+ l = len;
+ size = l;
+
+ ret = krb5_print_address (&a->low, str + size, len - size, &l);
+ if (ret)
+ return ret;
+ ret_len += l;
+ if (len - size > l)
+ size += l;
+ else
+ size = len;
+
+ l = strlcat(str + size, "-", len - size);
+ ret_len += l;
+ if (len - size > l)
+ size += l;
+ else
+ size = len;
+
+ ret = krb5_print_address (&a->high, str + size, len - size, &l);
+ if (ret)
+ return ret;
+ ret_len += l;
+
+ return ret_len;
+}
+
+static int
+arange_order_addr(krb5_context context,
+ const krb5_address *addr1,
+ const krb5_address *addr2)
+{
+ int tmp1, tmp2, sign;
+ struct arange *a;
+ const krb5_address *a2;
+
+ if(addr1->addr_type == KRB5_ADDRESS_ARANGE) {
+ a = addr1->address.data;
+ a2 = addr2;
+ sign = 1;
+ } else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) {
+ a = addr2->address.data;
+ a2 = addr1;
+ sign = -1;
+ } else
+ abort();
+
+ if(a2->addr_type == KRB5_ADDRESS_ARANGE) {
+ struct arange *b = a2->address.data;
+ tmp1 = krb5_address_order(context, &a->low, &b->low);
+ if(tmp1 != 0)
+ return sign * tmp1;
+ return sign * krb5_address_order(context, &a->high, &b->high);
+ } else if(a2->addr_type == a->low.addr_type) {
+ tmp1 = krb5_address_order(context, &a->low, a2);
+ if(tmp1 > 0)
+ return sign;
+ tmp2 = krb5_address_order(context, &a->high, a2);
+ if(tmp2 < 0)
+ return -sign;
+ return 0;
+ } else {
+ return sign * (addr1->addr_type - addr2->addr_type);
+ }
+}
+
+static int
+addrport_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ krb5_error_code ret;
+ krb5_address addr1, addr2;
+ uint16_t port = 0;
+ size_t ret_len = 0, l, size = 0;
+ krb5_storage *sp;
+
+ sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address));
+ /* for totally obscure reasons, these are not in network byteorder */
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */
+ krb5_ret_address(sp, &addr1);
+
+ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */
+ krb5_ret_address(sp, &addr2);
+ krb5_storage_free(sp);
+ if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) {
+ unsigned long value;
+ _krb5_get_int(addr2.address.data, &value, 2);
+ port = value;
+ }
+ l = strlcpy(str, "ADDRPORT:", len);
+ ret_len += l;
+ if (len > l)
+ size += l;
+ else
+ size = len;
+
+ ret = krb5_print_address(&addr1, str + size, len - size, &l);
+ if (ret)
+ return ret;
+ ret_len += l;
+ if (len - size > l)
+ size += l;
+ else
+ size = len;
+
+ ret = snprintf(str + size, len - size, ",PORT=%u", port);
+ if (ret < 0)
+ return EINVAL;
+ ret_len += ret;
+ return ret_len;
+}
+
+static struct addr_operations at[] = {
+ {AF_INET, KRB5_ADDRESS_INET, sizeof(struct sockaddr_in),
+ ipv4_sockaddr2addr,
+ ipv4_sockaddr2port,
+ ipv4_addr2sockaddr,
+ ipv4_h_addr2sockaddr,
+ ipv4_h_addr2addr,
+ ipv4_uninteresting, ipv4_anyaddr, ipv4_print_addr, ipv4_parse_addr,
+ NULL, NULL, NULL, ipv4_mask_boundary },
+#ifdef HAVE_IPV6
+ {AF_INET6, KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6),
+ ipv6_sockaddr2addr,
+ ipv6_sockaddr2port,
+ ipv6_addr2sockaddr,
+ ipv6_h_addr2sockaddr,
+ ipv6_h_addr2addr,
+ ipv6_uninteresting, ipv6_anyaddr, ipv6_print_addr, ipv6_parse_addr,
+ NULL, NULL, NULL, ipv6_mask_boundary } ,
+#endif
+ {KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, addrport_print_addr, NULL, NULL, NULL, NULL },
+ /* fake address type */
+ {KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange),
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ arange_print_addr, arange_parse_addr,
+ arange_order_addr, arange_free, arange_copy }
+};
+
+static int num_addrs = sizeof(at) / sizeof(at[0]);
+
+static size_t max_sockaddr_size = 0;
+
+/*
+ * generic functions
+ */
+
+static struct addr_operations *
+find_af(int af)
+{
+ struct addr_operations *a;
+
+ for (a = at; a < at + num_addrs; ++a)
+ if (af == a->af)
+ return a;
+ return NULL;
+}
+
+static struct addr_operations *
+find_atype(int atype)
+{
+ struct addr_operations *a;
+
+ for (a = at; a < at + num_addrs; ++a)
+ if (atype == a->atype)
+ return a;
+ return NULL;
+}
+
+/**
+ * krb5_sockaddr2address stores a address a "struct sockaddr" sa in
+ * the krb5_address addr.
+ *
+ * @param context a Keberos context
+ * @param sa a struct sockaddr to extract the address from
+ * @param addr an Kerberos 5 address to store the address in.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sockaddr2address (krb5_context context,
+ const struct sockaddr *sa, krb5_address *addr)
+{
+ struct addr_operations *a = find_af(sa->sa_family);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported",
+ sa->sa_family);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ return (*a->sockaddr2addr)(sa, addr);
+}
+
+/**
+ * krb5_sockaddr2port extracts a port (if possible) from a "struct
+ * sockaddr.
+ *
+ * @param context a Keberos context
+ * @param sa a struct sockaddr to extract the port from
+ * @param port a pointer to an int16_t store the port in.
+ *
+ * @return Return an error code or 0. Will return
+ * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sockaddr2port (krb5_context context,
+ const struct sockaddr *sa, int16_t *port)
+{
+ struct addr_operations *a = find_af(sa->sa_family);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported",
+ sa->sa_family);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ return (*a->sockaddr2port)(sa, port);
+}
+
+/**
+ * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr
+ * and port. The argument sa_size should initially contain the size of
+ * the sa and after the call, it will contain the actual length of the
+ * address. In case of the sa is too small to fit the whole address,
+ * the up to *sa_size will be stored, and then *sa_size will be set to
+ * the required length.
+ *
+ * @param context a Keberos context
+ * @param addr the address to copy the from
+ * @param sa the struct sockaddr that will be filled in
+ * @param sa_size pointer to length of sa, and after the call, it will
+ * contain the actual length of the address.
+ * @param port set port in sa.
+ *
+ * @return Return an error code or 0. Will return
+ * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_addr2sockaddr (krb5_context context,
+ const krb5_address *addr,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct addr_operations *a = find_atype(addr->addr_type);
+
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address type %d not supported",
+ addr->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ if (a->addr2sockaddr == NULL) {
+ krb5_set_error_message (context,
+ KRB5_PROG_ATYPE_NOSUPP,
+ "Can't convert address type %d to sockaddr",
+ addr->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ (*a->addr2sockaddr)(addr, sa, sa_size, port);
+ return 0;
+}
+
+/**
+ * krb5_max_sockaddr_size returns the max size of the .Li struct
+ * sockaddr that the Kerberos library will return.
+ *
+ * @return Return an size_t of the maximum struct sockaddr.
+ *
+ * @ingroup krb5_address
+ */
+
+size_t KRB5_LIB_FUNCTION
+krb5_max_sockaddr_size (void)
+{
+ if (max_sockaddr_size == 0) {
+ struct addr_operations *a;
+
+ for(a = at; a < at + num_addrs; ++a)
+ max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size);
+ }
+ return max_sockaddr_size;
+}
+
+/**
+ * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the
+ * kerberos library thinks are uninteresting. One example are link
+ * local addresses.
+ *
+ * @param sa pointer to struct sockaddr that might be interesting.
+ *
+ * @return Return a non zero for uninteresting addresses.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_sockaddr_uninteresting(const struct sockaddr *sa)
+{
+ struct addr_operations *a = find_af(sa->sa_family);
+ if (a == NULL || a->uninteresting == NULL)
+ return TRUE;
+ return (*a->uninteresting)(sa);
+}
+
+/**
+ * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and
+ * the "struct hostent" (see gethostbyname(3) ) h_addr_list
+ * component. The argument sa_size should initially contain the size
+ * of the sa, and after the call, it will contain the actual length of
+ * the address.
+ *
+ * @param context a Keberos context
+ * @param af addresses
+ * @param addr address
+ * @param sa returned struct sockaddr
+ * @param sa_size size of sa
+ * @param port port to set in sa.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_h_addr2sockaddr (krb5_context context,
+ int af,
+ const char *addr, struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct addr_operations *a = find_af(af);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported", af);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ (*a->h_addr2sockaddr)(addr, sa, sa_size, port);
+ return 0;
+}
+
+/**
+ * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception
+ * that it operates on a krb5_address instead of a struct sockaddr.
+ *
+ * @param context a Keberos context
+ * @param af address family
+ * @param haddr host address from struct hostent.
+ * @param addr returned krb5_address.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_h_addr2addr (krb5_context context,
+ int af,
+ const char *haddr, krb5_address *addr)
+{
+ struct addr_operations *a = find_af(af);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported", af);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ return (*a->h_addr2addr)(haddr, addr);
+}
+
+/**
+ * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to
+ * bind(2) to. The argument sa_size should initially contain the size
+ * of the sa, and after the call, it will contain the actual length
+ * of the address.
+ *
+ * @param context a Keberos context
+ * @param af address family
+ * @param sa sockaddr
+ * @param sa_size lenght of sa.
+ * @param port for to fill into sa.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_anyaddr (krb5_context context,
+ int af,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct addr_operations *a = find_af (af);
+
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported", af);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+
+ (*a->anyaddr)(sa, sa_size, port);
+ return 0;
+}
+
+/**
+ * krb5_print_address prints the address in addr to the string string
+ * that have the length len. If ret_len is not NULL, it will be filled
+ * with the length of the string if size were unlimited (not including
+ * the final NUL) .
+ *
+ * @param addr address to be printed
+ * @param str pointer string to print the address into
+ * @param len length that will fit into area pointed to by "str".
+ * @param ret_len return length the str.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_print_address (const krb5_address *addr,
+ char *str, size_t len, size_t *ret_len)
+{
+ struct addr_operations *a = find_atype(addr->addr_type);
+ int ret;
+
+ if (a == NULL || a->print_addr == NULL) {
+ char *s;
+ int l;
+ int i;
+
+ s = str;
+ l = snprintf(s, len, "TYPE_%d:", addr->addr_type);
+ if (l < 0 || l >= len)
+ return EINVAL;
+ s += l;
+ len -= l;
+ for(i = 0; i < addr->address.length; i++) {
+ l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]);
+ if (l < 0 || l >= len)
+ return EINVAL;
+ len -= l;
+ s += l;
+ }
+ if(ret_len != NULL)
+ *ret_len = s - str;
+ return 0;
+ }
+ ret = (*a->print_addr)(addr, str, len);
+ if (ret < 0)
+ return EINVAL;
+ if(ret_len != NULL)
+ *ret_len = ret;
+ return 0;
+}
+
+/**
+ * krb5_parse_address returns the resolved hostname in string to the
+ * krb5_addresses addresses .
+ *
+ * @param context a Keberos context
+ * @param string
+ * @param addresses
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_parse_address(krb5_context context,
+ const char *string,
+ krb5_addresses *addresses)
+{
+ int i, n;
+ struct addrinfo *ai, *a;
+ int error;
+ int save_errno;
+
+ addresses->len = 0;
+ addresses->val = NULL;
+
+ for(i = 0; i < num_addrs; i++) {
+ if(at[i].parse_addr) {
+ krb5_address addr;
+ if((*at[i].parse_addr)(context, string, &addr) == 0) {
+ ALLOC_SEQ(addresses, 1);
+ if (addresses->val == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ "malloc: out of memory");
+ return ENOMEM;
+ }
+ addresses->val[0] = addr;
+ return 0;
+ }
+ }
+ }
+
+ error = getaddrinfo (string, NULL, NULL, &ai);
+ if (error) {
+ krb5_error_code ret2;
+ save_errno = errno;
+ ret2 = krb5_eai_to_heim_errno(error, save_errno);
+ krb5_set_error_message (context, ret2, "%s: %s",
+ string, gai_strerror(error));
+ return ret2;
+ }
+
+ n = 0;
+ for (a = ai; a != NULL; a = a->ai_next)
+ ++n;
+
+ ALLOC_SEQ(addresses, n);
+ if (addresses->val == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ "malloc: out of memory");
+ freeaddrinfo(ai);
+ return ENOMEM;
+ }
+
+ addresses->len = 0;
+ for (a = ai, i = 0; a != NULL; a = a->ai_next) {
+ if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i]))
+ continue;
+ if(krb5_address_search(context, &addresses->val[i], addresses))
+ continue;
+ addresses->len = i;
+ i++;
+ }
+ freeaddrinfo (ai);
+ return 0;
+}
+
+/**
+ * krb5_address_order compares the addresses addr1 and addr2 so that
+ * it can be used for sorting addresses. If the addresses are the same
+ * address krb5_address_order will return 0. Behavies like memcmp(2).
+ *
+ * @param context a Keberos context
+ * @param addr1 krb5_address to compare
+ * @param addr2 krb5_address to compare
+ *
+ * @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and
+ * addr2 is the same address, > 0 if addr2 is "less" then addr1.
+ *
+ * @ingroup krb5_address
+ */
+
+int KRB5_LIB_FUNCTION
+krb5_address_order(krb5_context context,
+ const krb5_address *addr1,
+ const krb5_address *addr2)
+{
+ /* this sucks; what if both addresses have order functions, which
+ should we call? this works for now, though */
+ struct addr_operations *a;
+ a = find_atype(addr1->addr_type);
+ if(a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported",
+ addr1->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ if(a->order_addr != NULL)
+ return (*a->order_addr)(context, addr1, addr2);
+ a = find_atype(addr2->addr_type);
+ if(a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported",
+ addr2->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ if(a->order_addr != NULL)
+ return (*a->order_addr)(context, addr1, addr2);
+
+ if(addr1->addr_type != addr2->addr_type)
+ return addr1->addr_type - addr2->addr_type;
+ if(addr1->address.length != addr2->address.length)
+ return addr1->address.length - addr2->address.length;
+ return memcmp (addr1->address.data,
+ addr2->address.data,
+ addr1->address.length);
+}
+
+/**
+ * krb5_address_compare compares the addresses addr1 and addr2.
+ * Returns TRUE if the two addresses are the same.
+ *
+ * @param context a Keberos context
+ * @param addr1 address to compare
+ * @param addr2 address to compare
+ *
+ * @return Return an TRUE is the address are the same FALSE if not
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_address_compare(krb5_context context,
+ const krb5_address *addr1,
+ const krb5_address *addr2)
+{
+ return krb5_address_order (context, addr1, addr2) == 0;
+}
+
+/**
+ * krb5_address_search checks if the address addr is a member of the
+ * address set list addrlist .
+ *
+ * @param context a Keberos context.
+ * @param addr address to search for.
+ * @param addrlist list of addresses to look in for addr.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_address_search(krb5_context context,
+ const krb5_address *addr,
+ const krb5_addresses *addrlist)
+{
+ int i;
+
+ for (i = 0; i < addrlist->len; ++i)
+ if (krb5_address_compare (context, addr, &addrlist->val[i]))
+ return TRUE;
+ return FALSE;
+}
+
+/**
+ * krb5_free_address frees the data stored in the address that is
+ * alloced with any of the krb5_address functions.
+ *
+ * @param context a Keberos context
+ * @param address addresss to be freed.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_address(krb5_context context,
+ krb5_address *address)
+{
+ struct addr_operations *a = find_atype (address->addr_type);
+ if(a != NULL && a->free_addr != NULL)
+ return (*a->free_addr)(context, address);
+ krb5_data_free (&address->address);
+ memset(address, 0, sizeof(*address));
+ return 0;
+}
+
+/**
+ * krb5_free_addresses frees the data stored in the address that is
+ * alloced with any of the krb5_address functions.
+ *
+ * @param context a Keberos context
+ * @param addresses addressses to be freed.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_addresses(krb5_context context,
+ krb5_addresses *addresses)
+{
+ int i;
+ for(i = 0; i < addresses->len; i++)
+ krb5_free_address(context, &addresses->val[i]);
+ free(addresses->val);
+ addresses->len = 0;
+ addresses->val = NULL;
+ return 0;
+}
+
+/**
+ * krb5_copy_address copies the content of address
+ * inaddr to outaddr.
+ *
+ * @param context a Keberos context
+ * @param inaddr pointer to source address
+ * @param outaddr pointer to destination address
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_address(krb5_context context,
+ const krb5_address *inaddr,
+ krb5_address *outaddr)
+{
+ struct addr_operations *a = find_af (inaddr->addr_type);
+ if(a != NULL && a->copy_addr != NULL)
+ return (*a->copy_addr)(context, inaddr, outaddr);
+ return copy_HostAddress(inaddr, outaddr);
+}
+
+/**
+ * krb5_copy_addresses copies the content of addresses
+ * inaddr to outaddr.
+ *
+ * @param context a Keberos context
+ * @param inaddr pointer to source addresses
+ * @param outaddr pointer to destination addresses
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_addresses(krb5_context context,
+ const krb5_addresses *inaddr,
+ krb5_addresses *outaddr)
+{
+ int i;
+ ALLOC_SEQ(outaddr, inaddr->len);
+ if(inaddr->len > 0 && outaddr->val == NULL)
+ return ENOMEM;
+ for(i = 0; i < inaddr->len; i++)
+ krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]);
+ return 0;
+}
+
+/**
+ * krb5_append_addresses adds the set of addresses in source to
+ * dest. While copying the addresses, duplicates are also sorted out.
+ *
+ * @param context a Keberos context
+ * @param dest destination of copy operation
+ * @param source adresses that are going to be added to dest
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_append_addresses(krb5_context context,
+ krb5_addresses *dest,
+ const krb5_addresses *source)
+{
+ krb5_address *tmp;
+ krb5_error_code ret;
+ int i;
+ if(source->len > 0) {
+ tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ "realloc: out of memory");
+ return ENOMEM;
+ }
+ dest->val = tmp;
+ for(i = 0; i < source->len; i++) {
+ /* skip duplicates */
+ if(krb5_address_search(context, &source->val[i], dest))
+ continue;
+ ret = krb5_copy_address(context,
+ &source->val[i],
+ &dest->val[dest->len]);
+ if(ret)
+ return ret;
+ dest->len++;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)
+ *
+ * @param context a Keberos context
+ * @param res built address from addr/port
+ * @param addr address to use
+ * @param port port to use
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_make_addrport (krb5_context context,
+ krb5_address **res, const krb5_address *addr, int16_t port)
+{
+ krb5_error_code ret;
+ size_t len = addr->address.length + 2 + 4 * 4;
+ u_char *p;
+
+ *res = malloc (sizeof(**res));
+ if (*res == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*res)->addr_type = KRB5_ADDRESS_ADDRPORT;
+ ret = krb5_data_alloc (&(*res)->address, len);
+ if (ret) {
+ krb5_set_error_message (context, ret,
+ "malloc: out of memory");
+ free (*res);
+ *res = NULL;
+ return ret;
+ }
+ p = (*res)->address.data;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = (addr->addr_type ) & 0xFF;
+ *p++ = (addr->addr_type >> 8) & 0xFF;
+
+ *p++ = (addr->address.length ) & 0xFF;
+ *p++ = (addr->address.length >> 8) & 0xFF;
+ *p++ = (addr->address.length >> 16) & 0xFF;
+ *p++ = (addr->address.length >> 24) & 0xFF;
+
+ memcpy (p, addr->address.data, addr->address.length);
+ p += addr->address.length;
+
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = (KRB5_ADDRESS_IPPORT ) & 0xFF;
+ *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF;
+
+ *p++ = (2 ) & 0xFF;
+ *p++ = (2 >> 8) & 0xFF;
+ *p++ = (2 >> 16) & 0xFF;
+ *p++ = (2 >> 24) & 0xFF;
+
+ memcpy (p, &port, 2);
+ p += 2;
+
+ return 0;
+}
+
+/**
+ * Calculate the boundary addresses of `inaddr'/`prefixlen' and store
+ * them in `low' and `high'.
+ *
+ * @param context a Keberos context
+ * @param inaddr address in prefixlen that the bondery searched
+ * @param prefixlen width of boundery
+ * @param low lowest address
+ * @param high highest address
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_address_prefixlen_boundary(krb5_context context,
+ const krb5_address *inaddr,
+ unsigned long prefixlen,
+ krb5_address *low,
+ krb5_address *high)
+{
+ struct addr_operations *a = find_atype (inaddr->addr_type);
+ if(a != NULL && a->mask_boundary != NULL)
+ return (*a->mask_boundary)(context, inaddr, prefixlen, low, high);
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d doesn't support "
+ "address mask operation", inaddr->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+}
diff --git a/source4/heimdal/lib/krb5/appdefault.c b/source4/heimdal/lib/krb5/appdefault.c
new file mode 100644
index 0000000000..a5b6e67e30
--- /dev/null
+++ b/source4/heimdal/lib/krb5/appdefault.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2000 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+void KRB5_LIB_FUNCTION
+krb5_appdefault_boolean(krb5_context context, const char *appname,
+ krb5_const_realm realm, const char *option,
+ krb5_boolean def_val, krb5_boolean *ret_val)
+{
+
+ if(appname == NULL)
+ appname = getprogname();
+
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "libdefaults", option, NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "realms", realm, option, NULL);
+
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ realm,
+ option,
+ NULL);
+ if(appname != NULL) {
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ realm,
+ option,
+ NULL);
+ }
+ *ret_val = def_val;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_appdefault_string(krb5_context context, const char *appname,
+ krb5_const_realm realm, const char *option,
+ const char *def_val, char **ret_val)
+{
+ if(appname == NULL)
+ appname = getprogname();
+
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "libdefaults", option, NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "realms", realm, option, NULL);
+
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ realm,
+ option,
+ NULL);
+ if(appname != NULL) {
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ realm,
+ option,
+ NULL);
+ }
+ if(def_val != NULL)
+ *ret_val = strdup(def_val);
+ else
+ *ret_val = NULL;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_appdefault_time(krb5_context context, const char *appname,
+ krb5_const_realm realm, const char *option,
+ time_t def_val, time_t *ret_val)
+{
+ krb5_deltat t;
+ char *val;
+
+ krb5_appdefault_string(context, appname, realm, option, NULL, &val);
+ if (val == NULL) {
+ *ret_val = def_val;
+ return;
+ }
+ if (krb5_string_to_deltat(val, &t))
+ *ret_val = def_val;
+ else
+ *ret_val = t;
+ free(val);
+}
diff --git a/source4/heimdal/lib/krb5/asn1_glue.c b/source4/heimdal/lib/krb5/asn1_glue.c
new file mode 100644
index 0000000000..84c9cd8b68
--- /dev/null
+++ b/source4/heimdal/lib/krb5/asn1_glue.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ *
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_principal2principalname (PrincipalName *p,
+ const krb5_principal from)
+{
+ return copy_PrincipalName(&from->name, p);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_principalname2krb5_principal (krb5_context context,
+ krb5_principal *principal,
+ const PrincipalName from,
+ const Realm realm)
+{
+ krb5_principal p = malloc(sizeof(*p));
+ if (p == NULL)
+ return ENOMEM;
+ copy_PrincipalName(&from, &p->name);
+ p->realm = strdup(realm);
+ if (p->realm == NULL)
+ return ENOMEM;
+ *principal = p;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/auth_context.c b/source4/heimdal/lib/krb5/auth_context.c
new file mode 100644
index 0000000000..cbb186d6c3
--- /dev/null
+++ b/source4/heimdal/lib/krb5/auth_context.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_init(krb5_context context,
+ krb5_auth_context *auth_context)
+{
+ krb5_auth_context p;
+
+ ALLOC(p, 1);
+ if(!p) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memset(p, 0, sizeof(*p));
+ ALLOC(p->authenticator, 1);
+ if (!p->authenticator) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ free(p);
+ return ENOMEM;
+ }
+ memset (p->authenticator, 0, sizeof(*p->authenticator));
+ p->flags = KRB5_AUTH_CONTEXT_DO_TIME;
+
+ p->local_address = NULL;
+ p->remote_address = NULL;
+ p->local_port = 0;
+ p->remote_port = 0;
+ p->keytype = KEYTYPE_NULL;
+ p->cksumtype = CKSUMTYPE_NONE;
+ *auth_context = p;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_free(krb5_context context,
+ krb5_auth_context auth_context)
+{
+ if (auth_context != NULL) {
+ krb5_free_authenticator(context, &auth_context->authenticator);
+ if(auth_context->local_address){
+ free_HostAddress(auth_context->local_address);
+ free(auth_context->local_address);
+ }
+ if(auth_context->remote_address){
+ free_HostAddress(auth_context->remote_address);
+ free(auth_context->remote_address);
+ }
+ krb5_free_keyblock(context, auth_context->keyblock);
+ krb5_free_keyblock(context, auth_context->remote_subkey);
+ krb5_free_keyblock(context, auth_context->local_subkey);
+ free (auth_context);
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t flags)
+{
+ auth_context->flags = flags;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t *flags)
+{
+ *flags = auth_context->flags;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_addflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t addflags,
+ int32_t *flags)
+{
+ if (flags)
+ *flags = auth_context->flags;
+ auth_context->flags |= addflags;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_removeflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t removeflags,
+ int32_t *flags)
+{
+ if (flags)
+ *flags = auth_context->flags;
+ auth_context->flags &= ~removeflags;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setaddrs(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_address *local_addr,
+ krb5_address *remote_addr)
+{
+ if (local_addr) {
+ if (auth_context->local_address)
+ krb5_free_address (context, auth_context->local_address);
+ else
+ if ((auth_context->local_address = malloc(sizeof(krb5_address))) == NULL)
+ return ENOMEM;
+ krb5_copy_address(context, local_addr, auth_context->local_address);
+ }
+ if (remote_addr) {
+ if (auth_context->remote_address)
+ krb5_free_address (context, auth_context->remote_address);
+ else
+ if ((auth_context->remote_address = malloc(sizeof(krb5_address))) == NULL)
+ return ENOMEM;
+ krb5_copy_address(context, remote_addr, auth_context->remote_address);
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_genaddrs(krb5_context context,
+ krb5_auth_context auth_context,
+ int fd, int flags)
+{
+ krb5_error_code ret;
+ krb5_address local_k_address, remote_k_address;
+ krb5_address *lptr = NULL, *rptr = NULL;
+ struct sockaddr_storage ss_local, ss_remote;
+ struct sockaddr *local = (struct sockaddr *)&ss_local;
+ struct sockaddr *remote = (struct sockaddr *)&ss_remote;
+ socklen_t len;
+
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR) {
+ if (auth_context->local_address == NULL) {
+ len = sizeof(ss_local);
+ if(getsockname(fd, local, &len) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "getsockname: %s",
+ strerror(ret));
+ goto out;
+ }
+ ret = krb5_sockaddr2address (context, local, &local_k_address);
+ if(ret) goto out;
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR) {
+ krb5_sockaddr2port (context, local, &auth_context->local_port);
+ } else
+ auth_context->local_port = 0;
+ lptr = &local_k_address;
+ }
+ }
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR) {
+ len = sizeof(ss_remote);
+ if(getpeername(fd, remote, &len) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "getpeername: %s", strerror(ret));
+ goto out;
+ }
+ ret = krb5_sockaddr2address (context, remote, &remote_k_address);
+ if(ret) goto out;
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR) {
+ krb5_sockaddr2port (context, remote, &auth_context->remote_port);
+ } else
+ auth_context->remote_port = 0;
+ rptr = &remote_k_address;
+ }
+ ret = krb5_auth_con_setaddrs (context,
+ auth_context,
+ lptr,
+ rptr);
+ out:
+ if (lptr)
+ krb5_free_address (context, lptr);
+ if (rptr)
+ krb5_free_address (context, rptr);
+ return ret;
+
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setaddrs_from_fd (krb5_context context,
+ krb5_auth_context auth_context,
+ void *p_fd)
+{
+ int fd = *(int*)p_fd;
+ int flags = 0;
+ if(auth_context->local_address == NULL)
+ flags |= KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR;
+ if(auth_context->remote_address == NULL)
+ flags |= KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR;
+ return krb5_auth_con_genaddrs(context, auth_context, fd, flags);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getaddrs(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_address **local_addr,
+ krb5_address **remote_addr)
+{
+ if(*local_addr)
+ krb5_free_address (context, *local_addr);
+ *local_addr = malloc (sizeof(**local_addr));
+ if (*local_addr == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_copy_address(context,
+ auth_context->local_address,
+ *local_addr);
+
+ if(*remote_addr)
+ krb5_free_address (context, *remote_addr);
+ *remote_addr = malloc (sizeof(**remote_addr));
+ if (*remote_addr == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ krb5_free_address (context, *local_addr);
+ *local_addr = NULL;
+ return ENOMEM;
+ }
+ krb5_copy_address(context,
+ auth_context->remote_address,
+ *remote_addr);
+ return 0;
+}
+
+static krb5_error_code
+copy_key(krb5_context context,
+ krb5_keyblock *in,
+ krb5_keyblock **out)
+{
+ if(in)
+ return krb5_copy_keyblock(context, in, out);
+ *out = NULL; /* is this right? */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return copy_key(context, auth_context->keyblock, keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getlocalsubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return copy_key(context, auth_context->local_subkey, keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getremotesubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return copy_key(context, auth_context->remote_subkey, keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->keyblock)
+ krb5_free_keyblock(context, auth_context->keyblock);
+ return copy_key(context, keyblock, &auth_context->keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setlocalsubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->local_subkey)
+ krb5_free_keyblock(context, auth_context->local_subkey);
+ return copy_key(context, keyblock, &auth_context->local_subkey);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_generatelocalsubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_keyblock *subkey;
+
+ ret = krb5_generate_subkey_extended (context, key,
+ auth_context->keytype,
+ &subkey);
+ if(ret)
+ return ret;
+ if(auth_context->local_subkey)
+ krb5_free_keyblock(context, auth_context->local_subkey);
+ auth_context->local_subkey = subkey;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setremotesubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->remote_subkey)
+ krb5_free_keyblock(context, auth_context->remote_subkey);
+ return copy_key(context, keyblock, &auth_context->remote_subkey);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setcksumtype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_cksumtype cksumtype)
+{
+ auth_context->cksumtype = cksumtype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getcksumtype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_cksumtype *cksumtype)
+{
+ *cksumtype = auth_context->cksumtype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setkeytype (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keytype keytype)
+{
+ auth_context->keytype = keytype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getkeytype (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keytype *keytype)
+{
+ *keytype = auth_context->keytype;
+ return 0;
+}
+
+#if 0
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setenctype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_enctype etype)
+{
+ if(auth_context->keyblock)
+ krb5_free_keyblock(context, auth_context->keyblock);
+ ALLOC(auth_context->keyblock, 1);
+ if(auth_context->keyblock == NULL)
+ return ENOMEM;
+ auth_context->keyblock->keytype = etype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getenctype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_enctype *etype)
+{
+ krb5_abortx(context, "unimplemented krb5_auth_getenctype called");
+}
+#endif
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getlocalseqnumber(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t *seqnumber)
+{
+ *seqnumber = auth_context->local_seqnumber;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setlocalseqnumber (krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t seqnumber)
+{
+ auth_context->local_seqnumber = seqnumber;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_getremoteseqnumber(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t *seqnumber)
+{
+ *seqnumber = auth_context->remote_seqnumber;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setremoteseqnumber (krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t seqnumber)
+{
+ auth_context->remote_seqnumber = seqnumber;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getauthenticator(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_authenticator *authenticator)
+{
+ *authenticator = malloc(sizeof(**authenticator));
+ if (*authenticator == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ copy_Authenticator(auth_context->authenticator,
+ *authenticator);
+ return 0;
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_free_authenticator(krb5_context context,
+ krb5_authenticator *authenticator)
+{
+ free_Authenticator (*authenticator);
+ free (*authenticator);
+ *authenticator = NULL;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setuserkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->keyblock)
+ krb5_free_keyblock(context, auth_context->keyblock);
+ return krb5_copy_keyblock(context, keyblock, &auth_context->keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getrcache(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_rcache *rcache)
+{
+ *rcache = auth_context->rcache;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setrcache(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_rcache rcache)
+{
+ auth_context->rcache = rcache;
+ return 0;
+}
+
+#if 0 /* not implemented */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_initivector(krb5_context context,
+ krb5_auth_context auth_context)
+{
+ krb5_abortx(context, "unimplemented krb5_auth_con_initivector called");
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setivector(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_pointer ivector)
+{
+ krb5_abortx(context, "unimplemented krb5_auth_con_setivector called");
+}
+
+#endif /* not implemented */
diff --git a/source4/heimdal/lib/krb5/build_ap_req.c b/source4/heimdal/lib/krb5/build_ap_req.c
new file mode 100644
index 0000000000..92051ba68a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/build_ap_req.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_ap_req (krb5_context context,
+ krb5_enctype enctype,
+ krb5_creds *cred,
+ krb5_flags ap_options,
+ krb5_data authenticator,
+ krb5_data *retdata)
+{
+ krb5_error_code ret = 0;
+ AP_REQ ap;
+ Ticket t;
+ size_t len;
+
+ ap.pvno = 5;
+ ap.msg_type = krb_ap_req;
+ memset(&ap.ap_options, 0, sizeof(ap.ap_options));
+ ap.ap_options.use_session_key = (ap_options & AP_OPTS_USE_SESSION_KEY) > 0;
+ ap.ap_options.mutual_required = (ap_options & AP_OPTS_MUTUAL_REQUIRED) > 0;
+
+ ap.ticket.tkt_vno = 5;
+ copy_Realm(&cred->server->realm, &ap.ticket.realm);
+ copy_PrincipalName(&cred->server->name, &ap.ticket.sname);
+
+ decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len);
+ copy_EncryptedData(&t.enc_part, &ap.ticket.enc_part);
+ free_Ticket(&t);
+
+ ap.authenticator.etype = enctype;
+ ap.authenticator.kvno = NULL;
+ ap.authenticator.cipher = authenticator;
+
+ ASN1_MALLOC_ENCODE(AP_REQ, retdata->data, retdata->length,
+ &ap, &len, ret);
+ if(ret == 0 && retdata->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_AP_REQ(&ap);
+ return ret;
+
+}
diff --git a/source4/heimdal/lib/krb5/build_auth.c b/source4/heimdal/lib/krb5/build_auth.c
new file mode 100644
index 0000000000..eb106dc23f
--- /dev/null
+++ b/source4/heimdal/lib/krb5/build_auth.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+make_etypelist(krb5_context context,
+ krb5_authdata **auth_data)
+{
+ EtypeList etypes;
+ krb5_error_code ret;
+ krb5_authdata ad;
+ u_char *buf;
+ size_t len;
+ size_t buf_size;
+
+ ret = krb5_init_etype(context, &etypes.len, &etypes.val, NULL);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EtypeList, buf, buf_size, &etypes, &len, ret);
+ if (ret) {
+ free_EtypeList(&etypes);
+ return ret;
+ }
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_EtypeList(&etypes);
+
+ ALLOC_SEQ(&ad, 1);
+ if (ad.val == NULL) {
+ free(buf);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ad.val[0].ad_type = KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION;
+ ad.val[0].ad_data.length = len;
+ ad.val[0].ad_data.data = buf;
+
+ ASN1_MALLOC_ENCODE(AD_IF_RELEVANT, buf, buf_size, &ad, &len, ret);
+ if (ret) {
+ free_AuthorizationData(&ad);
+ return ret;
+ }
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_AuthorizationData(&ad);
+
+ ALLOC(*auth_data, 1);
+ if (*auth_data == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ALLOC_SEQ(*auth_data, 1);
+ if ((*auth_data)->val == NULL) {
+ free(buf);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*auth_data)->val[0].ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+ (*auth_data)->val[0].ad_data.length = len;
+ (*auth_data)->val[0].ad_data.data = buf;
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_authenticator (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_enctype enctype,
+ krb5_creds *cred,
+ Checksum *cksum,
+ Authenticator **auth_result,
+ krb5_data *result,
+ krb5_key_usage usage)
+{
+ Authenticator *auth;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ auth = calloc(1, sizeof(*auth));
+ if (auth == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ auth->authenticator_vno = 5;
+ copy_Realm(&cred->client->realm, &auth->crealm);
+ copy_PrincipalName(&cred->client->name, &auth->cname);
+
+ krb5_us_timeofday (context, &auth->ctime, &auth->cusec);
+
+ ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth->subkey);
+ if(ret)
+ goto fail;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if(auth_context->local_seqnumber == 0)
+ krb5_generate_seq_number (context,
+ &cred->session,
+ &auth_context->local_seqnumber);
+ ALLOC(auth->seq_number, 1);
+ if(auth->seq_number == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ *auth->seq_number = auth_context->local_seqnumber;
+ } else
+ auth->seq_number = NULL;
+ auth->authorization_data = NULL;
+ auth->cksum = cksum;
+
+ if (cksum != NULL && cksum->cksumtype == CKSUMTYPE_GSSAPI) {
+ /*
+ * This is not GSS-API specific, we only enable it for
+ * GSS for now
+ */
+ ret = make_etypelist(context, &auth->authorization_data);
+ if (ret)
+ goto fail;
+ }
+
+ /* XXX - Copy more to auth_context? */
+
+ auth_context->authenticator->ctime = auth->ctime;
+ auth_context->authenticator->cusec = auth->cusec;
+
+ ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, auth, &len, ret);
+ if (ret)
+ goto fail;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_crypto_init(context, &cred->session, enctype, &crypto);
+ if (ret)
+ goto fail;
+ ret = krb5_encrypt (context,
+ crypto,
+ usage /* KRB5_KU_AP_REQ_AUTH */,
+ buf + buf_size - len,
+ len,
+ result);
+ krb5_crypto_destroy(context, crypto);
+
+ if (ret)
+ goto fail;
+
+ free (buf);
+
+ if (auth_result)
+ *auth_result = auth;
+ else {
+ /* Don't free the `cksum', it's allocated by the caller */
+ auth->cksum = NULL;
+ free_Authenticator (auth);
+ free (auth);
+ }
+ return ret;
+ fail:
+ free_Authenticator (auth);
+ free (auth);
+ free (buf);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/cache.c b/source4/heimdal/lib/krb5/cache.c
new file mode 100644
index 0000000000..02db405f7e
--- /dev/null
+++ b/source4/heimdal/lib/krb5/cache.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Add a new ccache type with operations `ops', overwriting any
+ * existing one if `override'.
+ *
+ * @param context a Keberos context
+ * @param ops type of plugin symbol
+ * @param override flag to select if the registration is to overide
+ * an existing ops with the same name.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_register(krb5_context context,
+ const krb5_cc_ops *ops,
+ krb5_boolean override)
+{
+ int i;
+
+ for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
+ if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) {
+ if(!override) {
+ krb5_set_error_message(context,
+ KRB5_CC_TYPE_EXISTS,
+ "ccache type %s already exists",
+ ops->prefix);
+ return KRB5_CC_TYPE_EXISTS;
+ }
+ break;
+ }
+ }
+ if(i == context->num_cc_ops) {
+ krb5_cc_ops *o = realloc(context->cc_ops,
+ (context->num_cc_ops + 1) *
+ sizeof(*context->cc_ops));
+ if(o == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ context->num_cc_ops++;
+ context->cc_ops = o;
+ memset(context->cc_ops + i, 0,
+ (context->num_cc_ops - i) * sizeof(*context->cc_ops));
+ }
+ memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
+ return 0;
+}
+
+/*
+ * Allocate the memory for a `id' and the that function table to
+ * `ops'. Returns 0 or and error code.
+ */
+
+krb5_error_code
+_krb5_cc_allocate(krb5_context context,
+ const krb5_cc_ops *ops,
+ krb5_ccache *id)
+{
+ krb5_ccache p;
+
+ p = malloc (sizeof(*p));
+ if(p == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ p->ops = ops;
+ *id = p;
+
+ return 0;
+}
+
+/*
+ * Allocate memory for a new ccache in `id' with operations `ops'
+ * and name `residual'. Return 0 or an error code.
+ */
+
+static krb5_error_code
+allocate_ccache (krb5_context context,
+ const krb5_cc_ops *ops,
+ const char *residual,
+ krb5_ccache *id)
+{
+ krb5_error_code ret;
+
+ ret = _krb5_cc_allocate(context, ops, id);
+ if (ret)
+ return ret;
+ ret = (*id)->ops->resolve(context, id, residual);
+ if(ret)
+ free(*id);
+ return ret;
+}
+
+/**
+ * Find and allocate a ccache in `id' from the specification in `residual'.
+ * If the ccache name doesn't contain any colon, interpret it as a file name.
+ *
+ * @param context a Keberos context.
+ * @param name string name of a credential cache.
+ * @param id return pointer to a found credential cache.
+ *
+ * @return Return 0 or an error code. In case of an error, id is set
+ * to NULL.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_resolve(krb5_context context,
+ const char *name,
+ krb5_ccache *id)
+{
+ int i;
+
+ *id = NULL;
+
+ for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
+ size_t prefix_len = strlen(context->cc_ops[i].prefix);
+
+ if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
+ && name[prefix_len] == ':') {
+ return allocate_ccache (context, &context->cc_ops[i],
+ name + prefix_len + 1,
+ id);
+ }
+ }
+ if (strchr (name, ':') == NULL)
+ return allocate_ccache (context, &krb5_fcc_ops, name, id);
+ else {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ "unknown ccache type %s", name);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+}
+
+/**
+ * Generate a new ccache of type `ops' in `id'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_gen_new(krb5_context context,
+ const krb5_cc_ops *ops,
+ krb5_ccache *id)
+{
+ return krb5_cc_new_unique(context, ops->prefix, NULL, id);
+}
+
+/**
+ * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
+ * the library chooses the default credential cache type. The supplied
+ * `hint' (that can be NULL) is a string that the credential cache
+ * type can use to base the name of the credential on, this is to make
+ * it easier for the user to differentiate the credentials.
+ *
+ * @return Returns 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_new_unique(krb5_context context, const char *type,
+ const char *hint, krb5_ccache *id)
+{
+ const krb5_cc_ops *ops;
+ krb5_error_code ret;
+
+ ops = krb5_cc_get_prefix_ops(context, type);
+ if (ops == NULL) {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ "Credential cache type %s is unknown", type);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+
+ ret = _krb5_cc_allocate(context, ops, id);
+ if (ret)
+ return ret;
+ return (*id)->ops->gen_new(context, id);
+}
+
+/**
+ * Return the name of the ccache `id'
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const char* KRB5_LIB_FUNCTION
+krb5_cc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return id->ops->get_name(context, id);
+}
+
+/**
+ * Return the type of the ccache `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const char* KRB5_LIB_FUNCTION
+krb5_cc_get_type(krb5_context context,
+ krb5_ccache id)
+{
+ return id->ops->prefix;
+}
+
+/**
+ * Return the complete resolvable name the ccache `id' in `str´.
+ * `str` should be freed with free(3).
+ * Returns 0 or an error (and then *str is set to NULL).
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_full_name(krb5_context context,
+ krb5_ccache id,
+ char **str)
+{
+ const char *type, *name;
+
+ *str = NULL;
+
+ type = krb5_cc_get_type(context, id);
+ if (type == NULL) {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ "cache have no name of type");
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+
+ name = krb5_cc_get_name(context, id);
+ if (name == NULL) {
+ krb5_set_error_message(context, KRB5_CC_BADNAME,
+ "cache of type %s have no name", type);
+ return KRB5_CC_BADNAME;
+ }
+
+ if (asprintf(str, "%s:%s", type, name) == -1) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ *str = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * Return krb5_cc_ops of a the ccache `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const krb5_cc_ops *
+krb5_cc_get_ops(krb5_context context, krb5_ccache id)
+{
+ return id->ops;
+}
+
+/*
+ * Expand variables in `str' into `res'
+ */
+
+krb5_error_code
+_krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
+{
+ size_t tlen, len = 0;
+ char *tmp, *tmp2, *append;
+
+ *res = NULL;
+
+ while (str && *str) {
+ tmp = strstr(str, "%{");
+ if (tmp && tmp != str) {
+ append = malloc((tmp - str) + 1);
+ if (append) {
+ memcpy(append, str, tmp - str);
+ append[tmp - str] = '\0';
+ }
+ str = tmp;
+ } else if (tmp) {
+ tmp2 = strchr(tmp, '}');
+ if (tmp2 == NULL) {
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT,
+ "variable missing }");
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ if (strncasecmp(tmp, "%{uid}", 6) == 0)
+ asprintf(&append, "%u", (unsigned)getuid());
+ else if (strncasecmp(tmp, "%{null}", 7) == 0)
+ append = strdup("");
+ else {
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context,
+ KRB5_CONFIG_BADFORMAT,
+ "expand default cache unknown "
+ "variable \"%.*s\"",
+ (int)(tmp2 - tmp) - 2, tmp + 2);
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ str = tmp2 + 1;
+ } else {
+ append = strdup(str);
+ str = NULL;
+ }
+ if (append == NULL) {
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ tlen = strlen(append);
+ tmp = realloc(*res, len + tlen + 1);
+ if (tmp == NULL) {
+ free(append);
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context, ENOMEM,
+ "malloc: out of memory");
+ return ENOMEM;
+ }
+ *res = tmp;
+ memcpy(*res + len, append, tlen + 1);
+ len = len + tlen;
+ free(append);
+ }
+ return 0;
+}
+
+/*
+ * Return non-zero if envirnoment that will determine default krb5cc
+ * name has changed.
+ */
+
+static int
+environment_changed(krb5_context context)
+{
+ const char *e;
+
+ /* if the cc name was set, don't change it */
+ if (context->default_cc_name_set)
+ return 0;
+
+ if(issuid())
+ return 0;
+
+ e = getenv("KRB5CCNAME");
+ if (e == NULL) {
+ if (context->default_cc_name_env) {
+ free(context->default_cc_name_env);
+ context->default_cc_name_env = NULL;
+ return 1;
+ }
+ } else {
+ if (context->default_cc_name_env == NULL)
+ return 1;
+ if (strcmp(e, context->default_cc_name_env) != 0)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Switch the default default credential cache for a specific
+ * credcache type (and name for some implementations).
+ *
+ * @return Returns 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code
+krb5_cc_switch(krb5_context context, krb5_ccache id)
+{
+
+ if (id->ops->set_default == NULL)
+ return 0;
+
+ return (*id->ops->set_default)(context, id);
+}
+
+/**
+ * Set the default cc name for `context' to `name'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_set_default_name(krb5_context context, const char *name)
+{
+ krb5_error_code ret = 0;
+ char *p;
+
+ if (name == NULL) {
+ const char *e = NULL;
+
+ if(!issuid()) {
+ e = getenv("KRB5CCNAME");
+ if (e) {
+ p = strdup(e);
+ if (context->default_cc_name_env)
+ free(context->default_cc_name_env);
+ context->default_cc_name_env = strdup(e);
+ }
+ }
+ if (e == NULL) {
+ e = krb5_config_get_string(context, NULL, "libdefaults",
+ "default_cc_name", NULL);
+ if (e) {
+ ret = _krb5_expand_default_cc_name(context, e, &p);
+ if (ret)
+ return ret;
+ }
+ if (e == NULL) {
+ const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
+ e = krb5_config_get_string(context, NULL, "libdefaults",
+ "default_cc_type", NULL);
+ if (e) {
+ ops = krb5_cc_get_prefix_ops(context, e);
+ if (ops == NULL) {
+ krb5_set_error_message(context,
+ KRB5_CC_UNKNOWN_TYPE,
+ "Credential cache type %s "
+ "is unknown", e);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+ }
+ ret = (*ops->get_default_name)(context, &p);
+ if (ret)
+ return ret;
+ }
+ }
+ context->default_cc_name_set = 0;
+ } else {
+ p = strdup(name);
+ context->default_cc_name_set = 1;
+ }
+
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ if (context->default_cc_name)
+ free(context->default_cc_name);
+
+ context->default_cc_name = p;
+
+ return ret;
+}
+
+/**
+ * Return a pointer to a context static string containing the default
+ * ccache name.
+ *
+ * @return String to the default credential cache name.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const char* KRB5_LIB_FUNCTION
+krb5_cc_default_name(krb5_context context)
+{
+ if (context->default_cc_name == NULL || environment_changed(context))
+ krb5_cc_set_default_name(context, NULL);
+
+ return context->default_cc_name;
+}
+
+/**
+ * Open the default ccache in `id'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_default(krb5_context context,
+ krb5_ccache *id)
+{
+ const char *p = krb5_cc_default_name(context);
+
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return krb5_cc_resolve(context, p, id);
+}
+
+/**
+ * Create a new ccache in `id' for `primary_principal'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ return (*id->ops->init)(context, id, primary_principal);
+}
+
+
+/**
+ * Remove the ccache `id'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+
+ ret = (*id->ops->destroy)(context, id);
+ krb5_cc_close (context, id);
+ return ret;
+}
+
+/**
+ * Stop using the ccache `id' and free the related resources.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_close(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+ ret = (*id->ops->close)(context, id);
+ free(id);
+ return ret;
+}
+
+/**
+ * Store `creds' in the ccache `id'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ return (*id->ops->store)(context, id, creds);
+}
+
+/**
+ * Retrieve the credential identified by `mcreds' (and `whichfields')
+ * from `id' in `creds'. 'creds' must be free by the caller using
+ * krb5_free_cred_contents.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_retrieve_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags whichfields,
+ const krb5_creds *mcreds,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_cc_cursor cursor;
+
+ if (id->ops->retrieve != NULL) {
+ return (*id->ops->retrieve)(context, id, whichfields,
+ mcreds, creds);
+ }
+
+ ret = krb5_cc_start_seq_get(context, id, &cursor);
+ if (ret)
+ return ret;
+ while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
+ if(krb5_compare_creds(context, whichfields, mcreds, creds)){
+ ret = 0;
+ break;
+ }
+ krb5_free_cred_contents (context, creds);
+ }
+ krb5_cc_end_seq_get(context, id, &cursor);
+ return ret;
+}
+
+/**
+ * Return the principal of `id' in `principal'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ return (*id->ops->get_princ)(context, id, principal);
+}
+
+/**
+ * Start iterating over `id', `cursor' is initialized to the
+ * beginning.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_start_seq_get (krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ return (*id->ops->get_first)(context, id, cursor);
+}
+
+/**
+ * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
+ * and advance `cursor'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_next_cred (krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ return (*id->ops->get_next)(context, id, cursor, creds);
+}
+
+/**
+ * Like krb5_cc_next_cred, but allow for selective retrieval
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_next_cred_match(krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor * cursor,
+ krb5_creds * creds,
+ krb5_flags whichfields,
+ const krb5_creds * mcreds)
+{
+ krb5_error_code ret;
+ while (1) {
+ ret = krb5_cc_next_cred(context, id, cursor, creds);
+ if (ret)
+ return ret;
+ if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
+ return 0;
+ krb5_free_cred_contents(context, creds);
+ }
+}
+
+/**
+ * Destroy the cursor `cursor'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_end_seq_get (krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ return (*id->ops->end_get)(context, id, cursor);
+}
+
+/**
+ * Remove the credential identified by `cred', `which' from `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ if(id->ops->remove_cred == NULL) {
+ krb5_set_error_message(context,
+ EACCES,
+ "ccache %s does not support remove_cred",
+ id->ops->prefix);
+ return EACCES; /* XXX */
+ }
+ return (*id->ops->remove_cred)(context, id, which, cred);
+}
+
+/**
+ * Set the flags of `id' to `flags'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return (*id->ops->set_flags)(context, id, flags);
+}
+
+/**
+ * Copy the contents of `from' to `to'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_copy_cache_match(krb5_context context,
+ const krb5_ccache from,
+ krb5_ccache to,
+ krb5_flags whichfields,
+ const krb5_creds * mcreds,
+ unsigned int *matched)
+{
+ krb5_error_code ret;
+ krb5_cc_cursor cursor;
+ krb5_creds cred;
+ krb5_principal princ;
+
+ ret = krb5_cc_get_principal(context, from, &princ);
+ if (ret)
+ return ret;
+ ret = krb5_cc_initialize(context, to, princ);
+ if (ret) {
+ krb5_free_principal(context, princ);
+ return ret;
+ }
+ ret = krb5_cc_start_seq_get(context, from, &cursor);
+ if (ret) {
+ krb5_free_principal(context, princ);
+ return ret;
+ }
+ if (matched)
+ *matched = 0;
+ while (ret == 0 &&
+ krb5_cc_next_cred_match(context, from, &cursor, &cred,
+ whichfields, mcreds) == 0) {
+ if (matched)
+ (*matched)++;
+ ret = krb5_cc_store_cred(context, to, &cred);
+ krb5_free_cred_contents(context, &cred);
+ }
+ krb5_cc_end_seq_get(context, from, &cursor);
+ krb5_free_principal(context, princ);
+ return ret;
+}
+
+/**
+ * Just like krb5_cc_copy_cache_match, but copy everything.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_copy_cache(krb5_context context,
+ const krb5_ccache from,
+ krb5_ccache to)
+{
+ return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
+}
+
+/**
+ * Return the version of `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_version(krb5_context context,
+ const krb5_ccache id)
+{
+ if(id->ops->get_version)
+ return (*id->ops->get_version)(context, id);
+ else
+ return 0;
+}
+
+/**
+ * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+void KRB5_LIB_FUNCTION
+krb5_cc_clear_mcred(krb5_creds *mcred)
+{
+ memset(mcred, 0, sizeof(*mcred));
+}
+
+/**
+ * Get the cc ops that is registered in `context' to handle the
+ * prefix. prefix can be a complete credential cache name or a
+ * prefix, the function will only use part up to the first colon (:)
+ * if there is one. If prefix the argument is NULL, the default ccache
+ * implemtation is returned.
+
+ * @return Returns NULL if ops not found.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const krb5_cc_ops *
+krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
+{
+ char *p, *p1;
+ int i;
+
+ if (prefix == NULL)
+ return KRB5_DEFAULT_CCTYPE;
+ if (prefix[0] == '/')
+ return &krb5_fcc_ops;
+
+ p = strdup(prefix);
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return NULL;
+ }
+ p1 = strchr(p, ':');
+ if (p1)
+ *p1 = '\0';
+
+ for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
+ if(strcmp(context->cc_ops[i].prefix, p) == 0) {
+ free(p);
+ return &context->cc_ops[i];
+ }
+ }
+ free(p);
+ return NULL;
+}
+
+struct krb5_cc_cache_cursor_data {
+ const krb5_cc_ops *ops;
+ krb5_cc_cursor cursor;
+};
+
+/**
+ * Start iterating over all caches of `type'. If `type' is NULL, the
+ * default type is * used. `cursor' is initialized to the beginning.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_get_first (krb5_context context,
+ const char *type,
+ krb5_cc_cache_cursor *cursor)
+{
+ const krb5_cc_ops *ops;
+ krb5_error_code ret;
+
+ if (type == NULL)
+ type = krb5_cc_default_name(context);
+
+ ops = krb5_cc_get_prefix_ops(context, type);
+ if (ops == NULL) {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ "Unknown type \"%s\" when iterating "
+ "trying to iterate the credential caches", type);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+
+ if (ops->get_cache_first == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ "Credential cache type %s doesn't support "
+ "iterations over caches", ops->prefix);
+ return KRB5_CC_NOSUPP;
+ }
+
+ *cursor = calloc(1, sizeof(**cursor));
+ if (*cursor == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*cursor)->ops = ops;
+
+ ret = ops->get_cache_first(context, &(*cursor)->cursor);
+ if (ret) {
+ free(*cursor);
+ *cursor = NULL;
+ }
+ return ret;
+}
+
+/**
+ * Retrieve the next cache pointed to by (`cursor') in `id'
+ * and advance `cursor'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_next (krb5_context context,
+ krb5_cc_cache_cursor cursor,
+ krb5_ccache *id)
+{
+ return cursor->ops->get_cache_next(context, cursor->cursor, id);
+}
+
+/**
+ * Destroy the cursor `cursor'.
+ *
+ * @return Return 0 or an error code.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_end_seq_get (krb5_context context,
+ krb5_cc_cache_cursor cursor)
+{
+ krb5_error_code ret;
+ ret = cursor->ops->end_cache_get(context, cursor->cursor);
+ cursor->ops = NULL;
+ free(cursor);
+ return ret;
+}
+
+/**
+ * Search for a matching credential cache of type `type' that have the
+ * `principal' as the default principal. If NULL is used for `type',
+ * the default type is used. On success, `id' needs to be freed with
+ * krb5_cc_close or krb5_cc_destroy.
+ *
+ * @return On failure, error code is returned and `id' is set to NULL.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_match (krb5_context context,
+ krb5_principal client,
+ const char *type,
+ krb5_ccache *id)
+{
+ krb5_cc_cache_cursor cursor;
+ krb5_error_code ret;
+ krb5_ccache cache = NULL;
+
+ *id = NULL;
+
+ ret = krb5_cc_cache_get_first (context, type, &cursor);
+ if (ret)
+ return ret;
+
+ while ((ret = krb5_cc_cache_next (context, cursor, &cache)) == 0) {
+ krb5_principal principal;
+
+ ret = krb5_cc_get_principal(context, cache, &principal);
+ if (ret == 0) {
+ krb5_boolean match;
+
+ match = krb5_principal_compare(context, principal, client);
+ krb5_free_principal(context, principal);
+ if (match)
+ break;
+ }
+
+ krb5_cc_close(context, cache);
+ cache = NULL;
+ }
+
+ krb5_cc_cache_end_seq_get(context, cursor);
+
+ if (cache == NULL) {
+ char *str;
+
+ krb5_unparse_name(context, client, &str);
+
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ "Principal %s not found in a "
+ "credential cache",
+ str ? str : "<out of memory>");
+ if (str)
+ free(str);
+ return KRB5_CC_NOTFOUND;
+ }
+ *id = cache;
+
+ return 0;
+}
+
+/**
+ * Move the content from one credential cache to another. The
+ * operation is an atomic switch.
+ *
+ * @param context a Keberos context
+ * @param from the credential cache to move the content from
+ * @param to the credential cache to move the content to
+
+ * @return On sucess, from is freed. On failure, error code is
+ * returned and from and to are both still allocated.
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code
+krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_error_code ret;
+
+ if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ "Moving credentials between diffrent "
+ "types not yet supported");
+ return KRB5_CC_NOSUPP;
+ }
+
+ ret = (*to->ops->move)(context, from, to);
+ if (ret == 0) {
+ memset(from, 0, sizeof(*from));
+ free(from);
+ }
+ return ret;
+}
+
+#define KRB5_CONF_NAME "@krb5_ccache_conf_data"
+
+static krb5_error_code
+build_conf_principals(krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *name, krb5_creds *cred)
+{
+ krb5_principal client;
+ krb5_error_code ret;
+ char *pname = NULL;
+
+ memset(cred, 0, sizeof(*cred));
+
+ ret = krb5_cc_get_principal(context, id, &client);
+ if (ret)
+ return ret;
+
+ if (principal) {
+ ret = krb5_unparse_name(context, principal, &pname);
+ if (ret)
+ return ret;
+ }
+
+ ret = krb5_make_principal(context, &cred->server,
+ krb5_principal_get_realm(context, client),
+ KRB5_CONF_NAME, name, pname, NULL);
+ free(pname);
+ if (ret) {
+ krb5_free_principal(context, client);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, client, &cred->client);
+ krb5_free_principal(context, client);
+ return ret;
+}
+
+/**
+ * Store some configuration for the credential cache in the cache.
+ * Existing configuration under the same name is over-written.
+ *
+ * @param context a Keberos context
+ * @param id the credential cache to store the data for
+ * @param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * @param name name under which the configuraion is stored.
+ * @param data data to store
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_set_config(krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *name, krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_creds cred;
+
+ ret = build_conf_principals(context, id, principal, name, &cred);
+ if (ret)
+ goto out;
+
+ /* Remove old configuration */
+ ret = krb5_cc_remove_cred(context, id, 0, &cred);
+ if (ret)
+ goto out;
+
+ /* not that anyone care when this expire */
+ cred.times.authtime = time(NULL);
+ cred.times.endtime = cred.times.authtime + 3600 * 24 * 30;
+
+ ret = krb5_data_copy(&cred.ticket, data->data, data->length);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_store_cred(context, id, &cred);
+
+out:
+ krb5_free_cred_contents (context, &cred);
+ return ret;
+}
+
+/**
+ * Get some configuration for the credential cache in the cache.
+ *
+ * @param context a Keberos context
+ * @param id the credential cache to store the data for
+ * @param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * @param name name under which the configuraion is stored.
+ * @param data data to fetched, free with krb5_data_free()
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_config(krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *name, krb5_data *data)
+{
+ krb5_creds mcred, cred;
+ krb5_error_code ret;
+
+ memset(&cred, 0, sizeof(cred));
+ krb5_data_zero(data);
+
+ ret = build_conf_principals(context, id, principal, name, &mcred);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
+ if (ret)
+ goto out;
+
+ ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length);
+
+out:
+ krb5_free_cred_contents (context, &cred);
+ krb5_free_cred_contents (context, &mcred);
+ return ret;
+}
+
diff --git a/source4/heimdal/lib/krb5/changepw.c b/source4/heimdal/lib/krb5/changepw.c
new file mode 100644
index 0000000000..d57ed9e3b8
--- /dev/null
+++ b/source4/heimdal/lib/krb5/changepw.c
@@ -0,0 +1,862 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+#undef __attribute__
+#define __attribute__(X)
+
+
+static void
+str2data (krb5_data *d,
+ const char *fmt,
+ ...) __attribute__ ((format (printf, 2, 3)));
+
+static void
+str2data (krb5_data *d,
+ const char *fmt,
+ ...)
+{
+ va_list args;
+ char *str;
+
+ va_start(args, fmt);
+ d->length = vasprintf (&str, fmt, args);
+ va_end(args);
+ d->data = str;
+}
+
+/*
+ * Change password protocol defined by
+ * draft-ietf-cat-kerb-chg-password-02.txt
+ *
+ * Share the response part of the protocol with MS set password
+ * (RFC3244)
+ */
+
+static krb5_error_code
+chgpw_send_request (krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_creds *creds,
+ krb5_principal targprinc,
+ int is_stream,
+ int sock,
+ const char *passwd,
+ const char *host)
+{
+ krb5_error_code ret;
+ krb5_data ap_req_data;
+ krb5_data krb_priv_data;
+ krb5_data passwd_data;
+ size_t len;
+ u_char header[6];
+ u_char *p;
+ struct iovec iov[3];
+ struct msghdr msghdr;
+
+ if (is_stream)
+ return KRB5_KPASSWD_MALFORMED;
+
+ if (targprinc &&
+ krb5_principal_compare(context, creds->client, targprinc) != TRUE)
+ return KRB5_KPASSWD_MALFORMED;
+
+ krb5_data_zero (&ap_req_data);
+
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
+ NULL, /* in_data */
+ creds,
+ &ap_req_data);
+ if (ret)
+ return ret;
+
+ passwd_data.data = rk_UNCONST(passwd);
+ passwd_data.length = strlen(passwd);
+
+ krb5_data_zero (&krb_priv_data);
+
+ ret = krb5_mk_priv (context,
+ *auth_context,
+ &passwd_data,
+ &krb_priv_data,
+ NULL);
+ if (ret)
+ goto out2;
+
+ len = 6 + ap_req_data.length + krb_priv_data.length;
+ p = header;
+ *p++ = (len >> 8) & 0xFF;
+ *p++ = (len >> 0) & 0xFF;
+ *p++ = 0;
+ *p++ = 1;
+ *p++ = (ap_req_data.length >> 8) & 0xFF;
+ *p++ = (ap_req_data.length >> 0) & 0xFF;
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = iov;
+ msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
+#if 0
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+#endif
+
+ iov[0].iov_base = (void*)header;
+ iov[0].iov_len = 6;
+ iov[1].iov_base = ap_req_data.data;
+ iov[1].iov_len = ap_req_data.length;
+ iov[2].iov_base = krb_priv_data.data;
+ iov[2].iov_len = krb_priv_data.length;
+
+ if (sendmsg (sock, &msghdr, 0) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "sendmsg %s: %s",
+ host, strerror(ret));
+ }
+
+ krb5_data_free (&krb_priv_data);
+out2:
+ krb5_data_free (&ap_req_data);
+ return ret;
+}
+
+/*
+ * Set password protocol as defined by RFC3244 --
+ * Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols
+ */
+
+static krb5_error_code
+setpw_send_request (krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_creds *creds,
+ krb5_principal targprinc,
+ int is_stream,
+ int sock,
+ const char *passwd,
+ const char *host)
+{
+ krb5_error_code ret;
+ krb5_data ap_req_data;
+ krb5_data krb_priv_data;
+ krb5_data pwd_data;
+ ChangePasswdDataMS chpw;
+ size_t len;
+ u_char header[4 + 6];
+ u_char *p;
+ struct iovec iov[3];
+ struct msghdr msghdr;
+
+ krb5_data_zero (&ap_req_data);
+
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
+ NULL, /* in_data */
+ creds,
+ &ap_req_data);
+ if (ret)
+ return ret;
+
+ chpw.newpasswd.length = strlen(passwd);
+ chpw.newpasswd.data = rk_UNCONST(passwd);
+ if (targprinc) {
+ chpw.targname = &targprinc->name;
+ chpw.targrealm = &targprinc->realm;
+ } else {
+ chpw.targname = NULL;
+ chpw.targrealm = NULL;
+ }
+
+ ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length,
+ &chpw, &len, ret);
+ if (ret) {
+ krb5_data_free (&ap_req_data);
+ return ret;
+ }
+
+ if(pwd_data.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_mk_priv (context,
+ *auth_context,
+ &pwd_data,
+ &krb_priv_data,
+ NULL);
+ if (ret)
+ goto out2;
+
+ len = 6 + ap_req_data.length + krb_priv_data.length;
+ p = header;
+ if (is_stream) {
+ _krb5_put_int(p, len, 4);
+ p += 4;
+ }
+ *p++ = (len >> 8) & 0xFF;
+ *p++ = (len >> 0) & 0xFF;
+ *p++ = 0xff;
+ *p++ = 0x80;
+ *p++ = (ap_req_data.length >> 8) & 0xFF;
+ *p++ = (ap_req_data.length >> 0) & 0xFF;
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = iov;
+ msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
+#if 0
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+#endif
+
+ iov[0].iov_base = (void*)header;
+ if (is_stream)
+ iov[0].iov_len = 10;
+ else
+ iov[0].iov_len = 6;
+ iov[1].iov_base = ap_req_data.data;
+ iov[1].iov_len = ap_req_data.length;
+ iov[2].iov_base = krb_priv_data.data;
+ iov[2].iov_len = krb_priv_data.length;
+
+ if (sendmsg (sock, &msghdr, 0) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "sendmsg %s: %s",
+ host, strerror(ret));
+ }
+
+ krb5_data_free (&krb_priv_data);
+out2:
+ krb5_data_free (&ap_req_data);
+ krb5_data_free (&pwd_data);
+ return ret;
+}
+
+static krb5_error_code
+process_reply (krb5_context context,
+ krb5_auth_context auth_context,
+ int is_stream,
+ int sock,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string,
+ const char *host)
+{
+ krb5_error_code ret;
+ u_char reply[1024 * 3];
+ ssize_t len;
+ uint16_t pkt_len, pkt_ver;
+ krb5_data ap_rep_data;
+ int save_errno;
+
+ len = 0;
+ if (is_stream) {
+ while (len < sizeof(reply)) {
+ unsigned long size;
+
+ ret = recvfrom (sock, reply + len, sizeof(reply) - len,
+ 0, NULL, NULL);
+ if (ret < 0) {
+ save_errno = errno;
+ krb5_set_error_message(context, save_errno,
+ "recvfrom %s: %s",
+ host, strerror(save_errno));
+ return save_errno;
+ } else if (ret == 0) {
+ krb5_set_error_message(context, 1,"recvfrom timeout %s", host);
+ return 1;
+ }
+ len += ret;
+ if (len < 4)
+ continue;
+ _krb5_get_int(reply, &size, 4);
+ if (size + 4 < len)
+ continue;
+ memmove(reply, reply + 4, size);
+ len = size;
+ break;
+ }
+ if (len == sizeof(reply)) {
+ krb5_set_error_message(context, ENOMEM,
+ "message too large from %s",
+ host);
+ return ENOMEM;
+ }
+ } else {
+ ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL);
+ if (ret < 0) {
+ save_errno = errno;
+ krb5_set_error_message(context, save_errno,
+ "recvfrom %s: %s",
+ host, strerror(save_errno));
+ return save_errno;
+ }
+ len = ret;
+ }
+
+ if (len < 6) {
+ str2data (result_string, "server %s sent to too short message "
+ "(%ld bytes)", host, (long)len);
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ pkt_len = (reply[0] << 8) | (reply[1]);
+ pkt_ver = (reply[2] << 8) | (reply[3]);
+
+ if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) {
+ KRB_ERROR error;
+ size_t size;
+ u_char *p;
+
+ memset(&error, 0, sizeof(error));
+
+ ret = decode_KRB_ERROR(reply, len, &error, &size);
+ if (ret)
+ return ret;
+
+ if (error.e_data->length < 2) {
+ str2data(result_string, "server %s sent too short "
+ "e_data to print anything usable", host);
+ free_KRB_ERROR(&error);
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ p = error.e_data->data;
+ *result_code = (p[0] << 8) | p[1];
+ if (error.e_data->length == 2)
+ str2data(result_string, "server only sent error code");
+ else
+ krb5_data_copy (result_string,
+ p + 2,
+ error.e_data->length - 2);
+ free_KRB_ERROR(&error);
+ return 0;
+ }
+
+ if (pkt_len != len) {
+ str2data (result_string, "client: wrong len in reply");
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+ if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) {
+ str2data (result_string,
+ "client: wrong version number (%d)", pkt_ver);
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ ap_rep_data.data = reply + 6;
+ ap_rep_data.length = (reply[4] << 8) | (reply[5]);
+
+ if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) {
+ str2data (result_string, "client: wrong AP len in reply");
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ if (ap_rep_data.length) {
+ krb5_ap_rep_enc_part *ap_rep;
+ krb5_data priv_data;
+ u_char *p;
+
+ priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length;
+ priv_data.length = len - ap_rep_data.length - 6;
+
+ ret = krb5_rd_rep (context,
+ auth_context,
+ &ap_rep_data,
+ &ap_rep);
+ if (ret)
+ return ret;
+
+ krb5_free_ap_rep_enc_part (context, ap_rep);
+
+ ret = krb5_rd_priv (context,
+ auth_context,
+ &priv_data,
+ result_code_string,
+ NULL);
+ if (ret) {
+ krb5_data_free (result_code_string);
+ return ret;
+ }
+
+ if (result_code_string->length < 2) {
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ str2data (result_string,
+ "client: bad length in result");
+ return 0;
+ }
+
+ p = result_code_string->data;
+
+ *result_code = (p[0] << 8) | p[1];
+ krb5_data_copy (result_string,
+ (unsigned char*)result_code_string->data + 2,
+ result_code_string->length - 2);
+ return 0;
+ } else {
+ KRB_ERROR error;
+ size_t size;
+ u_char *p;
+
+ ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
+ if (ret) {
+ return ret;
+ }
+ if (error.e_data->length < 2) {
+ krb5_warnx (context, "too short e_data to print anything usable");
+ return 1; /* XXX */
+ }
+
+ p = error.e_data->data;
+ *result_code = (p[0] << 8) | p[1];
+ krb5_data_copy (result_string,
+ p + 2,
+ error.e_data->length - 2);
+ return 0;
+ }
+}
+
+
+/*
+ * change the password using the credentials in `creds' (for the
+ * principal indicated in them) to `newpw', storing the result of
+ * the operation in `result_*' and an error code or 0.
+ */
+
+typedef krb5_error_code (*kpwd_send_request) (krb5_context,
+ krb5_auth_context *,
+ krb5_creds *,
+ krb5_principal,
+ int,
+ int,
+ const char *,
+ const char *);
+typedef krb5_error_code (*kpwd_process_reply) (krb5_context,
+ krb5_auth_context,
+ int,
+ int,
+ int *,
+ krb5_data *,
+ krb5_data *,
+ const char *);
+
+static struct kpwd_proc {
+ const char *name;
+ int flags;
+#define SUPPORT_TCP 1
+#define SUPPORT_UDP 2
+ kpwd_send_request send_req;
+ kpwd_process_reply process_rep;
+} procs[] = {
+ {
+ "MS set password",
+ SUPPORT_TCP|SUPPORT_UDP,
+ setpw_send_request,
+ process_reply
+ },
+ {
+ "change password",
+ SUPPORT_UDP,
+ chgpw_send_request,
+ process_reply
+ },
+ { NULL }
+};
+
+static struct kpwd_proc *
+find_chpw_proto(const char *name)
+{
+ struct kpwd_proc *p;
+ for (p = procs; p->name != NULL; p++) {
+ if (strcmp(p->name, name) == 0)
+ return p;
+ }
+ return NULL;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+change_password_loop (krb5_context context,
+ krb5_creds *creds,
+ krb5_principal targprinc,
+ const char *newpw,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string,
+ struct kpwd_proc *proc)
+{
+ krb5_error_code ret;
+ krb5_auth_context auth_context = NULL;
+ krb5_krbhst_handle handle = NULL;
+ krb5_krbhst_info *hi;
+ int sock;
+ unsigned int i;
+ int done = 0;
+ krb5_realm realm;
+
+ if (targprinc)
+ realm = targprinc->realm;
+ else
+ realm = creds->client->realm;
+
+ ret = krb5_auth_con_init (context, &auth_context);
+ if (ret)
+ return ret;
+
+ krb5_auth_con_setflags (context, auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+
+ ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle);
+ if (ret)
+ goto out;
+
+ while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) {
+ struct addrinfo *ai, *a;
+ int is_stream;
+
+ switch (hi->proto) {
+ case KRB5_KRBHST_UDP:
+ if ((proc->flags & SUPPORT_UDP) == 0)
+ continue;
+ is_stream = 0;
+ break;
+ case KRB5_KRBHST_TCP:
+ if ((proc->flags & SUPPORT_TCP) == 0)
+ continue;
+ is_stream = 1;
+ break;
+ default:
+ continue;
+ }
+
+ ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+ if (ret)
+ continue;
+
+ for (a = ai; !done && a != NULL; a = a->ai_next) {
+ int replied = 0;
+
+ sock = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
+ if (sock < 0)
+ continue;
+ rk_cloexec(sock);
+
+ ret = connect(sock, a->ai_addr, a->ai_addrlen);
+ if (ret < 0) {
+ close (sock);
+ goto out;
+ }
+
+ ret = krb5_auth_con_genaddrs (context, auth_context, sock,
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
+ if (ret) {
+ close (sock);
+ goto out;
+ }
+
+ for (i = 0; !done && i < 5; ++i) {
+ fd_set fdset;
+ struct timeval tv;
+
+ if (!replied) {
+ replied = 0;
+
+ ret = (*proc->send_req) (context,
+ &auth_context,
+ creds,
+ targprinc,
+ is_stream,
+ sock,
+ newpw,
+ hi->hostname);
+ if (ret) {
+ close(sock);
+ goto out;
+ }
+ }
+
+ if (sock >= FD_SETSIZE) {
+ ret = ERANGE;
+ krb5_set_error_message(context, ret,
+ "fd %d too large", sock);
+ close (sock);
+ goto out;
+ }
+
+ FD_ZERO(&fdset);
+ FD_SET(sock, &fdset);
+ tv.tv_usec = 0;
+ tv.tv_sec = 1 + (1 << i);
+
+ ret = select (sock + 1, &fdset, NULL, NULL, &tv);
+ if (ret < 0 && errno != EINTR) {
+ close(sock);
+ goto out;
+ }
+ if (ret == 1) {
+ ret = (*proc->process_rep) (context,
+ auth_context,
+ is_stream,
+ sock,
+ result_code,
+ result_code_string,
+ result_string,
+ hi->hostname);
+ if (ret == 0)
+ done = 1;
+ else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL)
+ replied = 1;
+ } else {
+ ret = KRB5_KDC_UNREACH;
+ }
+ }
+ close (sock);
+ }
+ }
+
+ out:
+ krb5_krbhst_free (context, handle);
+ krb5_auth_con_free (context, auth_context);
+
+ if (ret == KRB5_KDC_UNREACH) {
+ krb5_set_error_message(context,
+ ret,
+ "unable to reach any changepw server "
+ " in realm %s", realm);
+ *result_code = KRB5_KPASSWD_HARDERROR;
+ }
+ return ret;
+}
+
+#ifndef HEIMDAL_SMALLER
+
+/**
+ * krb5_change_password() is deprecated, use krb5_set_password().
+ *
+ * @param context a Keberos context
+ * @param creds
+ * @param newpw
+ * @param result_code
+ * @param result_code_string
+ * @param result_string
+ *
+ * @return On sucess password is changed.
+
+ * @ingroup @krb5_deprecated
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_change_password (krb5_context context,
+ krb5_creds *creds,
+ const char *newpw,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string)
+ __attribute__((deprecated))
+{
+ struct kpwd_proc *p = find_chpw_proto("change password");
+
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ result_code_string->data = result_string->data = NULL;
+ result_code_string->length = result_string->length = 0;
+
+ if (p == NULL)
+ return KRB5_KPASSWD_MALFORMED;
+
+ return change_password_loop(context, creds, NULL, newpw,
+ result_code, result_code_string,
+ result_string, p);
+}
+#endif /* HEIMDAL_SMALLER */
+
+/**
+ * Change passwrod using creds.
+ *
+ * @param context a Keberos context
+ * @param creds The initial kadmin/passwd for the principal or an admin principal
+ * @param newpw The new password to set
+ * @param targprinc if unset, the default principal is used.
+ * @param result_code Result code, KRB5_KPASSWD_SUCCESS is when password is changed.
+ * @param result_code_string binary message from the server, contains
+ * at least the result_code.
+ * @param result_string A message from the kpasswd service or the
+ * library in human printable form. The string is NUL terminated.
+ *
+ * @return On sucess and *result_code is KRB5_KPASSWD_SUCCESS, the password is changed.
+
+ * @ingroup @krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_password(krb5_context context,
+ krb5_creds *creds,
+ const char *newpw,
+ krb5_principal targprinc,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string)
+{
+ krb5_principal principal = NULL;
+ krb5_error_code ret = 0;
+ int i;
+
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ krb5_data_zero(result_code_string);
+ krb5_data_zero(result_string);
+
+ if (targprinc == NULL) {
+ ret = krb5_get_default_principal(context, &principal);
+ if (ret)
+ return ret;
+ } else
+ principal = targprinc;
+
+ for (i = 0; procs[i].name != NULL; i++) {
+ *result_code = 0;
+ ret = change_password_loop(context, creds, principal, newpw,
+ result_code, result_code_string,
+ result_string,
+ &procs[i]);
+ if (ret == 0 && *result_code == 0)
+ break;
+ }
+
+ if (targprinc == NULL)
+ krb5_free_principal(context, principal);
+ return ret;
+}
+
+#ifndef HEIMDAL_SMALLER
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_password_using_ccache(krb5_context context,
+ krb5_ccache ccache,
+ const char *newpw,
+ krb5_principal targprinc,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string)
+{
+ krb5_creds creds, *credsp;
+ krb5_error_code ret;
+ krb5_principal principal = NULL;
+
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ result_code_string->data = result_string->data = NULL;
+ result_code_string->length = result_string->length = 0;
+
+ memset(&creds, 0, sizeof(creds));
+
+ if (targprinc == NULL) {
+ ret = krb5_cc_get_principal(context, ccache, &principal);
+ if (ret)
+ return ret;
+ } else
+ principal = targprinc;
+
+ ret = krb5_make_principal(context, &creds.server,
+ krb5_principal_get_realm(context, principal),
+ "kadmin", "changepw", NULL);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_get_principal(context, ccache, &creds.client);
+ if (ret) {
+ krb5_free_principal(context, creds.server);
+ goto out;
+ }
+
+ ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
+ krb5_free_principal(context, creds.server);
+ krb5_free_principal(context, creds.client);
+ if (ret)
+ goto out;
+
+ ret = krb5_set_password(context,
+ credsp,
+ newpw,
+ principal,
+ result_code,
+ result_code_string,
+ result_string);
+
+ krb5_free_creds(context, credsp);
+
+ return ret;
+ out:
+ if (targprinc == NULL)
+ krb5_free_principal(context, principal);
+ return ret;
+}
+
+#endif /* !HEIMDAL_SMALLER */
+
+/*
+ *
+ */
+
+const char* KRB5_LIB_FUNCTION
+krb5_passwd_result_to_string (krb5_context context,
+ int result)
+{
+ static const char *strings[] = {
+ "Success",
+ "Malformed",
+ "Hard error",
+ "Auth error",
+ "Soft error" ,
+ "Access denied",
+ "Bad version",
+ "Initial flag needed"
+ };
+
+ if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)
+ return "unknown result code";
+ else
+ return strings[result];
+}
diff --git a/source4/heimdal/lib/krb5/codec.c b/source4/heimdal/lib/krb5/codec.c
new file mode 100644
index 0000000000..478f77ecef
--- /dev/null
+++ b/source4/heimdal/lib/krb5/codec.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 1998 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncTicketPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncTicketPart *t,
+ size_t *len)
+{
+ return decode_EncTicketPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncTicketPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncTicketPart *t,
+ size_t *len)
+{
+ return encode_EncTicketPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncASRepPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncASRepPart *t,
+ size_t *len)
+{
+ return decode_EncASRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncASRepPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncASRepPart *t,
+ size_t *len)
+{
+ return encode_EncASRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncTGSRepPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncTGSRepPart *t,
+ size_t *len)
+{
+ return decode_EncTGSRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncTGSRepPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncTGSRepPart *t,
+ size_t *len)
+{
+ return encode_EncTGSRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncAPRepPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncAPRepPart *t,
+ size_t *len)
+{
+ return decode_EncAPRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncAPRepPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncAPRepPart *t,
+ size_t *len)
+{
+ return encode_EncAPRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_Authenticator (krb5_context context,
+ const void *data,
+ size_t length,
+ Authenticator *t,
+ size_t *len)
+{
+ return decode_Authenticator(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_Authenticator (krb5_context context,
+ void *data,
+ size_t length,
+ Authenticator *t,
+ size_t *len)
+{
+ return encode_Authenticator(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncKrbCredPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncKrbCredPart *t,
+ size_t *len)
+{
+ return decode_EncKrbCredPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncKrbCredPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncKrbCredPart *t,
+ size_t *len)
+{
+ return encode_EncKrbCredPart (data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_ETYPE_INFO (krb5_context context,
+ const void *data,
+ size_t length,
+ ETYPE_INFO *t,
+ size_t *len)
+{
+ return decode_ETYPE_INFO(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_ETYPE_INFO (krb5_context context,
+ void *data,
+ size_t length,
+ ETYPE_INFO *t,
+ size_t *len)
+{
+ return encode_ETYPE_INFO (data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_ETYPE_INFO2 (krb5_context context,
+ const void *data,
+ size_t length,
+ ETYPE_INFO2 *t,
+ size_t *len)
+{
+ return decode_ETYPE_INFO2(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_ETYPE_INFO2 (krb5_context context,
+ void *data,
+ size_t length,
+ ETYPE_INFO2 *t,
+ size_t *len)
+{
+ return encode_ETYPE_INFO2 (data, length, t, len);
+}
diff --git a/source4/heimdal/lib/krb5/config_file.c b/source4/heimdal/lib/krb5/config_file.c
new file mode 100644
index 0000000000..f7f7957b04
--- /dev/null
+++ b/source4/heimdal/lib/krb5/config_file.c
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+#ifndef HAVE_NETINFO
+
+/* Gaah! I want a portable funopen */
+struct fileptr {
+ const char *s;
+ FILE *f;
+};
+
+static char *
+config_fgets(char *str, size_t len, struct fileptr *ptr)
+{
+ /* XXX this is not correct, in that they don't do the same if the
+ line is longer than len */
+ if(ptr->f != NULL)
+ return fgets(str, len, ptr->f);
+ else {
+ /* this is almost strsep_copy */
+ const char *p;
+ ssize_t l;
+ if(*ptr->s == '\0')
+ return NULL;
+ p = ptr->s + strcspn(ptr->s, "\n");
+ if(*p == '\n')
+ p++;
+ l = min(len, p - ptr->s);
+ if(len > 0) {
+ memcpy(str, ptr->s, l);
+ str[l] = '\0';
+ }
+ ptr->s = p;
+ return str;
+ }
+}
+
+static krb5_error_code parse_section(char *p, krb5_config_section **s,
+ krb5_config_section **res,
+ const char **error_message);
+static krb5_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ krb5_config_binding **b,
+ krb5_config_binding **parent,
+ const char **error_message);
+static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
+ krb5_config_binding **parent,
+ const char **error_message);
+
+static krb5_config_section *
+get_entry(krb5_config_section **parent, const char *name, int type)
+{
+ krb5_config_section **q;
+
+ for(q = parent; *q != NULL; q = &(*q)->next)
+ if(type == krb5_config_list &&
+ type == (*q)->type &&
+ strcmp(name, (*q)->name) == 0)
+ return *q;
+ *q = calloc(1, sizeof(**q));
+ if(*q == NULL)
+ return NULL;
+ (*q)->name = strdup(name);
+ (*q)->type = type;
+ if((*q)->name == NULL) {
+ free(*q);
+ *q = NULL;
+ return NULL;
+ }
+ return *q;
+}
+
+/*
+ * Parse a section:
+ *
+ * [section]
+ * foo = bar
+ * b = {
+ * a
+ * }
+ * ...
+ *
+ * starting at the line in `p', storing the resulting structure in
+ * `s' and hooking it into `parent'.
+ * Store the error message in `error_message'.
+ */
+
+static krb5_error_code
+parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
+ const char **error_message)
+{
+ char *p1;
+ krb5_config_section *tmp;
+
+ p1 = strchr (p + 1, ']');
+ if (p1 == NULL) {
+ *error_message = "missing ]";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ *p1 = '\0';
+ tmp = get_entry(parent, p + 1, krb5_config_list);
+ if(tmp == NULL) {
+ *error_message = "out of memory";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ *s = tmp;
+ return 0;
+}
+
+/*
+ * Parse a brace-enclosed list from `f', hooking in the structure at
+ * `parent'.
+ * Store the error message in `error_message'.
+ */
+
+static krb5_error_code
+parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent,
+ const char **error_message)
+{
+ char buf[BUFSIZ];
+ krb5_error_code ret;
+ krb5_config_binding *b = NULL;
+ unsigned beg_lineno = *lineno;
+
+ while(config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';' || *p == '\0')
+ continue;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '}')
+ return 0;
+ if (*p == '\0')
+ continue;
+ ret = parse_binding (f, lineno, p, &b, parent, error_message);
+ if (ret)
+ return ret;
+ }
+ *lineno = beg_lineno;
+ *error_message = "unclosed {";
+ return KRB5_CONFIG_BADFORMAT;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ krb5_config_binding **b, krb5_config_binding **parent,
+ const char **error_message)
+{
+ krb5_config_binding *tmp;
+ char *p1, *p2;
+ krb5_error_code ret = 0;
+
+ p1 = p;
+ while (*p && *p != '=' && !isspace((unsigned char)*p))
+ ++p;
+ if (*p == '\0') {
+ *error_message = "missing =";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ p2 = p;
+ while (isspace((unsigned char)*p))
+ ++p;
+ if (*p != '=') {
+ *error_message = "missing =";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ ++p;
+ while(isspace((unsigned char)*p))
+ ++p;
+ *p2 = '\0';
+ if (*p == '{') {
+ tmp = get_entry(parent, p1, krb5_config_list);
+ if (tmp == NULL) {
+ *error_message = "out of memory";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ ret = parse_list (f, lineno, &tmp->u.list, error_message);
+ } else {
+ tmp = get_entry(parent, p1, krb5_config_string);
+ if (tmp == NULL) {
+ *error_message = "out of memory";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ p1 = p;
+ p = p1 + strlen(p1);
+ while(p > p1 && isspace((unsigned char)*(p-1)))
+ --p;
+ *p = '\0';
+ tmp->u.string = strdup(p1);
+ }
+ *b = tmp;
+ return ret;
+}
+
+/*
+ * Parse the config file `fname', generating the structures into `res'
+ * returning error messages in `error_message'
+ */
+
+static krb5_error_code
+krb5_config_parse_debug (struct fileptr *f,
+ krb5_config_section **res,
+ unsigned *lineno,
+ const char **error_message)
+{
+ krb5_config_section *s = NULL;
+ krb5_config_binding *b = NULL;
+ char buf[BUFSIZ];
+ krb5_error_code ret;
+
+ while (config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';')
+ continue;
+ if (*p == '[') {
+ ret = parse_section(p, &s, res, error_message);
+ if (ret)
+ return ret;
+ b = NULL;
+ } else if (*p == '}') {
+ *error_message = "unmatched }";
+ return EINVAL; /* XXX */
+ } else if(*p != '\0') {
+ if (s == NULL) {
+ *error_message = "binding before section";
+ return EINVAL;
+ }
+ ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_string_multi(krb5_context context,
+ const char *string,
+ krb5_config_section **res)
+{
+ const char *str;
+ unsigned lineno = 0;
+ krb5_error_code ret;
+ struct fileptr f;
+ f.f = NULL;
+ f.s = string;
+
+ ret = krb5_config_parse_debug (&f, res, &lineno, &str);
+ if (ret) {
+ krb5_set_error_message (context, ret, "%s:%u: %s",
+ "<constant>", lineno, str);
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_file_multi (krb5_context context,
+ const char *fname,
+ krb5_config_section **res)
+{
+ const char *str;
+ unsigned lineno = 0;
+ krb5_error_code ret;
+ struct fileptr f;
+ f.f = fopen(fname, "r");
+ f.s = NULL;
+ if(f.f == NULL) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "open %s: %s",
+ fname, strerror(ret));
+ return ret;
+ }
+
+ ret = krb5_config_parse_debug (&f, res, &lineno, &str);
+ fclose(f.f);
+ if (ret) {
+ krb5_set_error_message (context, ret, "%s:%u: %s", fname, lineno, str);
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_file (krb5_context context,
+ const char *fname,
+ krb5_config_section **res)
+{
+ *res = NULL;
+ return krb5_config_parse_file_multi(context, fname, res);
+}
+
+#endif /* !HAVE_NETINFO */
+
+static void
+free_binding (krb5_context context, krb5_config_binding *b)
+{
+ krb5_config_binding *next_b;
+
+ while (b) {
+ free (b->name);
+ if (b->type == krb5_config_string)
+ free (b->u.string);
+ else if (b->type == krb5_config_list)
+ free_binding (context, b->u.list);
+ else
+ krb5_abortx(context, "unknown binding type (%d) in free_binding",
+ b->type);
+ next_b = b->next;
+ free (b);
+ b = next_b;
+ }
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_file_free (krb5_context context, krb5_config_section *s)
+{
+ free_binding (context, s);
+ return 0;
+}
+
+const void *
+krb5_config_get_next (krb5_context context,
+ const krb5_config_section *c,
+ const krb5_config_binding **pointer,
+ int type,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = krb5_config_vget_next (context, c, pointer, type, args);
+ va_end(args);
+ return ret;
+}
+
+static const void *
+vget_next(krb5_context context,
+ const krb5_config_binding *b,
+ const krb5_config_binding **pointer,
+ int type,
+ const char *name,
+ va_list args)
+{
+ const char *p = va_arg(args, const char *);
+ while(b != NULL) {
+ if(strcmp(b->name, name) == 0) {
+ if(b->type == type && p == NULL) {
+ *pointer = b;
+ return b->u.generic;
+ } else if(b->type == krb5_config_list && p != NULL) {
+ return vget_next(context, b->u.list, pointer, type, p, args);
+ }
+ }
+ b = b->next;
+ }
+ return NULL;
+}
+
+const void *
+krb5_config_vget_next (krb5_context context,
+ const krb5_config_section *c,
+ const krb5_config_binding **pointer,
+ int type,
+ va_list args)
+{
+ const krb5_config_binding *b;
+ const char *p;
+
+ if(c == NULL)
+ c = context->cf;
+
+ if (c == NULL)
+ return NULL;
+
+ if (*pointer == NULL) {
+ /* first time here, walk down the tree looking for the right
+ section */
+ p = va_arg(args, const char *);
+ if (p == NULL)
+ return NULL;
+ return vget_next(context, c, pointer, type, p, args);
+ }
+
+ /* we were called again, so just look for more entries with the
+ same name and type */
+ for (b = (*pointer)->next; b != NULL; b = b->next) {
+ if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) {
+ *pointer = b;
+ return b->u.generic;
+ }
+ }
+ return NULL;
+}
+
+const void *
+krb5_config_get (krb5_context context,
+ const krb5_config_section *c,
+ int type,
+ ...)
+{
+ const void *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = krb5_config_vget (context, c, type, args);
+ va_end(args);
+ return ret;
+}
+
+const void *
+krb5_config_vget (krb5_context context,
+ const krb5_config_section *c,
+ int type,
+ va_list args)
+{
+ const krb5_config_binding *foo = NULL;
+
+ return krb5_config_vget_next (context, c, &foo, type, args);
+}
+
+const krb5_config_binding *
+krb5_config_get_list (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ const krb5_config_binding *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = krb5_config_vget_list (context, c, args);
+ va_end(args);
+ return ret;
+}
+
+const krb5_config_binding *
+krb5_config_vget_list (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget (context, c, krb5_config_list, args);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_get_string (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = krb5_config_vget_string (context, c, args);
+ va_end(args);
+ return ret;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_vget_string (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget (context, c, krb5_config_string, args);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_vget_string_default (krb5_context context,
+ const krb5_config_section *c,
+ const char *def_value,
+ va_list args)
+{
+ const char *ret;
+
+ ret = krb5_config_vget_string (context, c, args);
+ if (ret == NULL)
+ ret = def_value;
+ return ret;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_get_string_default (krb5_context context,
+ const krb5_config_section *c,
+ const char *def_value,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, def_value);
+ ret = krb5_config_vget_string_default (context, c, def_value, args);
+ va_end(args);
+ return ret;
+}
+
+char ** KRB5_LIB_FUNCTION
+krb5_config_vget_strings(krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ char **strings = NULL;
+ int nstr = 0;
+ const krb5_config_binding *b = NULL;
+ const char *p;
+
+ while((p = krb5_config_vget_next(context, c, &b,
+ krb5_config_string, args))) {
+ char *tmp = strdup(p);
+ char *pos = NULL;
+ char *s;
+ if(tmp == NULL)
+ goto cleanup;
+ s = strtok_r(tmp, " \t", &pos);
+ while(s){
+ char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp2 == NULL)
+ goto cleanup;
+ strings = tmp2;
+ strings[nstr] = strdup(s);
+ nstr++;
+ if(strings[nstr-1] == NULL)
+ goto cleanup;
+ s = strtok_r(NULL, " \t", &pos);
+ }
+ free(tmp);
+ }
+ if(nstr){
+ char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp == NULL)
+ goto cleanup;
+ strings = tmp;
+ strings[nstr] = NULL;
+ }
+ return strings;
+cleanup:
+ while(nstr--)
+ free(strings[nstr]);
+ free(strings);
+ return NULL;
+
+}
+
+char**
+krb5_config_get_strings(krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ char **ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_strings(context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_config_free_strings(char **strings)
+{
+ char **s = strings;
+ while(s && *s){
+ free(*s);
+ s++;
+ }
+ free(strings);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_vget_bool_default (krb5_context context,
+ const krb5_config_section *c,
+ krb5_boolean def_value,
+ va_list args)
+{
+ const char *str;
+ str = krb5_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ if(strcasecmp(str, "yes") == 0 ||
+ strcasecmp(str, "true") == 0 ||
+ atoi(str)) return TRUE;
+ return FALSE;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_vget_bool (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget_bool_default (context, c, FALSE, args);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_get_bool_default (krb5_context context,
+ const krb5_config_section *c,
+ krb5_boolean def_value,
+ ...)
+{
+ va_list ap;
+ krb5_boolean ret;
+ va_start(ap, def_value);
+ ret = krb5_config_vget_bool_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_get_bool (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ krb5_boolean ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_bool (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_time_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ krb5_deltat t;
+
+ str = krb5_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ if (krb5_string_to_deltat(str, &t))
+ return def_value;
+ return t;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_time (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget_time_default (context, c, -1, args);
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_time_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, def_value);
+ ret = krb5_config_vget_time_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_time (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_time (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_int_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ str = krb5_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ else {
+ char *endptr;
+ long l;
+ l = strtol(str, &endptr, 0);
+ if (endptr == str)
+ return def_value;
+ else
+ return l;
+ }
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_int (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget_int_default (context, c, -1, args);
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_int_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, def_value);
+ ret = krb5_config_vget_int_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_int (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_int (context, c, ap);
+ va_end(ap);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/config_file_netinfo.c b/source4/heimdal/lib/krb5/config_file_netinfo.c
new file mode 100644
index 0000000000..d51739ae37
--- /dev/null
+++ b/source4/heimdal/lib/krb5/config_file_netinfo.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+/*
+ * Netinfo implementation from Luke Howard <lukeh@xedoc.com.au>
+ */
+
+#ifdef HAVE_NETINFO
+#include <netinfo/ni.h>
+static ni_status
+ni_proplist2binding(ni_proplist *pl, krb5_config_section **ret)
+{
+ int i, j;
+ krb5_config_section **next = NULL;
+
+ for (i = 0; i < pl->ni_proplist_len; i++) {
+ if (!strcmp(pl->nipl_val[i].nip_name, "name"))
+ continue;
+
+ for (j = 0; j < pl->nipl_val[i].nip_val.ni_namelist_len; j++) {
+ krb5_config_binding *b;
+
+ b = malloc(sizeof(*b));
+ if (b == NULL)
+ return NI_FAILED;
+
+ b->next = NULL;
+ b->type = krb5_config_string;
+ b->name = ni_name_dup(pl->nipl_val[i].nip_name);
+ b->u.string = ni_name_dup(pl->nipl_val[i].nip_val.ninl_val[j]);
+
+ if (next == NULL) {
+ *ret = b;
+ } else {
+ *next = b;
+ }
+ next = &b->next;
+ }
+ }
+ return NI_OK;
+}
+
+static ni_status
+ni_idlist2binding(void *ni, ni_idlist *idlist, krb5_config_section **ret)
+{
+ int i;
+ ni_status nis;
+ krb5_config_section **next;
+
+ for (i = 0; i < idlist->ni_idlist_len; i++) {
+ ni_proplist pl;
+ ni_id nid;
+ ni_idlist children;
+ krb5_config_binding *b;
+ ni_index index;
+
+ nid.nii_instance = 0;
+ nid.nii_object = idlist->ni_idlist_val[i];
+
+ nis = ni_read(ni, &nid, &pl);
+
+ if (nis != NI_OK) {
+ return nis;
+ }
+ index = ni_proplist_match(pl, "name", NULL);
+ b = malloc(sizeof(*b));
+ if (b == NULL) return NI_FAILED;
+
+ if (i == 0) {
+ *ret = b;
+ } else {
+ *next = b;
+ }
+
+ b->type = krb5_config_list;
+ b->name = ni_name_dup(pl.nipl_val[index].nip_val.ninl_val[0]);
+ b->next = NULL;
+ b->u.list = NULL;
+
+ /* get the child directories */
+ nis = ni_children(ni, &nid, &children);
+ if (nis == NI_OK) {
+ nis = ni_idlist2binding(ni, &children, &b->u.list);
+ if (nis != NI_OK) {
+ return nis;
+ }
+ }
+
+ nis = ni_proplist2binding(&pl, b->u.list == NULL ? &b->u.list : &b->u.list->next);
+ ni_proplist_free(&pl);
+ if (nis != NI_OK) {
+ return nis;
+ }
+ next = &b->next;
+ }
+ ni_idlist_free(idlist);
+ return NI_OK;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_file (krb5_context context,
+ const char *fname,
+ krb5_config_section **res)
+{
+ void *ni = NULL, *lastni = NULL;
+ int i;
+ ni_status nis;
+ ni_id nid;
+ ni_idlist children;
+
+ krb5_config_section *s;
+ int ret;
+
+ s = NULL;
+
+ for (i = 0; i < 256; i++) {
+ if (i == 0) {
+ nis = ni_open(NULL, ".", &ni);
+ } else {
+ if (lastni != NULL) ni_free(lastni);
+ lastni = ni;
+ nis = ni_open(lastni, "..", &ni);
+ }
+ if (nis != NI_OK)
+ break;
+ nis = ni_pathsearch(ni, &nid, "/locations/kerberos");
+ if (nis == NI_OK) {
+ nis = ni_children(ni, &nid, &children);
+ if (nis != NI_OK)
+ break;
+ nis = ni_idlist2binding(ni, &children, &s);
+ break;
+ }
+ }
+
+ if (ni != NULL) ni_free(ni);
+ if (ni != lastni && lastni != NULL) ni_free(lastni);
+
+ ret = (nis == NI_OK) ? 0 : -1;
+ if (ret == 0) {
+ *res = s;
+ } else {
+ *res = NULL;
+ }
+ return ret;
+}
+#endif /* HAVE_NETINFO */
diff --git a/source4/heimdal/lib/krb5/constants.c b/source4/heimdal/lib/krb5/constants.c
new file mode 100644
index 0000000000..dc96bcb632
--- /dev/null
+++ b/source4/heimdal/lib/krb5/constants.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+KRB5_LIB_VARIABLE const char *krb5_config_file =
+#ifdef __APPLE__
+"/Library/Preferences/edu.mit.Kerberos:"
+#endif
+SYSCONFDIR "/krb5.conf:/etc/krb5.conf";
+KRB5_LIB_VARIABLE const char *krb5_defkeyname = KEYTAB_DEFAULT;
diff --git a/source4/heimdal/lib/krb5/context.c b/source4/heimdal/lib/krb5/context.c
new file mode 100644
index 0000000000..358ab20349
--- /dev/null
+++ b/source4/heimdal/lib/krb5/context.c
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <com_err.h>
+
+RCSID("$Id$");
+
+#define INIT_FIELD(C, T, E, D, F) \
+ (C)->E = krb5_config_get_ ## T ## _default ((C), NULL, (D), \
+ "libdefaults", F, NULL)
+
+#define INIT_FLAG(C, O, V, D, F) \
+ do { \
+ if (krb5_config_get_bool_default((C), NULL, (D),"libdefaults", F, NULL)) { \
+ (C)->O |= V; \
+ } \
+ } while(0)
+
+/*
+ * Set the list of etypes `ret_etypes' from the configuration variable
+ * `name'
+ */
+
+static krb5_error_code
+set_etypes (krb5_context context,
+ const char *name,
+ krb5_enctype **ret_enctypes)
+{
+ char **etypes_str;
+ krb5_enctype *etypes = NULL;
+
+ etypes_str = krb5_config_get_strings(context, NULL, "libdefaults",
+ name, NULL);
+ if(etypes_str){
+ int i, j, k;
+ for(i = 0; etypes_str[i]; i++);
+ etypes = malloc((i+1) * sizeof(*etypes));
+ if (etypes == NULL) {
+ krb5_config_free_strings (etypes_str);
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ for(j = 0, k = 0; j < i; j++) {
+ krb5_enctype e;
+ if(krb5_string_to_enctype(context, etypes_str[j], &e) != 0)
+ continue;
+ if (krb5_enctype_valid(context, e) != 0)
+ continue;
+ etypes[k++] = e;
+ }
+ etypes[k] = ETYPE_NULL;
+ krb5_config_free_strings(etypes_str);
+ }
+ *ret_enctypes = etypes;
+ return 0;
+}
+
+/*
+ * read variables from the configuration file and set in `context'
+ */
+
+static krb5_error_code
+init_context_from_config_file(krb5_context context)
+{
+ krb5_error_code ret;
+ const char * tmp;
+ krb5_enctype *tmptypes;
+
+ INIT_FIELD(context, time, max_skew, 5 * 60, "clockskew");
+ INIT_FIELD(context, time, kdc_timeout, 3, "kdc_timeout");
+ INIT_FIELD(context, int, max_retries, 3, "max_retries");
+
+ INIT_FIELD(context, string, http_proxy, NULL, "http_proxy");
+
+ ret = set_etypes (context, "default_etypes", &tmptypes);
+ if(ret)
+ return ret;
+ free(context->etypes);
+ context->etypes = tmptypes;
+
+ ret = set_etypes (context, "default_etypes_des", &tmptypes);
+ if(ret)
+ return ret;
+ free(context->etypes_des);
+ context->etypes_des = tmptypes;
+
+ /* default keytab name */
+ tmp = NULL;
+ if(!issuid())
+ tmp = getenv("KRB5_KTNAME");
+ if(tmp != NULL)
+ context->default_keytab = tmp;
+ else
+ INIT_FIELD(context, string, default_keytab,
+ KEYTAB_DEFAULT, "default_keytab_name");
+
+ INIT_FIELD(context, string, default_keytab_modify,
+ NULL, "default_keytab_modify_name");
+
+ INIT_FIELD(context, string, time_fmt,
+ "%Y-%m-%dT%H:%M:%S", "time_format");
+
+ INIT_FIELD(context, string, date_fmt,
+ "%Y-%m-%d", "date_format");
+
+ INIT_FIELD(context, bool, log_utc,
+ FALSE, "log_utc");
+
+
+
+ /* init dns-proxy slime */
+ tmp = krb5_config_get_string(context, NULL, "libdefaults",
+ "dns_proxy", NULL);
+ if(tmp)
+ roken_gethostby_setup(context->http_proxy, tmp);
+ krb5_free_host_realm (context, context->default_realms);
+ context->default_realms = NULL;
+
+ {
+ krb5_addresses addresses;
+ char **adr, **a;
+
+ krb5_set_extra_addresses(context, NULL);
+ adr = krb5_config_get_strings(context, NULL,
+ "libdefaults",
+ "extra_addresses",
+ NULL);
+ memset(&addresses, 0, sizeof(addresses));
+ for(a = adr; a && *a; a++) {
+ ret = krb5_parse_address(context, *a, &addresses);
+ if (ret == 0) {
+ krb5_add_extra_addresses(context, &addresses);
+ krb5_free_addresses(context, &addresses);
+ }
+ }
+ krb5_config_free_strings(adr);
+
+ krb5_set_ignore_addresses(context, NULL);
+ adr = krb5_config_get_strings(context, NULL,
+ "libdefaults",
+ "ignore_addresses",
+ NULL);
+ memset(&addresses, 0, sizeof(addresses));
+ for(a = adr; a && *a; a++) {
+ ret = krb5_parse_address(context, *a, &addresses);
+ if (ret == 0) {
+ krb5_add_ignore_addresses(context, &addresses);
+ krb5_free_addresses(context, &addresses);
+ }
+ }
+ krb5_config_free_strings(adr);
+ }
+
+ INIT_FIELD(context, bool, scan_interfaces, TRUE, "scan_interfaces");
+ INIT_FIELD(context, int, fcache_vno, 0, "fcache_version");
+ /* prefer dns_lookup_kdc over srv_lookup. */
+ INIT_FIELD(context, bool, srv_lookup, TRUE, "srv_lookup");
+ INIT_FIELD(context, bool, srv_lookup, context->srv_lookup, "dns_lookup_kdc");
+ INIT_FIELD(context, int, large_msg_size, 1400, "large_message_size");
+ INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname");
+ INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac");
+ context->default_cc_name = NULL;
+ context->default_cc_name_set = 0;
+
+ ret = krb5_config_get_bool_default(context, NULL, FALSE,
+ "libdefaults",
+ "allow_weak_crypto", NULL);
+ if (ret) {
+ krb5_enctype_enable(context, ETYPE_DES_CBC_CRC);
+ krb5_enctype_enable(context, ETYPE_DES_CBC_MD4);
+ krb5_enctype_enable(context, ETYPE_DES_CBC_MD5);
+ krb5_enctype_enable(context, ETYPE_DES_CBC_NONE);
+ krb5_enctype_enable(context, ETYPE_DES_CFB64_NONE);
+ krb5_enctype_enable(context, ETYPE_DES_PCBC_NONE);
+ }
+
+ return 0;
+}
+
+/**
+ * Initializes the context structure and reads the configuration file
+ * /etc/krb5.conf. The structure should be freed by calling
+ * krb5_free_context() when it is no longer being used.
+ *
+ * @param context pointer to returned context
+ *
+ * @return Returns 0 to indicate success. Otherwise an errno code is
+ * returned. Failure means either that something bad happened during
+ * initialization (typically ENOMEM) or that Kerberos should not be
+ * used ENXIO.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_init_context(krb5_context *context)
+{
+ krb5_context p;
+ krb5_error_code ret;
+ char **files;
+
+ *context = NULL;
+
+ p = calloc(1, sizeof(*p));
+ if(!p)
+ return ENOMEM;
+
+ p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
+ if (p->mutex == NULL) {
+ free(p);
+ return ENOMEM;
+ }
+ HEIMDAL_MUTEX_init(p->mutex);
+
+ ret = krb5_get_default_config_files(&files);
+ if(ret)
+ goto out;
+ ret = krb5_set_config_files(p, files);
+ krb5_free_config_files(files);
+ if(ret)
+ goto out;
+
+ /* init error tables */
+ krb5_init_ets(p);
+
+ p->cc_ops = NULL;
+ p->num_cc_ops = 0;
+ krb5_cc_register(p, &krb5_acc_ops, TRUE);
+ krb5_cc_register(p, &krb5_fcc_ops, TRUE);
+ krb5_cc_register(p, &krb5_mcc_ops, TRUE);
+ krb5_cc_register(p, &krb5_scc_ops, TRUE);
+#ifdef HAVE_KCM
+ krb5_cc_register(p, &krb5_kcm_ops, TRUE);
+#endif
+
+ p->num_kt_types = 0;
+ p->kt_types = NULL;
+ krb5_kt_register (p, &krb5_fkt_ops);
+ krb5_kt_register (p, &krb5_wrfkt_ops);
+ krb5_kt_register (p, &krb5_javakt_ops);
+ krb5_kt_register (p, &krb5_mkt_ops);
+ krb5_kt_register (p, &krb5_akf_ops);
+ krb5_kt_register (p, &krb5_any_ops);
+
+out:
+ if(ret) {
+ krb5_free_context(p);
+ p = NULL;
+ }
+ *context = p;
+ return ret;
+}
+
+/**
+ * Frees the krb5_context allocated by krb5_init_context().
+ *
+ * @param context context to be freed.
+ *
+ * @ingroup krb5
+*/
+
+void KRB5_LIB_FUNCTION
+krb5_free_context(krb5_context context)
+{
+ if (context->default_cc_name)
+ free(context->default_cc_name);
+ if (context->default_cc_name_env)
+ free(context->default_cc_name_env);
+ free(context->etypes);
+ free(context->etypes_des);
+ krb5_free_host_realm (context, context->default_realms);
+ krb5_config_file_free (context, context->cf);
+ free_error_table (context->et_list);
+ free(context->cc_ops);
+ free(context->kt_types);
+ krb5_clear_error_string(context);
+ if(context->warn_dest != NULL)
+ krb5_closelog(context, context->warn_dest);
+ krb5_set_extra_addresses(context, NULL);
+ krb5_set_ignore_addresses(context, NULL);
+ krb5_set_send_to_kdc_func(context, NULL, NULL);
+ if (context->mutex != NULL) {
+ HEIMDAL_MUTEX_destroy(context->mutex);
+ free(context->mutex);
+ }
+ memset(context, 0, sizeof(*context));
+ free(context);
+}
+
+/**
+ * Reinit the context from a new set of filenames.
+ *
+ * @param context context to add configuration too.
+ * @param filenames array of filenames, end of list is indicated with a NULL filename.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_config_files(krb5_context context, char **filenames)
+{
+ krb5_error_code ret;
+ krb5_config_binding *tmp = NULL;
+ while(filenames != NULL && *filenames != NULL && **filenames != '\0') {
+ ret = krb5_config_parse_file_multi(context, *filenames, &tmp);
+ if(ret != 0 && ret != ENOENT && ret != EACCES) {
+ krb5_config_file_free(context, tmp);
+ return ret;
+ }
+ filenames++;
+ }
+#if 0
+ /* with this enabled and if there are no config files, Kerberos is
+ considererd disabled */
+ if(tmp == NULL)
+ return ENXIO;
+#endif
+ krb5_config_file_free(context, context->cf);
+ context->cf = tmp;
+ ret = init_context_from_config_file(context);
+ return ret;
+}
+
+static krb5_error_code
+add_file(char ***pfilenames, int *len, char *file)
+{
+ char **pp = *pfilenames;
+ int i;
+
+ for(i = 0; i < *len; i++) {
+ if(strcmp(pp[i], file) == 0) {
+ free(file);
+ return 0;
+ }
+ }
+
+ pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp));
+ if (pp == NULL) {
+ free(file);
+ return ENOMEM;
+ }
+
+ pp[*len] = file;
+ pp[*len + 1] = NULL;
+ *pfilenames = pp;
+ *len += 1;
+ return 0;
+}
+
+/*
+ * `pq' isn't free, it's up the the caller
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_prepend_config_files(const char *filelist, char **pq, char ***ret_pp)
+{
+ krb5_error_code ret;
+ const char *p, *q;
+ char **pp;
+ int len;
+ char *fn;
+
+ pp = NULL;
+
+ len = 0;
+ p = filelist;
+ while(1) {
+ ssize_t l;
+ q = p;
+ l = strsep_copy(&q, ":", NULL, 0);
+ if(l == -1)
+ break;
+ fn = malloc(l + 1);
+ if(fn == NULL) {
+ krb5_free_config_files(pp);
+ return ENOMEM;
+ }
+ l = strsep_copy(&p, ":", fn, l + 1);
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ krb5_free_config_files(pp);
+ return ret;
+ }
+ }
+
+ if (pq != NULL) {
+ int i;
+
+ for (i = 0; pq[i] != NULL; i++) {
+ fn = strdup(pq[i]);
+ if (fn == NULL) {
+ krb5_free_config_files(pp);
+ return ENOMEM;
+ }
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ krb5_free_config_files(pp);
+ return ret;
+ }
+ }
+ }
+
+ *ret_pp = pp;
+ return 0;
+}
+
+/**
+ * Prepend the filename to the global configuration list.
+ *
+ * @param filelist a filename to add to the default list of filename
+ * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_prepend_config_files_default(const char *filelist, char ***pfilenames)
+{
+ krb5_error_code ret;
+ char **defpp, **pp = NULL;
+
+ ret = krb5_get_default_config_files(&defpp);
+ if (ret)
+ return ret;
+
+ ret = krb5_prepend_config_files(filelist, defpp, &pp);
+ krb5_free_config_files(defpp);
+ if (ret) {
+ return ret;
+ }
+ *pfilenames = pp;
+ return 0;
+}
+
+/**
+ * Get the global configuration list.
+ *
+ * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_config_files(char ***pfilenames)
+{
+ const char *files = NULL;
+
+ if (pfilenames == NULL)
+ return EINVAL;
+ if(!issuid())
+ files = getenv("KRB5_CONFIG");
+ if (files == NULL)
+ files = krb5_config_file;
+
+ return krb5_prepend_config_files(files, NULL, pfilenames);
+}
+
+/**
+ * Free a list of configuration files.
+ *
+ * @param filenames list to be freed.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_config_files(char **filenames)
+{
+ char **p;
+ for(p = filenames; *p != NULL; p++)
+ free(*p);
+ free(filenames);
+}
+
+/**
+ * Returns the list of Kerberos encryption types sorted in order of
+ * most preferred to least preferred encryption type. Note that some
+ * encryption types might be disabled, so you need to check with
+ * krb5_enctype_valid() before using the encryption type.
+ *
+ * @return list of enctypes, terminated with ETYPE_NULL. Its a static
+ * array completed into the Kerberos library so the content doesn't
+ * need to be freed.
+ *
+ * @ingroup krb5
+ */
+
+const krb5_enctype * KRB5_LIB_FUNCTION
+krb5_kerberos_enctypes(krb5_context context)
+{
+ static const krb5_enctype p[] = {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_DES3_CBC_SHA1,
+ ETYPE_DES3_CBC_MD5,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_DES_CBC_MD5,
+ ETYPE_DES_CBC_MD4,
+ ETYPE_DES_CBC_CRC,
+ ETYPE_NULL
+ };
+ return p;
+}
+
+/*
+ * set `etype' to a malloced list of the default enctypes
+ */
+
+static krb5_error_code
+default_etypes(krb5_context context, krb5_enctype **etype)
+{
+ const krb5_enctype *p;
+ krb5_enctype *e = NULL, *ep;
+ int i, n = 0;
+
+ p = krb5_kerberos_enctypes(context);
+
+ for (i = 0; p[i] != ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+ ep = realloc(e, (n + 2) * sizeof(*e));
+ if (ep == NULL) {
+ free(e);
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ e = ep;
+ e[n] = p[i];
+ e[n + 1] = ETYPE_NULL;
+ n++;
+ }
+ *etype = e;
+ return 0;
+}
+
+/**
+ * Set the default encryption types that will be use in communcation
+ * with the KDC, clients and servers.
+ *
+ * @param context Kerberos 5 context.
+ * @param etypes Encryption types, array terminated with ETYPE_NULL (0).
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_default_in_tkt_etypes(krb5_context context,
+ const krb5_enctype *etypes)
+{
+ krb5_enctype *p = NULL;
+ int i;
+
+ if(etypes) {
+ for (i = 0; etypes[i]; ++i) {
+ krb5_error_code ret;
+ ret = krb5_enctype_valid(context, etypes[i]);
+ if (ret)
+ return ret;
+ }
+ ++i;
+ ALLOC(p, i);
+ if(!p) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memmove(p, etypes, i * sizeof(krb5_enctype));
+ }
+ if(context->etypes)
+ free(context->etypes);
+ context->etypes = p;
+ return 0;
+}
+
+/**
+ * Get the default encryption types that will be use in communcation
+ * with the KDC, clients and servers.
+ *
+ * @param context Kerberos 5 context.
+ * @param etypes Encryption types, array terminated with
+ * ETYPE_NULL(0), caller should free array with krb5_xfree():
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_in_tkt_etypes(krb5_context context,
+ krb5_enctype **etypes)
+{
+ krb5_enctype *p;
+ int i;
+ krb5_error_code ret;
+
+ if(context->etypes) {
+ for(i = 0; context->etypes[i]; i++);
+ ++i;
+ ALLOC(p, i);
+ if(!p) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memmove(p, context->etypes, i * sizeof(krb5_enctype));
+ } else {
+ ret = default_etypes(context, &p);
+ if (ret)
+ return ret;
+ }
+ *etypes = p;
+ return 0;
+}
+
+/**
+ * Return the error string for the error code. The caller must not
+ * free the string.
+ *
+ * @param context Kerberos 5 context.
+ * @param code Kerberos error code.
+ *
+ * @return the error message matching code
+ *
+ * @ingroup krb5
+ */
+
+const char* KRB5_LIB_FUNCTION
+krb5_get_err_text(krb5_context context, krb5_error_code code)
+{
+ const char *p = NULL;
+ if(context != NULL)
+ p = com_right(context->et_list, code);
+ if(p == NULL)
+ p = strerror(code);
+ if (p == NULL)
+ p = "Unknown error";
+ return p;
+}
+
+/**
+ * Init the built-in ets in the Kerberos library.
+ *
+ * @param context kerberos context to add the ets too
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_init_ets(krb5_context context)
+{
+ if(context->et_list == NULL){
+ krb5_add_et_list(context, initialize_krb5_error_table_r);
+ krb5_add_et_list(context, initialize_asn1_error_table_r);
+ krb5_add_et_list(context, initialize_heim_error_table_r);
+ krb5_add_et_list(context, initialize_k524_error_table_r);
+#ifdef PKINIT
+ krb5_add_et_list(context, initialize_hx_error_table_r);
+#endif
+ }
+}
+
+/**
+ * Make the kerberos library default to the admin KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param flag boolean flag to select if the use the admin KDC or not.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_use_admin_kdc (krb5_context context, krb5_boolean flag)
+{
+ context->use_admin_kdc = flag;
+}
+
+/**
+ * Make the kerberos library default to the admin KDC.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return boolean flag to telling the context will use admin KDC as the default KDC.
+ *
+ * @ingroup krb5
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_get_use_admin_kdc (krb5_context context)
+{
+ return context->use_admin_kdc;
+}
+
+/**
+ * Add extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to add
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_add_extra_addresses(krb5_context context, krb5_addresses *addresses)
+{
+
+ if(context->extra_addresses)
+ return krb5_append_addresses(context,
+ context->extra_addresses, addresses);
+ else
+ return krb5_set_extra_addresses(context, addresses);
+}
+
+/**
+ * Set extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to set
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_extra_addresses(krb5_context context, const krb5_addresses *addresses)
+{
+ if(context->extra_addresses)
+ krb5_free_addresses(context, context->extra_addresses);
+
+ if(addresses == NULL) {
+ if(context->extra_addresses != NULL) {
+ free(context->extra_addresses);
+ context->extra_addresses = NULL;
+ }
+ return 0;
+ }
+ if(context->extra_addresses == NULL) {
+ context->extra_addresses = malloc(sizeof(*context->extra_addresses));
+ if(context->extra_addresses == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ }
+ return krb5_copy_addresses(context, addresses, context->extra_addresses);
+}
+
+/**
+ * Get extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to set
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_extra_addresses(krb5_context context, krb5_addresses *addresses)
+{
+ if(context->extra_addresses == NULL) {
+ memset(addresses, 0, sizeof(*addresses));
+ return 0;
+ }
+ return krb5_copy_addresses(context,context->extra_addresses, addresses);
+}
+
+/**
+ * Add extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to ignore
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_add_ignore_addresses(krb5_context context, krb5_addresses *addresses)
+{
+
+ if(context->ignore_addresses)
+ return krb5_append_addresses(context,
+ context->ignore_addresses, addresses);
+ else
+ return krb5_set_ignore_addresses(context, addresses);
+}
+
+/**
+ * Set extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to ignore
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_ignore_addresses(krb5_context context, const krb5_addresses *addresses)
+{
+ if(context->ignore_addresses)
+ krb5_free_addresses(context, context->ignore_addresses);
+ if(addresses == NULL) {
+ if(context->ignore_addresses != NULL) {
+ free(context->ignore_addresses);
+ context->ignore_addresses = NULL;
+ }
+ return 0;
+ }
+ if(context->ignore_addresses == NULL) {
+ context->ignore_addresses = malloc(sizeof(*context->ignore_addresses));
+ if(context->ignore_addresses == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ }
+ return krb5_copy_addresses(context, addresses, context->ignore_addresses);
+}
+
+/**
+ * Get extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses list addreses ignored
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_ignore_addresses(krb5_context context, krb5_addresses *addresses)
+{
+ if(context->ignore_addresses == NULL) {
+ memset(addresses, 0, sizeof(*addresses));
+ return 0;
+ }
+ return krb5_copy_addresses(context, context->ignore_addresses, addresses);
+}
+
+/**
+ * Set version of fcache that the library should use.
+ *
+ * @param context Kerberos 5 context.
+ * @param version version number.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_fcache_version(krb5_context context, int version)
+{
+ context->fcache_vno = version;
+ return 0;
+}
+
+/**
+ * Get version of fcache that the library should use.
+ *
+ * @param context Kerberos 5 context.
+ * @param version version number.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_fcache_version(krb5_context context, int *version)
+{
+ *version = context->fcache_vno;
+ return 0;
+}
+
+/**
+ * Runtime check if the Kerberos library was complied with thread support.
+ *
+ * @return TRUE if the library was compiled with thread support, FALSE if not.
+ *
+ * @ingroup krb5
+ */
+
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_is_thread_safe(void)
+{
+#ifdef ENABLE_PTHREAD_SUPPORT
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/**
+ * Set if the library should use DNS to canonicalize hostnames.
+ *
+ * @param context Kerberos 5 context.
+ * @param flag if its dns canonicalizion is used or not.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_dns_canonicalize_hostname (krb5_context context, krb5_boolean flag)
+{
+ if (flag)
+ context->flags |= KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
+ else
+ context->flags &= ~KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
+}
+
+/**
+ * Get if the library uses DNS to canonicalize hostnames.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return return non zero if the library uses DNS to canonicalize hostnames.
+ *
+ * @ingroup krb5
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_get_dns_canonicalize_hostname (krb5_context context)
+{
+ return (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) ? 1 : 0;
+}
+
+/**
+ * Get current offset in time to the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param sec seconds part of offset.
+ * @param usec micro seconds part of offset.
+ *
+ * @return returns zero
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_kdc_sec_offset (krb5_context context, int32_t *sec, int32_t *usec)
+{
+ if (sec)
+ *sec = context->kdc_sec_offset;
+ if (usec)
+ *usec = context->kdc_usec_offset;
+ return 0;
+}
+
+/**
+ * Set current offset in time to the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param sec seconds part of offset.
+ * @param usec micro seconds part of offset.
+ *
+ * @return returns zero
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_kdc_sec_offset (krb5_context context, int32_t sec, int32_t usec)
+{
+ context->kdc_sec_offset = sec;
+ if (usec >= 0)
+ context->kdc_usec_offset = usec;
+ return 0;
+}
+
+/**
+ * Get max time skew allowed.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return timeskew in seconds.
+ *
+ * @ingroup krb5
+ */
+
+time_t KRB5_LIB_FUNCTION
+krb5_get_max_time_skew (krb5_context context)
+{
+ return context->max_skew;
+}
+
+/**
+ * Set max time skew allowed.
+ *
+ * @param context Kerberos 5 context.
+ * @param t timeskew in seconds.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_max_time_skew (krb5_context context, time_t t)
+{
+ context->max_skew = t;
+}
diff --git a/source4/heimdal/lib/krb5/convert_creds.c b/source4/heimdal/lib/krb5/convert_creds.c
new file mode 100644
index 0000000000..d74f121207
--- /dev/null
+++ b/source4/heimdal/lib/krb5/convert_creds.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+#include "krb5-v4compat.h"
+
+static krb5_error_code
+check_ticket_flags(TicketFlags f)
+{
+ return 0; /* maybe add some more tests here? */
+}
+
+/**
+ * Convert the v5 credentials in in_cred to v4-dito in v4creds. This
+ * is done by sending them to the 524 function in the KDC. If
+ * `in_cred' doesn't contain a DES session key, then a new one is
+ * gotten from the KDC and stored in the cred cache `ccache'.
+ *
+ * @param context Kerberos 5 context.
+ * @param in_cred the credential to convert
+ * @param v4creds the converted credential
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_v4compat
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb524_convert_creds_kdc(krb5_context context,
+ krb5_creds *in_cred,
+ struct credentials *v4creds)
+{
+ krb5_error_code ret;
+ krb5_data reply;
+ krb5_storage *sp;
+ int32_t tmp;
+ krb5_data ticket;
+ char realm[REALM_SZ];
+ krb5_creds *v5_creds = in_cred;
+
+ ret = check_ticket_flags(v5_creds->flags.b);
+ if(ret)
+ goto out2;
+
+ {
+ krb5_krbhst_handle handle;
+
+ ret = krb5_krbhst_init(context,
+ krb5_principal_get_realm(context,
+ v5_creds->server),
+ KRB5_KRBHST_KRB524,
+ &handle);
+ if (ret)
+ goto out2;
+
+ ret = krb5_sendto (context,
+ &v5_creds->ticket,
+ handle,
+ &reply);
+ krb5_krbhst_free(context, handle);
+ if (ret)
+ goto out2;
+ }
+ sp = krb5_storage_from_mem(reply.data, reply.length);
+ if(sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ goto out2;
+ }
+ krb5_ret_int32(sp, &tmp);
+ ret = tmp;
+ if(ret == 0) {
+ memset(v4creds, 0, sizeof(*v4creds));
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret)
+ goto out;
+ v4creds->kvno = tmp;
+ ret = krb5_ret_data(sp, &ticket);
+ if(ret)
+ goto out;
+ v4creds->ticket_st.length = ticket.length;
+ memcpy(v4creds->ticket_st.dat, ticket.data, ticket.length);
+ krb5_data_free(&ticket);
+ ret = krb5_524_conv_principal(context,
+ v5_creds->server,
+ v4creds->service,
+ v4creds->instance,
+ v4creds->realm);
+ if(ret)
+ goto out;
+ v4creds->issue_date = v5_creds->times.starttime;
+ v4creds->lifetime = _krb5_krb_time_to_life(v4creds->issue_date,
+ v5_creds->times.endtime);
+ ret = krb5_524_conv_principal(context, v5_creds->client,
+ v4creds->pname,
+ v4creds->pinst,
+ realm);
+ if(ret)
+ goto out;
+ memcpy(v4creds->session, v5_creds->session.keyvalue.data, 8);
+ } else {
+ krb5_set_error_message (context, ret, "converting credentials: %s",
+ krb5_get_err_text(context, ret));
+ }
+out:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+out2:
+ if (v5_creds != in_cred)
+ krb5_free_creds (context, v5_creds);
+ return ret;
+}
+
+/**
+ * Convert the v5 credentials in in_cred to v4-dito in v4creds,
+ * check the credential cache ccache before checking with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param ccache credential cache used to check for des-ticket.
+ * @param in_cred the credential to convert
+ * @param v4creds the converted credential
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_v4compat
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb524_convert_creds_kdc_ccache(krb5_context context,
+ krb5_ccache ccache,
+ krb5_creds *in_cred,
+ struct credentials *v4creds)
+{
+ krb5_error_code ret;
+ krb5_creds *v5_creds = in_cred;
+ krb5_keytype keytype;
+
+ keytype = v5_creds->session.keytype;
+
+ if (keytype != ENCTYPE_DES_CBC_CRC) {
+ /* MIT krb524d doesn't like nothing but des-cbc-crc tickets,
+ so go get one */
+ krb5_creds template;
+
+ memset (&template, 0, sizeof(template));
+ template.session.keytype = ENCTYPE_DES_CBC_CRC;
+ ret = krb5_copy_principal (context, in_cred->client, &template.client);
+ if (ret) {
+ krb5_free_cred_contents (context, &template);
+ return ret;
+ }
+ ret = krb5_copy_principal (context, in_cred->server, &template.server);
+ if (ret) {
+ krb5_free_cred_contents (context, &template);
+ return ret;
+ }
+
+ ret = krb5_get_credentials (context, 0, ccache,
+ &template, &v5_creds);
+ krb5_free_cred_contents (context, &template);
+ if (ret)
+ return ret;
+ }
+
+ ret = krb524_convert_creds_kdc(context, v5_creds, v4creds);
+
+ if (v5_creds != in_cred)
+ krb5_free_creds (context, v5_creds);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/copy_host_realm.c b/source4/heimdal/lib/krb5/copy_host_realm.c
new file mode 100644
index 0000000000..db06e56fb6
--- /dev/null
+++ b/source4/heimdal/lib/krb5/copy_host_realm.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Copy the list of realms from `from' to `to'.
+ *
+ * @param context Kerberos 5 context.
+ * @param from list of realms to copy from.
+ * @param to list of realms to copy to, free list of krb5_free_host_realm().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_host_realm(krb5_context context,
+ const krb5_realm *from,
+ krb5_realm **to)
+{
+ unsigned int n, i;
+ const krb5_realm *p;
+
+ for (n = 1, p = from; *p != NULL; ++p)
+ ++n;
+
+ *to = calloc (n, sizeof(**to));
+ if (*to == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ for (i = 0, p = from; *p != NULL; ++p, ++i) {
+ (*to)[i] = strdup(*p);
+ if ((*to)[i] == NULL) {
+ krb5_free_host_realm (context, *to);
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/crc.c b/source4/heimdal/lib/krb5/crc.c
new file mode 100644
index 0000000000..cdb40b8110
--- /dev/null
+++ b/source4/heimdal/lib/krb5/crc.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+static u_long table[256];
+
+#define CRC_GEN 0xEDB88320L
+
+void
+_krb5_crc_init_table(void)
+{
+ static int flag = 0;
+ unsigned long crc, poly;
+ unsigned int i, j;
+
+ if(flag) return;
+ poly = CRC_GEN;
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1) {
+ crc = (crc >> 1) ^ poly;
+ } else {
+ crc >>= 1;
+ }
+ }
+ table[i] = crc;
+ }
+ flag = 1;
+}
+
+uint32_t
+_krb5_crc_update (const char *p, size_t len, uint32_t res)
+{
+ while (len--)
+ res = table[(res ^ *p++) & 0xFF] ^ (res >> 8);
+ return res & 0xFFFFFFFF;
+}
diff --git a/source4/heimdal/lib/krb5/creds.c b/source4/heimdal/lib/krb5/creds.c
new file mode 100644
index 0000000000..d194041766
--- /dev/null
+++ b/source4/heimdal/lib/krb5/creds.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#undef __attribute__
+#define __attribute__(X)
+
+#ifndef HEIMDAL_SMALLER
+
+/* keep this for compatibility with older code */
+krb5_error_code KRB5_LIB_FUNCTION __attribute__((deprecated))
+krb5_free_creds_contents (krb5_context context, krb5_creds *c)
+{
+ return krb5_free_cred_contents (context, c);
+}
+
+#endif /* HEIMDAL_SMALLER */
+
+/**
+ * Free content of krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param c krb5_creds to free.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_cred_contents (krb5_context context, krb5_creds *c)
+{
+ krb5_free_principal (context, c->client);
+ c->client = NULL;
+ krb5_free_principal (context, c->server);
+ c->server = NULL;
+ krb5_free_keyblock_contents (context, &c->session);
+ krb5_data_free (&c->ticket);
+ krb5_data_free (&c->second_ticket);
+ free_AuthorizationData (&c->authdata);
+ krb5_free_addresses (context, &c->addresses);
+ memset(c, 0, sizeof(*c));
+ return 0;
+}
+
+/**
+ * Copy content of krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param incred source credential
+ * @param c destination credential, free with krb5_free_cred_contents().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_creds_contents (krb5_context context,
+ const krb5_creds *incred,
+ krb5_creds *c)
+{
+ krb5_error_code ret;
+
+ memset(c, 0, sizeof(*c));
+ ret = krb5_copy_principal (context, incred->client, &c->client);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_principal (context, incred->server, &c->server);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_keyblock_contents (context, &incred->session, &c->session);
+ if (ret)
+ goto fail;
+ c->times = incred->times;
+ ret = krb5_data_copy (&c->ticket,
+ incred->ticket.data,
+ incred->ticket.length);
+ if (ret)
+ goto fail;
+ ret = krb5_data_copy (&c->second_ticket,
+ incred->second_ticket.data,
+ incred->second_ticket.length);
+ if (ret)
+ goto fail;
+ ret = copy_AuthorizationData(&incred->authdata, &c->authdata);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_addresses (context,
+ &incred->addresses,
+ &c->addresses);
+ if (ret)
+ goto fail;
+ c->flags = incred->flags;
+ return 0;
+
+fail:
+ krb5_free_cred_contents (context, c);
+ return ret;
+}
+
+/**
+ * Copy krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param incred source credential
+ * @param outcred destination credential, free with krb5_free_creds().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_creds (krb5_context context,
+ const krb5_creds *incred,
+ krb5_creds **outcred)
+{
+ krb5_creds *c;
+
+ c = malloc (sizeof (*c));
+ if (c == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memset (c, 0, sizeof(*c));
+ *outcred = c;
+ return krb5_copy_creds_contents (context, incred, c);
+}
+
+/**
+ * Free krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param c krb5_creds to free.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_creds (krb5_context context, krb5_creds *c)
+{
+ krb5_free_cred_contents (context, c);
+ free (c);
+ return 0;
+}
+
+/* XXX this do not belong here */
+static krb5_boolean
+krb5_times_equal(const krb5_times *a, const krb5_times *b)
+{
+ return a->starttime == b->starttime &&
+ a->authtime == b->authtime &&
+ a->endtime == b->endtime &&
+ a->renew_till == b->renew_till;
+}
+
+/**
+ * Return TRUE if `mcreds' and `creds' are equal (`whichfields'
+ * determines what equal means).
+ *
+ * @param context Kerberos 5 context.
+ * @param whichfields which fields to compare.
+ * @param mcreds cred to compare with.
+ * @param creds cred to compare with.
+ *
+ * @return return TRUE if mcred and creds are equal, FALSE if not.
+ *
+ * @ingroup krb5
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_compare_creds(krb5_context context, krb5_flags whichfields,
+ const krb5_creds * mcreds, const krb5_creds * creds)
+{
+ krb5_boolean match = TRUE;
+
+ if (match && mcreds->server) {
+ if (whichfields & (KRB5_TC_DONT_MATCH_REALM | KRB5_TC_MATCH_SRV_NAMEONLY))
+ match = krb5_principal_compare_any_realm (context, mcreds->server,
+ creds->server);
+ else
+ match = krb5_principal_compare (context, mcreds->server,
+ creds->server);
+ }
+
+ if (match && mcreds->client) {
+ if(whichfields & KRB5_TC_DONT_MATCH_REALM)
+ match = krb5_principal_compare_any_realm (context, mcreds->client,
+ creds->client);
+ else
+ match = krb5_principal_compare (context, mcreds->client,
+ creds->client);
+ }
+
+ if (match && (whichfields & KRB5_TC_MATCH_KEYTYPE))
+ match = krb5_enctypes_compatible_keys(context,
+ mcreds->session.keytype,
+ creds->session.keytype);
+
+ if (match && (whichfields & KRB5_TC_MATCH_FLAGS_EXACT))
+ match = mcreds->flags.i == creds->flags.i;
+
+ if (match && (whichfields & KRB5_TC_MATCH_FLAGS))
+ match = (creds->flags.i & mcreds->flags.i) == mcreds->flags.i;
+
+ if (match && (whichfields & KRB5_TC_MATCH_TIMES_EXACT))
+ match = krb5_times_equal(&mcreds->times, &creds->times);
+
+ if (match && (whichfields & KRB5_TC_MATCH_TIMES))
+ /* compare only expiration times */
+ match = (mcreds->times.renew_till <= creds->times.renew_till) &&
+ (mcreds->times.endtime <= creds->times.endtime);
+
+ if (match && (whichfields & KRB5_TC_MATCH_AUTHDATA)) {
+ unsigned int i;
+ if(mcreds->authdata.len != creds->authdata.len)
+ match = FALSE;
+ else
+ for(i = 0; match && i < mcreds->authdata.len; i++)
+ match = (mcreds->authdata.val[i].ad_type ==
+ creds->authdata.val[i].ad_type) &&
+ (krb5_data_cmp(&mcreds->authdata.val[i].ad_data,
+ &creds->authdata.val[i].ad_data) == 0);
+ }
+ if (match && (whichfields & KRB5_TC_MATCH_2ND_TKT))
+ match = (krb5_data_cmp(&mcreds->second_ticket, &creds->second_ticket) == 0);
+
+ if (match && (whichfields & KRB5_TC_MATCH_IS_SKEY))
+ match = ((mcreds->second_ticket.length == 0) ==
+ (creds->second_ticket.length == 0));
+
+ return match;
+}
diff --git a/source4/heimdal/lib/krb5/crypto.c b/source4/heimdal/lib/krb5/crypto.c
new file mode 100644
index 0000000000..9379c6fdf1
--- /dev/null
+++ b/source4/heimdal/lib/krb5/crypto.c
@@ -0,0 +1,4603 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+#include <pkinit_asn1.h>
+
+#undef __attribute__
+#define __attribute__(X)
+
+#ifndef HEIMDAL_SMALLER
+#define WEAK_ENCTYPES 1
+#define DES3_OLD_ENCTYPE 1
+#endif
+
+
+#ifdef HAVE_OPENSSL /* XXX forward decl for hcrypto glue */
+const EVP_CIPHER * _krb5_EVP_hcrypto_aes_128_cts(void);
+const EVP_CIPHER * _krb5_EVP_hcrypto_aes_256_cts(void);
+#define EVP_hcrypto_aes_128_cts _krb5_EVP_hcrypto_aes_128_cts
+#define EVP_hcrypto_aes_256_cts _krb5_EVP_hcrypto_aes_256_cts
+#endif
+
+struct key_data {
+ krb5_keyblock *key;
+ krb5_data *schedule;
+};
+
+struct key_usage {
+ unsigned usage;
+ struct key_data key;
+};
+
+struct krb5_crypto_data {
+ struct encryption_type *et;
+ struct key_data key;
+ int num_key_usage;
+ struct key_usage *key_usage;
+};
+
+#define CRYPTO_ETYPE(C) ((C)->et->type)
+
+/* bits for `flags' below */
+#define F_KEYED 1 /* checksum is keyed */
+#define F_CPROOF 2 /* checksum is collision proof */
+#define F_DERIVED 4 /* uses derived keys */
+#define F_VARIANT 8 /* uses `variant' keys (6.4.3) */
+#define F_PSEUDO 16 /* not a real protocol type */
+#define F_SPECIAL 32 /* backwards */
+#define F_DISABLED 64 /* enctype/checksum disabled */
+
+struct salt_type {
+ krb5_salttype type;
+ const char *name;
+ krb5_error_code (*string_to_key)(krb5_context, krb5_enctype, krb5_data,
+ krb5_salt, krb5_data, krb5_keyblock*);
+};
+
+struct key_type {
+ krb5_keytype type; /* XXX */
+ const char *name;
+ size_t bits;
+ size_t size;
+ size_t schedule_size;
+ void (*random_key)(krb5_context, krb5_keyblock*);
+ void (*schedule)(krb5_context, struct key_type *, struct key_data *);
+ struct salt_type *string_to_key;
+ void (*random_to_key)(krb5_context, krb5_keyblock*, const void*, size_t);
+ void (*cleanup)(krb5_context, struct key_data *);
+ const EVP_CIPHER *(*evp)(void);
+};
+
+struct checksum_type {
+ krb5_cksumtype type;
+ const char *name;
+ size_t blocksize;
+ size_t checksumsize;
+ unsigned flags;
+ krb5_enctype (*checksum)(krb5_context context,
+ struct key_data *key,
+ const void *buf, size_t len,
+ unsigned usage,
+ Checksum *csum);
+ krb5_error_code (*verify)(krb5_context context,
+ struct key_data *key,
+ const void *buf, size_t len,
+ unsigned usage,
+ Checksum *csum);
+};
+
+struct encryption_type {
+ krb5_enctype type;
+ const char *name;
+ size_t blocksize;
+ size_t padsize;
+ size_t confoundersize;
+ struct key_type *keytype;
+ struct checksum_type *checksum;
+ struct checksum_type *keyed_checksum;
+ unsigned flags;
+ krb5_error_code (*encrypt)(krb5_context context,
+ struct key_data *key,
+ void *data, size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec);
+ size_t prf_length;
+ krb5_error_code (*prf)(krb5_context,
+ krb5_crypto, const krb5_data *, krb5_data *);
+};
+
+#define ENCRYPTION_USAGE(U) (((U) << 8) | 0xAA)
+#define INTEGRITY_USAGE(U) (((U) << 8) | 0x55)
+#define CHECKSUM_USAGE(U) (((U) << 8) | 0x99)
+
+static struct checksum_type *_find_checksum(krb5_cksumtype type);
+static struct encryption_type *_find_enctype(krb5_enctype type);
+static struct key_type *_find_keytype(krb5_keytype type);
+static krb5_error_code _get_derived_key(krb5_context, krb5_crypto,
+ unsigned, struct key_data**);
+static struct key_data *_new_derived_key(krb5_crypto crypto, unsigned usage);
+static krb5_error_code derive_key(krb5_context context,
+ struct encryption_type *et,
+ struct key_data *key,
+ const void *constant,
+ size_t len);
+static krb5_error_code hmac(krb5_context context,
+ struct checksum_type *cm,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ struct key_data *keyblock,
+ Checksum *result);
+static void free_key_data(krb5_context,
+ struct key_data *,
+ struct encryption_type *);
+static krb5_error_code usage2arcfour (krb5_context, unsigned *);
+static void xor (DES_cblock *, const unsigned char *);
+
+/************************************************************
+ * *
+ ************************************************************/
+
+struct evp_schedule {
+ EVP_CIPHER_CTX ectx;
+ EVP_CIPHER_CTX dctx;
+};
+
+
+static HEIMDAL_MUTEX crypto_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+static void
+krb5_DES_random_key(krb5_context context,
+ krb5_keyblock *key)
+{
+ DES_cblock *k = key->keyvalue.data;
+ do {
+ krb5_generate_random_block(k, sizeof(DES_cblock));
+ DES_set_odd_parity(k);
+ } while(DES_is_weak_key(k));
+}
+
+#ifdef WEAK_ENCTYPES
+static void
+krb5_DES_schedule_old(krb5_context context,
+ struct key_type *kt,
+ struct key_data *key)
+{
+ DES_set_key_unchecked(key->key->keyvalue.data, key->schedule->data);
+}
+#endif /* WEAK_ENCTYPES */
+
+
+#ifdef ENABLE_AFS_STRING_TO_KEY
+
+/* This defines the Andrew string_to_key function. It accepts a password
+ * string as input and converts it via a one-way encryption algorithm to a DES
+ * encryption key. It is compatible with the original Andrew authentication
+ * service password database.
+ */
+
+/*
+ * Short passwords, i.e 8 characters or less.
+ */
+static void
+krb5_DES_AFS3_CMU_string_to_key (krb5_data pw,
+ krb5_data cell,
+ DES_cblock *key)
+{
+ char password[8+1]; /* crypt is limited to 8 chars anyway */
+ int i;
+
+ for(i = 0; i < 8; i++) {
+ char c = ((i < pw.length) ? ((char*)pw.data)[i] : 0) ^
+ ((i < cell.length) ?
+ tolower(((unsigned char*)cell.data)[i]) : 0);
+ password[i] = c ? c : 'X';
+ }
+ password[8] = '\0';
+
+ memcpy(key, crypt(password, "p1") + 2, sizeof(DES_cblock));
+
+ /* parity is inserted into the LSB so left shift each byte up one
+ bit. This allows ascii characters with a zero MSB to retain as
+ much significance as possible. */
+ for (i = 0; i < sizeof(DES_cblock); i++)
+ ((unsigned char*)key)[i] <<= 1;
+ DES_set_odd_parity (key);
+}
+
+/*
+ * Long passwords, i.e 9 characters or more.
+ */
+static void
+krb5_DES_AFS3_Transarc_string_to_key (krb5_data pw,
+ krb5_data cell,
+ DES_cblock *key)
+{
+ DES_key_schedule schedule;
+ DES_cblock temp_key;
+ DES_cblock ivec;
+ char password[512];
+ size_t passlen;
+
+ memcpy(password, pw.data, min(pw.length, sizeof(password)));
+ if(pw.length < sizeof(password)) {
+ int len = min(cell.length, sizeof(password) - pw.length);
+ int i;
+
+ memcpy(password + pw.length, cell.data, len);
+ for (i = pw.length; i < pw.length + len; ++i)
+ password[i] = tolower((unsigned char)password[i]);
+ }
+ passlen = min(sizeof(password), pw.length + cell.length);
+ memcpy(&ivec, "kerberos", 8);
+ memcpy(&temp_key, "kerberos", 8);
+ DES_set_odd_parity (&temp_key);
+ DES_set_key_unchecked (&temp_key, &schedule);
+ DES_cbc_cksum ((void*)password, &ivec, passlen, &schedule, &ivec);
+
+ memcpy(&temp_key, &ivec, 8);
+ DES_set_odd_parity (&temp_key);
+ DES_set_key_unchecked (&temp_key, &schedule);
+ DES_cbc_cksum ((void*)password, key, passlen, &schedule, &ivec);
+ memset(&schedule, 0, sizeof(schedule));
+ memset(&temp_key, 0, sizeof(temp_key));
+ memset(&ivec, 0, sizeof(ivec));
+ memset(password, 0, sizeof(password));
+
+ DES_set_odd_parity (key);
+}
+
+static krb5_error_code
+DES_AFS3_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ DES_cblock tmp;
+ if(password.length > 8)
+ krb5_DES_AFS3_Transarc_string_to_key(password, salt.saltvalue, &tmp);
+ else
+ krb5_DES_AFS3_CMU_string_to_key(password, salt.saltvalue, &tmp);
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
+ memset(&key, 0, sizeof(key));
+ return 0;
+}
+#endif /* ENABLE_AFS_STRING_TO_KEY */
+
+static void
+DES_string_to_key_int(unsigned char *data, size_t length, DES_cblock *key)
+{
+ DES_key_schedule schedule;
+ int i;
+ int reverse = 0;
+ unsigned char *p;
+
+ unsigned char swap[] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf };
+ memset(key, 0, 8);
+
+ p = (unsigned char*)key;
+ for (i = 0; i < length; i++) {
+ unsigned char tmp = data[i];
+ if (!reverse)
+ *p++ ^= (tmp << 1);
+ else
+ *--p ^= (swap[tmp & 0xf] << 4) | swap[(tmp & 0xf0) >> 4];
+ if((i % 8) == 7)
+ reverse = !reverse;
+ }
+ DES_set_odd_parity(key);
+ if(DES_is_weak_key(key))
+ (*key)[7] ^= 0xF0;
+ DES_set_key_unchecked(key, &schedule);
+ DES_cbc_cksum((void*)data, key, length, &schedule, key);
+ memset(&schedule, 0, sizeof(schedule));
+ DES_set_odd_parity(key);
+ if(DES_is_weak_key(key))
+ (*key)[7] ^= 0xF0;
+}
+
+static krb5_error_code
+krb5_DES_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ unsigned char *s;
+ size_t len;
+ DES_cblock tmp;
+
+#ifdef ENABLE_AFS_STRING_TO_KEY
+ if (opaque.length == 1) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 1);
+ if (v == 1)
+ return DES_AFS3_string_to_key(context, enctype, password,
+ salt, opaque, key);
+ }
+#endif
+
+ len = password.length + salt.saltvalue.length;
+ s = malloc(len);
+ if(len > 0 && s == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(s, password.data, password.length);
+ memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ DES_string_to_key_int(s, len, &tmp);
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
+ memset(&tmp, 0, sizeof(tmp));
+ memset(s, 0, len);
+ free(s);
+ return 0;
+}
+
+static void
+krb5_DES_random_to_key(krb5_context context,
+ krb5_keyblock *key,
+ const void *data,
+ size_t size)
+{
+ DES_cblock *k = key->keyvalue.data;
+ memcpy(k, data, key->keyvalue.length);
+ DES_set_odd_parity(k);
+ if(DES_is_weak_key(k))
+ xor(k, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+}
+
+/*
+ *
+ */
+
+static void
+DES3_random_key(krb5_context context,
+ krb5_keyblock *key)
+{
+ DES_cblock *k = key->keyvalue.data;
+ do {
+ krb5_generate_random_block(k, 3 * sizeof(DES_cblock));
+ DES_set_odd_parity(&k[0]);
+ DES_set_odd_parity(&k[1]);
+ DES_set_odd_parity(&k[2]);
+ } while(DES_is_weak_key(&k[0]) ||
+ DES_is_weak_key(&k[1]) ||
+ DES_is_weak_key(&k[2]));
+}
+
+/*
+ * A = A xor B. A & B are 8 bytes.
+ */
+
+static void
+xor (DES_cblock *key, const unsigned char *b)
+{
+ unsigned char *a = (unsigned char*)key;
+ a[0] ^= b[0];
+ a[1] ^= b[1];
+ a[2] ^= b[2];
+ a[3] ^= b[3];
+ a[4] ^= b[4];
+ a[5] ^= b[5];
+ a[6] ^= b[6];
+ a[7] ^= b[7];
+}
+
+#ifdef DES3_OLD_ENCTYPE
+static krb5_error_code
+DES3_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ char *str;
+ size_t len;
+ unsigned char tmp[24];
+ DES_cblock keys[3];
+ krb5_error_code ret;
+
+ len = password.length + salt.saltvalue.length;
+ str = malloc(len);
+ if(len != 0 && str == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(str, password.data, password.length);
+ memcpy(str + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ {
+ DES_cblock ivec;
+ DES_key_schedule s[3];
+ int i;
+
+ ret = _krb5_n_fold(str, len, tmp, 24);
+ if (ret) {
+ memset(str, 0, len);
+ free(str);
+ krb5_set_error_message (context, ret, "malloc: out of memory");
+ return ret;
+ }
+
+ for(i = 0; i < 3; i++){
+ memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
+ DES_set_odd_parity(keys + i);
+ if(DES_is_weak_key(keys + i))
+ xor(keys + i, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ DES_set_key_unchecked(keys + i, &s[i]);
+ }
+ memset(&ivec, 0, sizeof(ivec));
+ DES_ede3_cbc_encrypt(tmp,
+ tmp, sizeof(tmp),
+ &s[0], &s[1], &s[2], &ivec, DES_ENCRYPT);
+ memset(s, 0, sizeof(s));
+ memset(&ivec, 0, sizeof(ivec));
+ for(i = 0; i < 3; i++){
+ memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
+ DES_set_odd_parity(keys + i);
+ if(DES_is_weak_key(keys + i))
+ xor(keys + i, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ }
+ memset(tmp, 0, sizeof(tmp));
+ }
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, keys, sizeof(keys));
+ memset(keys, 0, sizeof(keys));
+ memset(str, 0, len);
+ free(str);
+ return 0;
+}
+#endif
+
+static krb5_error_code
+DES3_string_to_key_derived(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ size_t len = password.length + salt.saltvalue.length;
+ char *s;
+
+ s = malloc(len);
+ if(len != 0 && s == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(s, password.data, password.length);
+ memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ ret = krb5_string_to_key_derived(context,
+ s,
+ len,
+ enctype,
+ key);
+ memset(s, 0, len);
+ free(s);
+ return ret;
+}
+
+static void
+DES3_random_to_key(krb5_context context,
+ krb5_keyblock *key,
+ const void *data,
+ size_t size)
+{
+ unsigned char *x = key->keyvalue.data;
+ const u_char *q = data;
+ DES_cblock *k;
+ int i, j;
+
+ memset(x, 0, sizeof(x));
+ for (i = 0; i < 3; ++i) {
+ unsigned char foo;
+ for (j = 0; j < 7; ++j) {
+ unsigned char b = q[7 * i + j];
+
+ x[8 * i + j] = b;
+ }
+ foo = 0;
+ for (j = 6; j >= 0; --j) {
+ foo |= q[7 * i + j] & 1;
+ foo <<= 1;
+ }
+ x[8 * i + 7] = foo;
+ }
+ k = key->keyvalue.data;
+ for (i = 0; i < 3; i++) {
+ DES_set_odd_parity(&k[i]);
+ if(DES_is_weak_key(&k[i]))
+ xor(&k[i], (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ }
+}
+
+/*
+ * ARCFOUR
+ */
+
+static void
+ARCFOUR_schedule(krb5_context context,
+ struct key_type *kt,
+ struct key_data *kd)
+{
+ RC4_set_key (kd->schedule->data,
+ kd->key->keyvalue.length, kd->key->keyvalue.data);
+}
+
+static krb5_error_code
+ARCFOUR_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint16_t *s = NULL;
+ size_t len, i;
+ EVP_MD_CTX *m;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "Malloc: out of memory");
+ goto out;
+ }
+
+ EVP_DigestInit_ex(m, EVP_md4(), NULL);
+
+ ret = wind_utf8ucs2_length(password.data, &len);
+ if (ret) {
+ krb5_set_error_message (context, ret, "Password not an UCS2 string");
+ goto out;
+ }
+
+ s = malloc (len * sizeof(s[0]));
+ if (len != 0 && s == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = wind_utf8ucs2(password.data, s, &len);
+ if (ret) {
+ krb5_set_error_message (context, ret, "Password not an UCS2 string");
+ goto out;
+ }
+
+ /* LE encoding */
+ for (i = 0; i < len; i++) {
+ unsigned char p;
+ p = (s[i] & 0xff);
+ EVP_DigestUpdate (m, &p, 1);
+ p = (s[i] >> 8) & 0xff;
+ EVP_DigestUpdate (m, &p, 1);
+ }
+
+ key->keytype = enctype;
+ ret = krb5_data_alloc (&key->keyvalue, 16);
+ if (ret) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ goto out;
+ }
+ EVP_DigestFinal_ex (m, key->keyvalue.data, NULL);
+
+out:
+ EVP_MD_CTX_destroy(m);
+ if (s)
+ memset (s, 0, len);
+ free (s);
+ return ret;
+}
+
+/*
+ * AES
+ */
+
+int _krb5_AES_string_to_default_iterator = 4096;
+
+static krb5_error_code
+AES_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint32_t iter;
+ struct encryption_type *et;
+ struct key_data kd;
+
+ if (opaque.length == 0)
+ iter = _krb5_AES_string_to_default_iterator;
+ else if (opaque.length == 4) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 4);
+ iter = ((uint32_t)v);
+ } else
+ return KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */
+
+ et = _find_enctype(enctype);
+ if (et == NULL)
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+
+ kd.schedule = NULL;
+ ALLOC(kd.key, 1);
+ if(kd.key == NULL) {
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ kd.key->keytype = enctype;
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if (ret) {
+ krb5_set_error_message (context, ret, "malloc: out of memory");
+ return ret;
+ }
+
+ ret = PKCS5_PBKDF2_HMAC_SHA1(password.data, password.length,
+ salt.saltvalue.data, salt.saltvalue.length,
+ iter,
+ et->keytype->size, kd.key->keyvalue.data);
+ if (ret != 1) {
+ free_key_data(context, &kd, et);
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "Error calculating s2k");
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+ }
+
+ ret = derive_key(context, et, &kd, "kerberos", strlen("kerberos"));
+ if (ret == 0)
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ free_key_data(context, &kd, et);
+
+ return ret;
+}
+
+static void
+evp_schedule(krb5_context context, struct key_type *kt, struct key_data *kd)
+{
+ struct evp_schedule *key = kd->schedule->data;
+ const EVP_CIPHER *c = (*kt->evp)();
+
+ EVP_CIPHER_CTX_init(&key->ectx);
+ EVP_CIPHER_CTX_init(&key->dctx);
+
+ EVP_CipherInit_ex(&key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1);
+ EVP_CipherInit_ex(&key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0);
+}
+
+static void
+evp_cleanup(krb5_context context, struct key_data *kd)
+{
+ struct evp_schedule *key = kd->schedule->data;
+ EVP_CIPHER_CTX_cleanup(&key->ectx);
+ EVP_CIPHER_CTX_cleanup(&key->dctx);
+}
+
+/*
+ *
+ */
+
+static struct salt_type des_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ krb5_DES_string_to_key
+ },
+#ifdef ENABLE_AFS_STRING_TO_KEY
+ {
+ KRB5_AFS3_SALT,
+ "afs3-salt",
+ DES_AFS3_string_to_key
+ },
+#endif
+ { 0 }
+};
+
+#ifdef DES3_OLD_ENCTYPE
+static struct salt_type des3_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ DES3_string_to_key
+ },
+ { 0 }
+};
+#endif
+
+static struct salt_type des3_salt_derived[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ DES3_string_to_key_derived
+ },
+ { 0 }
+};
+
+static struct salt_type AES_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ AES_string_to_key
+ },
+ { 0 }
+};
+
+static struct salt_type arcfour_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ ARCFOUR_string_to_key
+ },
+ { 0 }
+};
+
+/*
+ *
+ */
+
+static struct key_type keytype_null = {
+ KEYTYPE_NULL,
+ "null",
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+#ifdef WEAK_ENCTYPES
+static struct key_type keytype_des_old = {
+ KEYTYPE_DES,
+ "des-old",
+ 56,
+ 8,
+ sizeof(DES_key_schedule),
+ krb5_DES_random_key,
+ krb5_DES_schedule_old,
+ des_salt,
+ krb5_DES_random_to_key
+};
+#endif /* WEAK_ENCTYPES */
+
+static struct key_type keytype_des = {
+ KEYTYPE_DES,
+ "des",
+ 56,
+ 8,
+ sizeof(struct evp_schedule),
+ krb5_DES_random_key,
+ evp_schedule,
+ des_salt,
+ krb5_DES_random_to_key,
+ evp_cleanup,
+ EVP_des_cbc
+};
+
+#ifdef DES3_OLD_ENCTYPE
+static struct key_type keytype_des3 = {
+ KEYTYPE_DES3,
+ "des3",
+ 168,
+ 24,
+ sizeof(struct evp_schedule),
+ DES3_random_key,
+ evp_schedule,
+ des3_salt,
+ DES3_random_to_key,
+ evp_cleanup,
+ EVP_des_ede3_cbc
+};
+#endif
+
+static struct key_type keytype_des3_derived = {
+ KEYTYPE_DES3,
+ "des3",
+ 168,
+ 24,
+ sizeof(struct evp_schedule),
+ DES3_random_key,
+ evp_schedule,
+ des3_salt_derived,
+ DES3_random_to_key,
+ evp_cleanup,
+ EVP_des_ede3_cbc
+};
+
+static struct key_type keytype_aes128 = {
+ KEYTYPE_AES128,
+ "aes-128",
+ 128,
+ 16,
+ sizeof(struct evp_schedule),
+ NULL,
+ evp_schedule,
+ AES_salt,
+ NULL,
+ evp_cleanup,
+ EVP_hcrypto_aes_128_cts
+};
+
+static struct key_type keytype_aes256 = {
+ KEYTYPE_AES256,
+ "aes-256",
+ 256,
+ 32,
+ sizeof(struct evp_schedule),
+ NULL,
+ evp_schedule,
+ AES_salt,
+ NULL,
+ evp_cleanup,
+ EVP_hcrypto_aes_256_cts
+};
+
+static struct key_type keytype_arcfour = {
+ KEYTYPE_ARCFOUR,
+ "arcfour",
+ 128,
+ 16,
+ sizeof(RC4_KEY),
+ NULL,
+ ARCFOUR_schedule,
+ arcfour_salt
+};
+
+static struct key_type *keytypes[] = {
+ &keytype_null,
+ &keytype_des,
+ &keytype_des3_derived,
+#ifdef DES3_OLD_ENCTYPE
+ &keytype_des3,
+#endif
+ &keytype_aes128,
+ &keytype_aes256,
+ &keytype_arcfour
+};
+
+static int num_keytypes = sizeof(keytypes) / sizeof(keytypes[0]);
+
+static struct key_type *
+_find_keytype(krb5_keytype type)
+{
+ int i;
+ for(i = 0; i < num_keytypes; i++)
+ if(keytypes[i]->type == type)
+ return keytypes[i];
+ return NULL;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_salttype_to_string (krb5_context context,
+ krb5_enctype etype,
+ krb5_salttype stype,
+ char **string)
+{
+ struct encryption_type *e;
+ struct salt_type *st;
+
+ e = _find_enctype (etype);
+ if (e == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for (st = e->keytype->string_to_key; st && st->type; st++) {
+ if (st->type == stype) {
+ *string = strdup (st->name);
+ if (*string == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+ }
+ }
+ krb5_set_error_message (context, HEIM_ERR_SALTTYPE_NOSUPP,
+ "salttype %d not supported", stype);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_salttype (krb5_context context,
+ krb5_enctype etype,
+ const char *string,
+ krb5_salttype *salttype)
+{
+ struct encryption_type *e;
+ struct salt_type *st;
+
+ e = _find_enctype (etype);
+ if (e == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for (st = e->keytype->string_to_key; st && st->type; st++) {
+ if (strcasecmp (st->name, string) == 0) {
+ *salttype = st->type;
+ return 0;
+ }
+ }
+ krb5_set_error_message(context, HEIM_ERR_SALTTYPE_NOSUPP,
+ "salttype %s not supported", string);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_pw_salt(krb5_context context,
+ krb5_const_principal principal,
+ krb5_salt *salt)
+{
+ size_t len;
+ int i;
+ krb5_error_code ret;
+ char *p;
+
+ salt->salttype = KRB5_PW_SALT;
+ len = strlen(principal->realm);
+ for (i = 0; i < principal->name.name_string.len; ++i)
+ len += strlen(principal->name.name_string.val[i]);
+ ret = krb5_data_alloc (&salt->saltvalue, len);
+ if (ret)
+ return ret;
+ p = salt->saltvalue.data;
+ memcpy (p, principal->realm, strlen(principal->realm));
+ p += strlen(principal->realm);
+ for (i = 0; i < principal->name.name_string.len; ++i) {
+ memcpy (p,
+ principal->name.name_string.val[i],
+ strlen(principal->name.name_string.val[i]));
+ p += strlen(principal->name.name_string.val[i]);
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_salt(krb5_context context,
+ krb5_salt salt)
+{
+ krb5_data_free(&salt.saltvalue);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_data (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_principal principal,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_salt salt;
+
+ ret = krb5_get_pw_salt(context, principal, &salt);
+ if(ret)
+ return ret;
+ ret = krb5_string_to_key_data_salt(context, enctype, password, salt, key);
+ krb5_free_salt(context, salt);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_principal principal,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data(context, enctype, pw, principal, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_data_salt (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_keyblock *key)
+{
+ krb5_data opaque;
+ krb5_data_zero(&opaque);
+ return krb5_string_to_key_data_salt_opaque(context, enctype, password,
+ salt, opaque, key);
+}
+
+/*
+ * Do a string -> key for encryption type `enctype' operation on
+ * `password' (with salt `salt' and the enctype specific data string
+ * `opaque'), returning the resulting key in `key'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_data_salt_opaque (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et =_find_enctype(enctype);
+ struct salt_type *st;
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for(st = et->keytype->string_to_key; st && st->type; st++)
+ if(st->type == salt.salttype)
+ return (*st->string_to_key)(context, enctype, password,
+ salt, opaque, key);
+ krb5_set_error_message(context, HEIM_ERR_SALTTYPE_NOSUPP,
+ "salt type %d not supported",
+ salt.salttype);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+/*
+ * Do a string -> key for encryption type `enctype' operation on the
+ * string `password' (with salt `salt'), returning the resulting key
+ * in `key'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_salt (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_salt salt,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data_salt(context, enctype, pw, salt, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_salt_opaque (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data_salt_opaque(context, enctype,
+ pw, salt, opaque, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_keysize(krb5_context context,
+ krb5_enctype type,
+ size_t *keysize)
+{
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *keysize = et->keytype->size;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_keybits(krb5_context context,
+ krb5_enctype type,
+ size_t *keybits)
+{
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *keybits = et->keytype->bits;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_random_keyblock(krb5_context context,
+ krb5_enctype type,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ ret = krb5_data_alloc(&key->keyvalue, et->keytype->size);
+ if(ret)
+ return ret;
+ key->keytype = type;
+ if(et->keytype->random_key)
+ (*et->keytype->random_key)(context, key);
+ else
+ krb5_generate_random_block(key->keyvalue.data,
+ key->keyvalue.length);
+ return 0;
+}
+
+static krb5_error_code
+_key_schedule(krb5_context context,
+ struct key_data *key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et = _find_enctype(key->key->keytype);
+ struct key_type *kt = et->keytype;
+
+ if(kt->schedule == NULL)
+ return 0;
+ if (key->schedule != NULL)
+ return 0;
+ ALLOC(key->schedule, 1);
+ if(key->schedule == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = krb5_data_alloc(key->schedule, kt->schedule_size);
+ if(ret) {
+ free(key->schedule);
+ key->schedule = NULL;
+ return ret;
+ }
+ (*kt->schedule)(context, kt, key);
+ return 0;
+}
+
+/************************************************************
+ * *
+ ************************************************************/
+
+static krb5_error_code
+NONE_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return 0;
+}
+
+static krb5_error_code
+CRC32_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ uint32_t crc;
+ unsigned char *r = C->checksum.data;
+ _krb5_crc_init_table ();
+ crc = _krb5_crc_update (data, len, 0);
+ r[0] = crc & 0xff;
+ r[1] = (crc >> 8) & 0xff;
+ r[2] = (crc >> 16) & 0xff;
+ r[3] = (crc >> 24) & 0xff;
+ return 0;
+}
+
+static krb5_error_code
+RSA_MD4_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_md4(), NULL) != 1)
+ krb5_abortx(context, "md4 checksum failed");
+ return 0;
+}
+
+static krb5_error_code
+des_checksum(krb5_context context,
+ const EVP_MD *evp_md,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ Checksum *cksum)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_MD_CTX *m;
+ DES_cblock ivec;
+ unsigned char *p = cksum->checksum.data;
+
+ krb5_generate_random_block(p, 8);
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM, "Malloc: out of memory");
+ return ENOMEM;
+ }
+
+ EVP_DigestInit_ex(m, evp_md, NULL);
+ EVP_DigestUpdate(m, p, 8);
+ EVP_DigestUpdate(m, data, len);
+ EVP_DigestFinal_ex (m, p + 8, NULL);
+ EVP_MD_CTX_destroy(m);
+ memset (&ivec, 0, sizeof(ivec));
+ EVP_CipherInit_ex(&ctx->ectx, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(&ctx->ectx, p, p, 24);
+
+ return 0;
+}
+
+static krb5_error_code
+des_verify(krb5_context context,
+ const EVP_MD *evp_md,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ Checksum *C)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_MD_CTX *m;
+ unsigned char tmp[24];
+ unsigned char res[16];
+ DES_cblock ivec;
+ krb5_error_code ret = 0;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM, "Malloc: out of memory");
+ return ENOMEM;
+ }
+
+ memset(&ivec, 0, sizeof(ivec));
+ EVP_CipherInit_ex(&ctx->dctx, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(&ctx->dctx, tmp, C->checksum.data, 24);
+
+ EVP_DigestInit_ex(m, evp_md, NULL);
+ EVP_DigestUpdate(m, tmp, 8); /* confounder */
+ EVP_DigestUpdate(m, data, len);
+ EVP_DigestFinal_ex (m, res, NULL);
+ EVP_MD_CTX_destroy(m);
+ if(memcmp(res, tmp + 8, sizeof(res)) != 0) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ }
+ memset(tmp, 0, sizeof(tmp));
+ memset(res, 0, sizeof(res));
+ return ret;
+}
+
+static krb5_error_code
+RSA_MD4_DES_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *cksum)
+{
+ return des_checksum(context, EVP_md4(), key, data, len, cksum);
+}
+
+static krb5_error_code
+RSA_MD4_DES_verify(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_verify(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+RSA_MD5_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_md5(), NULL) != 1)
+ krb5_abortx(context, "md5 checksum failed");
+ return 0;
+}
+
+static krb5_error_code
+RSA_MD5_DES_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_checksum(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+RSA_MD5_DES_verify(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_verify(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+RSA_MD5_DES3_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_checksum(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+RSA_MD5_DES3_verify(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_verify(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+SHA1_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_sha1(), NULL) != 1)
+ krb5_abortx(context, "sha1 checksum failed");
+ return 0;
+}
+
+/* HMAC according to RFC2104 */
+static krb5_error_code
+hmac(krb5_context context,
+ struct checksum_type *cm,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ struct key_data *keyblock,
+ Checksum *result)
+{
+ unsigned char *ipad, *opad;
+ unsigned char *key;
+ size_t key_len;
+ int i;
+
+ ipad = malloc(cm->blocksize + len);
+ if (ipad == NULL)
+ return ENOMEM;
+ opad = malloc(cm->blocksize + cm->checksumsize);
+ if (opad == NULL) {
+ free(ipad);
+ return ENOMEM;
+ }
+ memset(ipad, 0x36, cm->blocksize);
+ memset(opad, 0x5c, cm->blocksize);
+
+ if(keyblock->key->keyvalue.length > cm->blocksize){
+ (*cm->checksum)(context,
+ keyblock,
+ keyblock->key->keyvalue.data,
+ keyblock->key->keyvalue.length,
+ usage,
+ result);
+ key = result->checksum.data;
+ key_len = result->checksum.length;
+ } else {
+ key = keyblock->key->keyvalue.data;
+ key_len = keyblock->key->keyvalue.length;
+ }
+ for(i = 0; i < key_len; i++){
+ ipad[i] ^= key[i];
+ opad[i] ^= key[i];
+ }
+ memcpy(ipad + cm->blocksize, data, len);
+ (*cm->checksum)(context, keyblock, ipad, cm->blocksize + len,
+ usage, result);
+ memcpy(opad + cm->blocksize, result->checksum.data,
+ result->checksum.length);
+ (*cm->checksum)(context, keyblock, opad,
+ cm->blocksize + cm->checksumsize, usage, result);
+ memset(ipad, 0, cm->blocksize + len);
+ free(ipad);
+ memset(opad, 0, cm->blocksize + cm->checksumsize);
+ free(opad);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_hmac(krb5_context context,
+ krb5_cksumtype cktype,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ krb5_keyblock *key,
+ Checksum *result)
+{
+ struct checksum_type *c = _find_checksum(cktype);
+ struct key_data kd;
+ krb5_error_code ret;
+
+ if (c == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ cktype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ kd.key = key;
+ kd.schedule = NULL;
+
+ ret = hmac(context, c, data, len, usage, &kd, result);
+
+ if (kd.schedule)
+ krb5_free_data(context, kd.schedule);
+
+ return ret;
+ }
+
+static krb5_error_code
+SP_HMAC_SHA1_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *result)
+{
+ struct checksum_type *c = _find_checksum(CKSUMTYPE_SHA1);
+ Checksum res;
+ char sha1_data[20];
+ krb5_error_code ret;
+
+ res.checksum.data = sha1_data;
+ res.checksum.length = sizeof(sha1_data);
+
+ ret = hmac(context, c, data, len, usage, key, &res);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+ memcpy(result->checksum.data, res.checksum.data, result->checksum.length);
+ return 0;
+}
+
+/*
+ * checksum according to section 5. of draft-brezak-win2k-krb-rc4-hmac-03.txt
+ */
+
+static krb5_error_code
+HMAC_MD5_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *result)
+{
+ EVP_MD_CTX *m;
+ struct checksum_type *c = _find_checksum (CKSUMTYPE_RSA_MD5);
+ const char signature[] = "signaturekey";
+ Checksum ksign_c;
+ struct key_data ksign;
+ krb5_keyblock kb;
+ unsigned char t[4];
+ unsigned char tmp[16];
+ unsigned char ksign_c_data[16];
+ krb5_error_code ret;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM, "Malloc: out of memory");
+ return ENOMEM;
+ }
+ ksign_c.checksum.length = sizeof(ksign_c_data);
+ ksign_c.checksum.data = ksign_c_data;
+ ret = hmac(context, c, signature, sizeof(signature), 0, key, &ksign_c);
+ if (ret) {
+ EVP_MD_CTX_destroy(m);
+ return ret;
+ }
+ ksign.key = &kb;
+ kb.keyvalue = ksign_c.checksum;
+ EVP_DigestInit_ex(m, EVP_md5(), NULL);
+ t[0] = (usage >> 0) & 0xFF;
+ t[1] = (usage >> 8) & 0xFF;
+ t[2] = (usage >> 16) & 0xFF;
+ t[3] = (usage >> 24) & 0xFF;
+ EVP_DigestUpdate(m, t, 4);
+ EVP_DigestUpdate(m, data, len);
+ EVP_DigestFinal_ex (m, tmp, NULL);
+ EVP_MD_CTX_destroy(m);
+
+ ret = hmac(context, c, tmp, sizeof(tmp), 0, &ksign, result);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static struct checksum_type checksum_none = {
+ CKSUMTYPE_NONE,
+ "none",
+ 1,
+ 0,
+ 0,
+ NONE_checksum,
+ NULL
+};
+static struct checksum_type checksum_crc32 = {
+ CKSUMTYPE_CRC32,
+ "crc32",
+ 1,
+ 4,
+ 0,
+ CRC32_checksum,
+ NULL
+};
+static struct checksum_type checksum_rsa_md4 = {
+ CKSUMTYPE_RSA_MD4,
+ "rsa-md4",
+ 64,
+ 16,
+ F_CPROOF,
+ RSA_MD4_checksum,
+ NULL
+};
+static struct checksum_type checksum_rsa_md4_des = {
+ CKSUMTYPE_RSA_MD4_DES,
+ "rsa-md4-des",
+ 64,
+ 24,
+ F_KEYED | F_CPROOF | F_VARIANT,
+ RSA_MD4_DES_checksum,
+ RSA_MD4_DES_verify
+};
+static struct checksum_type checksum_rsa_md5 = {
+ CKSUMTYPE_RSA_MD5,
+ "rsa-md5",
+ 64,
+ 16,
+ F_CPROOF,
+ RSA_MD5_checksum,
+ NULL
+};
+static struct checksum_type checksum_rsa_md5_des = {
+ CKSUMTYPE_RSA_MD5_DES,
+ "rsa-md5-des",
+ 64,
+ 24,
+ F_KEYED | F_CPROOF | F_VARIANT,
+ RSA_MD5_DES_checksum,
+ RSA_MD5_DES_verify
+};
+#ifdef DES3_OLD_ENCTYPE
+static struct checksum_type checksum_rsa_md5_des3 = {
+ CKSUMTYPE_RSA_MD5_DES3,
+ "rsa-md5-des3",
+ 64,
+ 24,
+ F_KEYED | F_CPROOF | F_VARIANT,
+ RSA_MD5_DES3_checksum,
+ RSA_MD5_DES3_verify
+};
+#endif
+static struct checksum_type checksum_sha1 = {
+ CKSUMTYPE_SHA1,
+ "sha1",
+ 64,
+ 20,
+ F_CPROOF,
+ SHA1_checksum,
+ NULL
+};
+static struct checksum_type checksum_hmac_sha1_des3 = {
+ CKSUMTYPE_HMAC_SHA1_DES3,
+ "hmac-sha1-des3",
+ 64,
+ 20,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA1_checksum,
+ NULL
+};
+
+static struct checksum_type checksum_hmac_sha1_aes128 = {
+ CKSUMTYPE_HMAC_SHA1_96_AES_128,
+ "hmac-sha1-96-aes128",
+ 64,
+ 12,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA1_checksum,
+ NULL
+};
+
+static struct checksum_type checksum_hmac_sha1_aes256 = {
+ CKSUMTYPE_HMAC_SHA1_96_AES_256,
+ "hmac-sha1-96-aes256",
+ 64,
+ 12,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA1_checksum,
+ NULL
+};
+
+static struct checksum_type checksum_hmac_md5 = {
+ CKSUMTYPE_HMAC_MD5,
+ "hmac-md5",
+ 64,
+ 16,
+ F_KEYED | F_CPROOF,
+ HMAC_MD5_checksum,
+ NULL
+};
+
+static struct checksum_type *checksum_types[] = {
+ &checksum_none,
+ &checksum_crc32,
+ &checksum_rsa_md4,
+ &checksum_rsa_md4_des,
+ &checksum_rsa_md5,
+ &checksum_rsa_md5_des,
+#ifdef DES3_OLD_ENCTYPE
+ &checksum_rsa_md5_des3,
+#endif
+ &checksum_sha1,
+ &checksum_hmac_sha1_des3,
+ &checksum_hmac_sha1_aes128,
+ &checksum_hmac_sha1_aes256,
+ &checksum_hmac_md5
+};
+
+static int num_checksums = sizeof(checksum_types) / sizeof(checksum_types[0]);
+
+static struct checksum_type *
+_find_checksum(krb5_cksumtype type)
+{
+ int i;
+ for(i = 0; i < num_checksums; i++)
+ if(checksum_types[i]->type == type)
+ return checksum_types[i];
+ return NULL;
+}
+
+static krb5_error_code
+get_checksum_key(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage, /* not krb5_key_usage */
+ struct checksum_type *ct,
+ struct key_data **key)
+{
+ krb5_error_code ret = 0;
+
+ if(ct->flags & F_DERIVED)
+ ret = _get_derived_key(context, crypto, usage, key);
+ else if(ct->flags & F_VARIANT) {
+ int i;
+
+ *key = _new_derived_key(crypto, 0xff/* KRB5_KU_RFC1510_VARIANT */);
+ if(*key == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = krb5_copy_keyblock(context, crypto->key.key, &(*key)->key);
+ if(ret)
+ return ret;
+ for(i = 0; i < (*key)->key->keyvalue.length; i++)
+ ((unsigned char*)(*key)->key->keyvalue.data)[i] ^= 0xF0;
+ } else {
+ *key = &crypto->key;
+ }
+ if(ret == 0)
+ ret = _key_schedule(context, *key);
+ return ret;
+}
+
+static krb5_error_code
+create_checksum (krb5_context context,
+ struct checksum_type *ct,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ Checksum *result)
+{
+ krb5_error_code ret;
+ struct key_data *dkey;
+ int keyed_checksum;
+
+ if (ct->flags & F_DISABLED) {
+ krb5_clear_error_string (context);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ keyed_checksum = (ct->flags & F_KEYED) != 0;
+ if(keyed_checksum && crypto == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "Checksum type %s is keyed "
+ "but no crypto context (key) was passed in",
+ ct->name);
+ return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
+ }
+ if(keyed_checksum) {
+ ret = get_checksum_key(context, crypto, usage, ct, &dkey);
+ if (ret)
+ return ret;
+ } else
+ dkey = NULL;
+ result->cksumtype = ct->type;
+ ret = krb5_data_alloc(&result->checksum, ct->checksumsize);
+ if (ret)
+ return (ret);
+ return (*ct->checksum)(context, dkey, data, len, usage, result);
+}
+
+static int
+arcfour_checksum_p(struct checksum_type *ct, krb5_crypto crypto)
+{
+ return (ct->type == CKSUMTYPE_HMAC_MD5) &&
+ (crypto->key.key->keytype == KEYTYPE_ARCFOUR);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_create_checksum(krb5_context context,
+ krb5_crypto crypto,
+ krb5_key_usage usage,
+ int type,
+ void *data,
+ size_t len,
+ Checksum *result)
+{
+ struct checksum_type *ct = NULL;
+ unsigned keyusage;
+
+ /* type 0 -> pick from crypto */
+ if (type) {
+ ct = _find_checksum(type);
+ } else if (crypto) {
+ ct = crypto->et->keyed_checksum;
+ if (ct == NULL)
+ ct = crypto->et->checksum;
+ }
+
+ if(ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ if (arcfour_checksum_p(ct, crypto)) {
+ keyusage = usage;
+ usage2arcfour(context, &keyusage);
+ } else
+ keyusage = CHECKSUM_USAGE(usage);
+
+ return create_checksum(context, ct, crypto, keyusage,
+ data, len, result);
+}
+
+static krb5_error_code
+verify_checksum(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage, /* not krb5_key_usage */
+ void *data,
+ size_t len,
+ Checksum *cksum)
+{
+ krb5_error_code ret;
+ struct key_data *dkey;
+ int keyed_checksum;
+ Checksum c;
+ struct checksum_type *ct;
+
+ ct = _find_checksum(cksum->cksumtype);
+ if (ct == NULL || (ct->flags & F_DISABLED)) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ cksum->cksumtype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ if(ct->checksumsize != cksum->checksum.length) {
+ krb5_clear_error_string (context);
+ return KRB5KRB_AP_ERR_BAD_INTEGRITY; /* XXX */
+ }
+ keyed_checksum = (ct->flags & F_KEYED) != 0;
+ if(keyed_checksum && crypto == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "Checksum type %s is keyed "
+ "but no crypto context (key) was passed in",
+ ct->name);
+ return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
+ }
+ if(keyed_checksum) {
+ ret = get_checksum_key(context, crypto, usage, ct, &dkey);
+ if (ret)
+ return ret;
+ } else
+ dkey = NULL;
+ if(ct->verify)
+ return (*ct->verify)(context, dkey, data, len, usage, cksum);
+
+ ret = krb5_data_alloc (&c.checksum, ct->checksumsize);
+ if (ret)
+ return ret;
+
+ ret = (*ct->checksum)(context, dkey, data, len, usage, &c);
+ if (ret) {
+ krb5_data_free(&c.checksum);
+ return ret;
+ }
+
+ if(c.checksum.length != cksum->checksum.length ||
+ memcmp(c.checksum.data, cksum->checksum.data, c.checksum.length)) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ } else {
+ ret = 0;
+ }
+ krb5_data_free (&c.checksum);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_checksum(krb5_context context,
+ krb5_crypto crypto,
+ krb5_key_usage usage,
+ void *data,
+ size_t len,
+ Checksum *cksum)
+{
+ struct checksum_type *ct;
+ unsigned keyusage;
+
+ ct = _find_checksum(cksum->cksumtype);
+ if(ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ cksum->cksumtype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ if (arcfour_checksum_p(ct, crypto)) {
+ keyusage = usage;
+ usage2arcfour(context, &keyusage);
+ } else
+ keyusage = CHECKSUM_USAGE(usage);
+
+ return verify_checksum(context, crypto, keyusage,
+ data, len, cksum);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_get_checksum_type(krb5_context context,
+ krb5_crypto crypto,
+ krb5_cksumtype *type)
+{
+ struct checksum_type *ct = NULL;
+
+ if (crypto != NULL) {
+ ct = crypto->et->keyed_checksum;
+ if (ct == NULL)
+ ct = crypto->et->checksum;
+ }
+
+ if (ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type not found");
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ *type = ct->type;
+
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_checksumsize(krb5_context context,
+ krb5_cksumtype type,
+ size_t *size)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ *size = ct->checksumsize;
+ return 0;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_checksum_is_keyed(krb5_context context,
+ krb5_cksumtype type)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ return ct->flags & F_KEYED;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_checksum_is_collision_proof(krb5_context context,
+ krb5_cksumtype type)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ return ct->flags & F_CPROOF;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_checksum_disable(krb5_context context,
+ krb5_cksumtype type)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ ct->flags |= F_DISABLED;
+ return 0;
+}
+
+/************************************************************
+ * *
+ ************************************************************/
+
+static krb5_error_code
+NULL_encrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec)
+{
+ return 0;
+}
+
+static krb5_error_code
+evp_encrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_CIPHER_CTX *c;
+ c = encryptp ? &ctx->ectx : &ctx->dctx;
+ if (ivec == NULL) {
+ /* alloca ? */
+ size_t len = EVP_CIPHER_CTX_iv_length(c);
+ void *loiv = malloc(len);
+ if (loiv == NULL) {
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+ memset(loiv, 0, len);
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1);
+ free(loiv);
+ } else
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1);
+ EVP_Cipher(c, data, data, len);
+ return 0;
+}
+
+#ifdef WEAK_ENCTYPES
+static krb5_error_code
+evp_des_encrypt_null_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_CIPHER_CTX *c;
+ DES_cblock ivec;
+ memset(&ivec, 0, sizeof(ivec));
+ c = encryptp ? &ctx->ectx : &ctx->dctx;
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(c, data, data, len);
+ return 0;
+}
+
+static krb5_error_code
+evp_des_encrypt_key_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_CIPHER_CTX *c;
+ DES_cblock ivec;
+ memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec));
+ c = encryptp ? &ctx->ectx : &ctx->dctx;
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(c, data, data, len);
+ return 0;
+}
+
+static krb5_error_code
+DES_CFB64_encrypt_null_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ DES_cblock ivec;
+ int num = 0;
+ DES_key_schedule *s = key->schedule->data;
+ memset(&ivec, 0, sizeof(ivec));
+
+ DES_cfb64_encrypt(data, data, len, s, &ivec, &num, encryptp);
+ return 0;
+}
+
+static krb5_error_code
+DES_PCBC_encrypt_key_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ DES_cblock ivec;
+ DES_key_schedule *s = key->schedule->data;
+ memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec));
+
+ DES_pcbc_encrypt(data, data, len, s, &ivec, encryptp);
+ return 0;
+}
+#endif
+
+/*
+ * section 6 of draft-brezak-win2k-krb-rc4-hmac-03
+ *
+ * warning: not for small children
+ */
+
+static krb5_error_code
+ARCFOUR_subencrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ unsigned usage,
+ void *ivec)
+{
+ struct checksum_type *c = _find_checksum (CKSUMTYPE_RSA_MD5);
+ Checksum k1_c, k2_c, k3_c, cksum;
+ struct key_data ke;
+ krb5_keyblock kb;
+ unsigned char t[4];
+ RC4_KEY rc4_key;
+ unsigned char *cdata = data;
+ unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
+ krb5_error_code ret;
+
+ t[0] = (usage >> 0) & 0xFF;
+ t[1] = (usage >> 8) & 0xFF;
+ t[2] = (usage >> 16) & 0xFF;
+ t[3] = (usage >> 24) & 0xFF;
+
+ k1_c.checksum.length = sizeof(k1_c_data);
+ k1_c.checksum.data = k1_c_data;
+
+ ret = hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
+
+ k2_c.checksum.length = sizeof(k2_c_data);
+ k2_c.checksum.data = k2_c_data;
+
+ ke.key = &kb;
+ kb.keyvalue = k2_c.checksum;
+
+ cksum.checksum.length = 16;
+ cksum.checksum.data = data;
+
+ ret = hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ ke.key = &kb;
+ kb.keyvalue = k1_c.checksum;
+
+ k3_c.checksum.length = sizeof(k3_c_data);
+ k3_c.checksum.data = k3_c_data;
+
+ ret = hmac(NULL, c, data, 16, 0, &ke, &k3_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ RC4_set_key (&rc4_key, k3_c.checksum.length, k3_c.checksum.data);
+ RC4 (&rc4_key, len - 16, cdata + 16, cdata + 16);
+ memset (k1_c_data, 0, sizeof(k1_c_data));
+ memset (k2_c_data, 0, sizeof(k2_c_data));
+ memset (k3_c_data, 0, sizeof(k3_c_data));
+ return 0;
+}
+
+static krb5_error_code
+ARCFOUR_subdecrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ unsigned usage,
+ void *ivec)
+{
+ struct checksum_type *c = _find_checksum (CKSUMTYPE_RSA_MD5);
+ Checksum k1_c, k2_c, k3_c, cksum;
+ struct key_data ke;
+ krb5_keyblock kb;
+ unsigned char t[4];
+ RC4_KEY rc4_key;
+ unsigned char *cdata = data;
+ unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
+ unsigned char cksum_data[16];
+ krb5_error_code ret;
+
+ t[0] = (usage >> 0) & 0xFF;
+ t[1] = (usage >> 8) & 0xFF;
+ t[2] = (usage >> 16) & 0xFF;
+ t[3] = (usage >> 24) & 0xFF;
+
+ k1_c.checksum.length = sizeof(k1_c_data);
+ k1_c.checksum.data = k1_c_data;
+
+ ret = hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
+
+ k2_c.checksum.length = sizeof(k2_c_data);
+ k2_c.checksum.data = k2_c_data;
+
+ ke.key = &kb;
+ kb.keyvalue = k1_c.checksum;
+
+ k3_c.checksum.length = sizeof(k3_c_data);
+ k3_c.checksum.data = k3_c_data;
+
+ ret = hmac(NULL, c, cdata, 16, 0, &ke, &k3_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ RC4_set_key (&rc4_key, k3_c.checksum.length, k3_c.checksum.data);
+ RC4 (&rc4_key, len - 16, cdata + 16, cdata + 16);
+
+ ke.key = &kb;
+ kb.keyvalue = k2_c.checksum;
+
+ cksum.checksum.length = 16;
+ cksum.checksum.data = cksum_data;
+
+ ret = hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ memset (k1_c_data, 0, sizeof(k1_c_data));
+ memset (k2_c_data, 0, sizeof(k2_c_data));
+ memset (k3_c_data, 0, sizeof(k3_c_data));
+
+ if (memcmp (cksum.checksum.data, data, 16) != 0) {
+ krb5_clear_error_string (context);
+ return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * convert the usage numbers used in
+ * draft-ietf-cat-kerb-key-derivation-00.txt to the ones in
+ * draft-brezak-win2k-krb-rc4-hmac-04.txt
+ */
+
+static krb5_error_code
+usage2arcfour (krb5_context context, unsigned *usage)
+{
+ switch (*usage) {
+ case KRB5_KU_AS_REP_ENC_PART : /* 3 */
+ case KRB5_KU_TGS_REP_ENC_PART_SUB_KEY : /* 9 */
+ *usage = 8;
+ return 0;
+ case KRB5_KU_USAGE_SEAL : /* 22 */
+ *usage = 13;
+ return 0;
+ case KRB5_KU_USAGE_SIGN : /* 23 */
+ *usage = 15;
+ return 0;
+ case KRB5_KU_USAGE_SEQ: /* 24 */
+ *usage = 0;
+ return 0;
+ default :
+ return 0;
+ }
+}
+
+static krb5_error_code
+ARCFOUR_encrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec)
+{
+ krb5_error_code ret;
+ unsigned keyusage = usage;
+
+ if((ret = usage2arcfour (context, &keyusage)) != 0)
+ return ret;
+
+ if (encryptp)
+ return ARCFOUR_subencrypt (context, key, data, len, keyusage, ivec);
+ else
+ return ARCFOUR_subdecrypt (context, key, data, len, keyusage, ivec);
+}
+
+
+/*
+ *
+ */
+
+static krb5_error_code
+AES_PRF(krb5_context context,
+ krb5_crypto crypto,
+ const krb5_data *in,
+ krb5_data *out)
+{
+ struct checksum_type *ct = crypto->et->checksum;
+ krb5_error_code ret;
+ Checksum result;
+ krb5_keyblock *derived;
+
+ result.cksumtype = ct->type;
+ ret = krb5_data_alloc(&result.checksum, ct->checksumsize);
+ if (ret) {
+ krb5_set_error_message(context, ret, "out memory");
+ return ret;
+ }
+
+ ret = (*ct->checksum)(context, NULL, in->data, in->length, 0, &result);
+ if (ret) {
+ krb5_data_free(&result.checksum);
+ return ret;
+ }
+
+ if (result.checksum.length < crypto->et->blocksize)
+ krb5_abortx(context, "internal prf error");
+
+ derived = NULL;
+ ret = krb5_derive_key(context, crypto->key.key,
+ crypto->et->type, "prf", 3, &derived);
+ if (ret)
+ krb5_abortx(context, "krb5_derive_key");
+
+ ret = krb5_data_alloc(out, crypto->et->blocksize);
+ if (ret)
+ krb5_abortx(context, "malloc failed");
+
+ {
+ const EVP_CIPHER *c = (*crypto->et->keytype->evp)();
+ EVP_CIPHER_CTX ctx;
+ /* XXX blksz 1 for cts, so we can't use that */
+ EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */
+ EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1);
+ EVP_Cipher(&ctx, out->data, result.checksum.data, 16);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+
+ krb5_data_free(&result.checksum);
+ krb5_free_keyblock(context, derived);
+
+ return ret;
+}
+
+/*
+ * these should currently be in reverse preference order.
+ * (only relevant for !F_PSEUDO) */
+
+static struct encryption_type enctype_null = {
+ ETYPE_NULL,
+ "null",
+ 1,
+ 1,
+ 0,
+ &keytype_null,
+ &checksum_none,
+ NULL,
+ F_DISABLED,
+ NULL_encrypt,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_arcfour_hmac_md5 = {
+ ETYPE_ARCFOUR_HMAC_MD5,
+ "arcfour-hmac-md5",
+ 1,
+ 1,
+ 8,
+ &keytype_arcfour,
+ &checksum_hmac_md5,
+ NULL,
+ F_SPECIAL,
+ ARCFOUR_encrypt,
+ 0,
+ NULL
+};
+#ifdef DES3_OLD_ENCTYPE
+static struct encryption_type enctype_des3_cbc_md5 = {
+ ETYPE_DES3_CBC_MD5,
+ "des3-cbc-md5",
+ 8,
+ 8,
+ 8,
+ &keytype_des3,
+ &checksum_rsa_md5,
+ &checksum_rsa_md5_des3,
+ 0,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#endif
+static struct encryption_type enctype_des3_cbc_sha1 = {
+ ETYPE_DES3_CBC_SHA1,
+ "des3-cbc-sha1",
+ 8,
+ 8,
+ 8,
+ &keytype_des3_derived,
+ &checksum_sha1,
+ &checksum_hmac_sha1_des3,
+ F_DERIVED,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#ifdef DES3_OLD_ENCTYPE
+static struct encryption_type enctype_old_des3_cbc_sha1 = {
+ ETYPE_OLD_DES3_CBC_SHA1,
+ "old-des3-cbc-sha1",
+ 8,
+ 8,
+ 8,
+ &keytype_des3,
+ &checksum_sha1,
+ &checksum_hmac_sha1_des3,
+ 0,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#endif
+static struct encryption_type enctype_aes128_cts_hmac_sha1 = {
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ "aes128-cts-hmac-sha1-96",
+ 16,
+ 1,
+ 16,
+ &keytype_aes128,
+ &checksum_sha1,
+ &checksum_hmac_sha1_aes128,
+ F_DERIVED,
+ evp_encrypt,
+ 16,
+ AES_PRF
+};
+static struct encryption_type enctype_aes256_cts_hmac_sha1 = {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ "aes256-cts-hmac-sha1-96",
+ 16,
+ 1,
+ 16,
+ &keytype_aes256,
+ &checksum_sha1,
+ &checksum_hmac_sha1_aes256,
+ F_DERIVED,
+ evp_encrypt,
+ 16,
+ AES_PRF
+};
+static struct encryption_type enctype_des3_cbc_none = {
+ ETYPE_DES3_CBC_NONE,
+ "des3-cbc-none",
+ 8,
+ 8,
+ 0,
+ &keytype_des3_derived,
+ &checksum_none,
+ NULL,
+ F_PSEUDO,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#ifdef WEAK_ENCTYPES
+static struct encryption_type enctype_des_cbc_crc = {
+ ETYPE_DES_CBC_CRC,
+ "des-cbc-crc",
+ 8,
+ 8,
+ 8,
+ &keytype_des,
+ &checksum_crc32,
+ NULL,
+ F_DISABLED,
+ evp_des_encrypt_key_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cbc_md4 = {
+ ETYPE_DES_CBC_MD4,
+ "des-cbc-md4",
+ 8,
+ 8,
+ 8,
+ &keytype_des,
+ &checksum_rsa_md4,
+ &checksum_rsa_md4_des,
+ F_DISABLED,
+ evp_des_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cbc_md5 = {
+ ETYPE_DES_CBC_MD5,
+ "des-cbc-md5",
+ 8,
+ 8,
+ 8,
+ &keytype_des,
+ &checksum_rsa_md5,
+ &checksum_rsa_md5_des,
+ F_DISABLED,
+ evp_des_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cbc_none = {
+ ETYPE_DES_CBC_NONE,
+ "des-cbc-none",
+ 8,
+ 8,
+ 0,
+ &keytype_des,
+ &checksum_none,
+ NULL,
+ F_PSEUDO|F_DISABLED,
+ evp_des_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cfb64_none = {
+ ETYPE_DES_CFB64_NONE,
+ "des-cfb64-none",
+ 1,
+ 1,
+ 0,
+ &keytype_des_old,
+ &checksum_none,
+ NULL,
+ F_PSEUDO|F_DISABLED,
+ DES_CFB64_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_pcbc_none = {
+ ETYPE_DES_PCBC_NONE,
+ "des-pcbc-none",
+ 8,
+ 8,
+ 0,
+ &keytype_des_old,
+ &checksum_none,
+ NULL,
+ F_PSEUDO|F_DISABLED,
+ DES_PCBC_encrypt_key_ivec,
+ 0,
+ NULL
+};
+#endif /* WEAK_ENCTYPES */
+
+static struct encryption_type *etypes[] = {
+ &enctype_aes256_cts_hmac_sha1,
+ &enctype_aes128_cts_hmac_sha1,
+ &enctype_des3_cbc_sha1,
+ &enctype_des3_cbc_none, /* used by the gss-api mech */
+ &enctype_arcfour_hmac_md5,
+#ifdef DES3_OLD_ENCTYPE
+ &enctype_des3_cbc_md5,
+ &enctype_old_des3_cbc_sha1,
+#endif
+#ifdef WEAK_ENCTYPES
+ &enctype_des_cbc_crc,
+ &enctype_des_cbc_md4,
+ &enctype_des_cbc_md5,
+ &enctype_des_cbc_none,
+ &enctype_des_cfb64_none,
+ &enctype_des_pcbc_none,
+#endif
+ &enctype_null
+};
+
+static unsigned num_etypes = sizeof(etypes) / sizeof(etypes[0]);
+
+
+static struct encryption_type *
+_find_enctype(krb5_enctype type)
+{
+ int i;
+ for(i = 0; i < num_etypes; i++)
+ if(etypes[i]->type == type)
+ return etypes[i];
+ return NULL;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_to_string(krb5_context context,
+ krb5_enctype etype,
+ char **string)
+{
+ struct encryption_type *e;
+ e = _find_enctype(etype);
+ if(e == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ *string = NULL;
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *string = strdup(e->name);
+ if(*string == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_enctype(krb5_context context,
+ const char *string,
+ krb5_enctype *etype)
+{
+ int i;
+ for(i = 0; i < num_etypes; i++)
+ if(strcasecmp(etypes[i]->name, string) == 0){
+ *etype = etypes[i]->type;
+ return 0;
+ }
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %s not supported",
+ string);
+ return KRB5_PROG_ETYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_to_keytype(krb5_context context,
+ krb5_enctype etype,
+ krb5_keytype *keytype)
+{
+ struct encryption_type *e = _find_enctype(etype);
+ if(e == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *keytype = e->keytype->type; /* XXX */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_valid(krb5_context context,
+ krb5_enctype etype)
+{
+ struct encryption_type *e = _find_enctype(etype);
+ if(e == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if (e->flags & F_DISABLED) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %s is disabled",
+ e->name);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ return 0;
+}
+
+/**
+ * Return the coresponding encryption type for a checksum type.
+ *
+ * @param context Kerberos context
+ * @param ctype The checksum type to get the result enctype for
+ * @param etype The returned encryption, when the matching etype is
+ * not found, etype is set to ETYPE_NULL.
+ *
+ * @return Return an error code for an failure or 0 on success.
+ * @ingroup krb5_crypto
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cksumtype_to_enctype(krb5_context context,
+ krb5_cksumtype ctype,
+ krb5_enctype *etype)
+{
+ int i;
+
+ *etype = ETYPE_NULL;
+
+ for(i = 0; i < num_etypes; i++) {
+ if(etypes[i]->keyed_checksum &&
+ etypes[i]->keyed_checksum->type == ctype)
+ {
+ *etype = etypes[i]->type;
+ return 0;
+ }
+ }
+
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "ckecksum type %d not supported",
+ (int)ctype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cksumtype_valid(krb5_context context,
+ krb5_cksumtype ctype)
+{
+ struct checksum_type *c = _find_checksum(ctype);
+ if (c == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %d not supported",
+ ctype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ if (c->flags & F_DISABLED) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ "checksum type %s is disabled",
+ c->name);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ return 0;
+}
+
+
+/* if two enctypes have compatible keys */
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_enctypes_compatible_keys(krb5_context context,
+ krb5_enctype etype1,
+ krb5_enctype etype2)
+{
+ struct encryption_type *e1 = _find_enctype(etype1);
+ struct encryption_type *e2 = _find_enctype(etype2);
+ return e1 != NULL && e2 != NULL && e1->keytype == e2->keytype;
+}
+
+static krb5_boolean
+derived_crypto(krb5_context context,
+ krb5_crypto crypto)
+{
+ return (crypto->et->flags & F_DERIVED) != 0;
+}
+
+static krb5_boolean
+special_crypto(krb5_context context,
+ krb5_crypto crypto)
+{
+ return (crypto->et->flags & F_SPECIAL) != 0;
+}
+
+#define CHECKSUMSIZE(C) ((C)->checksumsize)
+#define CHECKSUMTYPE(C) ((C)->type)
+
+static krb5_error_code
+encrypt_internal_derived(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t sz, block_sz, checksum_sz, total_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ const struct encryption_type *et = crypto->et;
+
+ checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+
+ sz = et->confoundersize + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+ total_sz = block_sz + checksum_sz;
+ p = calloc(1, total_sz);
+ if(p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ q = p;
+ krb5_generate_random_block(q, et->confoundersize); /* XXX */
+ q += et->confoundersize;
+ memcpy(q, data, len);
+
+ ret = create_checksum(context,
+ et->keyed_checksum,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ block_sz,
+ &cksum);
+ if(ret == 0 && cksum.checksum.length != checksum_sz) {
+ free_Checksum (&cksum);
+ krb5_clear_error_string (context);
+ ret = KRB5_CRYPTO_INTERNAL;
+ }
+ if(ret)
+ goto fail;
+ memcpy(p + block_sz, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum (&cksum);
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ goto fail;
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ goto fail;
+ ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec);
+ if (ret)
+ goto fail;
+ result->data = p;
+ result->length = total_sz;
+ return 0;
+ fail:
+ memset(p, 0, total_sz);
+ free(p);
+ return ret;
+}
+
+
+static krb5_error_code
+encrypt_internal(krb5_context context,
+ krb5_crypto crypto,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t sz, block_sz, checksum_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ const struct encryption_type *et = crypto->et;
+
+ checksum_sz = CHECKSUMSIZE(et->checksum);
+
+ sz = et->confoundersize + checksum_sz + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+ p = calloc(1, block_sz);
+ if(p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ q = p;
+ krb5_generate_random_block(q, et->confoundersize); /* XXX */
+ q += et->confoundersize;
+ memset(q, 0, checksum_sz);
+ q += checksum_sz;
+ memcpy(q, data, len);
+
+ ret = create_checksum(context,
+ et->checksum,
+ crypto,
+ 0,
+ p,
+ block_sz,
+ &cksum);
+ if(ret == 0 && cksum.checksum.length != checksum_sz) {
+ krb5_clear_error_string (context);
+ free_Checksum(&cksum);
+ ret = KRB5_CRYPTO_INTERNAL;
+ }
+ if(ret)
+ goto fail;
+ memcpy(p + et->confoundersize, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum(&cksum);
+ ret = _key_schedule(context, &crypto->key);
+ if(ret)
+ goto fail;
+ ret = (*et->encrypt)(context, &crypto->key, p, block_sz, 1, 0, ivec);
+ if (ret) {
+ memset(p, 0, block_sz);
+ free(p);
+ return ret;
+ }
+ result->data = p;
+ result->length = block_sz;
+ return 0;
+ fail:
+ memset(p, 0, block_sz);
+ free(p);
+ return ret;
+}
+
+static krb5_error_code
+encrypt_internal_special(krb5_context context,
+ krb5_crypto crypto,
+ int usage,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ struct encryption_type *et = crypto->et;
+ size_t cksum_sz = CHECKSUMSIZE(et->checksum);
+ size_t sz = len + cksum_sz + et->confoundersize;
+ char *tmp, *p;
+ krb5_error_code ret;
+
+ tmp = malloc (sz);
+ if (tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ p = tmp;
+ memset (p, 0, cksum_sz);
+ p += cksum_sz;
+ krb5_generate_random_block(p, et->confoundersize);
+ p += et->confoundersize;
+ memcpy (p, data, len);
+ ret = (*et->encrypt)(context, &crypto->key, tmp, sz, TRUE, usage, ivec);
+ if (ret) {
+ memset(tmp, 0, sz);
+ free(tmp);
+ return ret;
+ }
+ result->data = tmp;
+ result->length = sz;
+ return 0;
+}
+
+static krb5_error_code
+decrypt_internal_derived(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t checksum_sz;
+ Checksum cksum;
+ unsigned char *p;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ struct encryption_type *et = crypto->et;
+ unsigned long l;
+
+ checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+ if (len < checksum_sz + et->confoundersize) {
+ krb5_set_error_message(context, KRB5_BAD_MSIZE,
+ "Encrypted data shorter then "
+ "checksum + confunder");
+ return KRB5_BAD_MSIZE;
+ }
+
+ if (((len - checksum_sz) % et->padsize) != 0) {
+ krb5_clear_error_string(context);
+ return KRB5_BAD_MSIZE;
+ }
+
+ p = malloc(len);
+ if(len != 0 && p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(p, data, len);
+
+ len -= checksum_sz;
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = _key_schedule(context, dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ cksum.checksum.data = p + len;
+ cksum.checksum.length = checksum_sz;
+ cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum);
+
+ ret = verify_checksum(context,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ len,
+ &cksum);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ l = len - et->confoundersize;
+ memmove(p, p + et->confoundersize, l);
+ result->data = realloc(p, l);
+ if(result->data == NULL && l != 0) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ result->length = l;
+ return 0;
+}
+
+static krb5_error_code
+decrypt_internal(krb5_context context,
+ krb5_crypto crypto,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ krb5_error_code ret;
+ unsigned char *p;
+ Checksum cksum;
+ size_t checksum_sz, l;
+ struct encryption_type *et = crypto->et;
+
+ if ((len % et->padsize) != 0) {
+ krb5_clear_error_string(context);
+ return KRB5_BAD_MSIZE;
+ }
+
+ checksum_sz = CHECKSUMSIZE(et->checksum);
+ p = malloc(len);
+ if(len != 0 && p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(p, data, len);
+
+ ret = _key_schedule(context, &crypto->key);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = (*et->encrypt)(context, &crypto->key, p, len, 0, 0, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+ ret = krb5_data_copy(&cksum.checksum, p + et->confoundersize, checksum_sz);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ memset(p + et->confoundersize, 0, checksum_sz);
+ cksum.cksumtype = CHECKSUMTYPE(et->checksum);
+ ret = verify_checksum(context, NULL, 0, p, len, &cksum);
+ free_Checksum(&cksum);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ l = len - et->confoundersize - checksum_sz;
+ memmove(p, p + et->confoundersize + checksum_sz, l);
+ result->data = realloc(p, l);
+ if(result->data == NULL && l != 0) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ result->length = l;
+ return 0;
+}
+
+static krb5_error_code
+decrypt_internal_special(krb5_context context,
+ krb5_crypto crypto,
+ int usage,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ struct encryption_type *et = crypto->et;
+ size_t cksum_sz = CHECKSUMSIZE(et->checksum);
+ size_t sz = len - cksum_sz - et->confoundersize;
+ unsigned char *p;
+ krb5_error_code ret;
+
+ if ((len % et->padsize) != 0) {
+ krb5_clear_error_string(context);
+ return KRB5_BAD_MSIZE;
+ }
+
+ p = malloc (len);
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(p, data, len);
+
+ ret = (*et->encrypt)(context, &crypto->key, p, len, FALSE, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ memmove (p, p + cksum_sz + et->confoundersize, sz);
+ result->data = realloc(p, sz);
+ if(result->data == NULL && sz != 0) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ result->length = sz;
+ return 0;
+}
+
+/**
+ * Inline encrypt a kerberos message
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param ivec initial cbc/cts vector
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ *
+ * Kerberos encrypted data look like this:
+ *
+ * 1. KRB5_CRYPTO_TYPE_HEADER
+ * 2. array KRB5_CRYPTO_TYPE_DATA and KRB5_CRYPTO_TYPE_SIGN_ONLY in
+ * any order, however the receiver have to aware of the
+ * order. KRB5_CRYPTO_TYPE_SIGN_ONLY is commonly used headers and
+ * trailers.
+ * 3. KRB5_CRYPTO_TYPE_TRAILER
+ */
+
+static krb5_crypto_iov *
+find_iv(krb5_crypto_iov *data, int num_data, int type)
+{
+ int i;
+ for (i = 0; i < num_data; i++)
+ if (data[i].flags == type)
+ return &data[i];
+ return NULL;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt_iov_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ krb5_crypto_iov *data,
+ size_t num_data,
+ void *ivec)
+{
+ size_t headersz, trailersz, len;
+ size_t i, sz, block_sz, pad_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ const struct encryption_type *et = crypto->et;
+ krb5_crypto_iov *tiv, *piv, *hiv;
+
+ if(!derived_crypto(context, crypto)) {
+ krb5_clear_error_string(context);
+ return KRB5_CRYPTO_INTERNAL;
+ }
+
+ headersz = et->confoundersize;
+ trailersz = CHECKSUMSIZE(et->keyed_checksum);
+
+ for (len = 0, i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_HEADER &&
+ data[i].flags == KRB5_CRYPTO_TYPE_DATA) {
+ len += data[i].data.length;
+ }
+ }
+
+ sz = headersz + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+
+ pad_sz = block_sz - sz;
+ trailersz += pad_sz;
+
+ /* header */
+
+ hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ if (hiv == NULL || hiv->data.length != headersz)
+ return KRB5_BAD_MSIZE;
+
+ krb5_generate_random_block(hiv->data.data, hiv->data.length);
+
+ /* padding */
+
+ piv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
+ /* its ok to have no TYPE_PADDING if there is no padding */
+ if (piv == NULL && pad_sz != 0)
+ return KRB5_BAD_MSIZE;
+ if (piv) {
+ if (piv->data.length < pad_sz)
+ return KRB5_BAD_MSIZE;
+ piv->data.length = pad_sz;
+ }
+
+
+ /* trailer */
+
+ tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (tiv == NULL || tiv->data.length != trailersz)
+ return KRB5_BAD_MSIZE;
+
+
+ /*
+ * XXX replace with EVP_Sign? at least make create_checksum an iov
+ * function.
+ * XXX CTS EVP is broken, can't handle multi buffers :(
+ */
+
+ len = hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ ret = create_checksum(context,
+ et->keyed_checksum,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ len,
+ &cksum);
+ free(p);
+ if(ret == 0 && cksum.checksum.length != trailersz) {
+ free_Checksum (&cksum);
+ krb5_clear_error_string (context);
+ ret = KRB5_CRYPTO_INTERNAL;
+ }
+ if(ret)
+ return ret;
+
+ /* save cksum at end */
+ memcpy(tiv->data.data, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum (&cksum);
+
+ /* now encrypt data */
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ return ret;
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ return ret;
+
+ /* XXX replace with EVP_Cipher */
+
+ len = hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_PADDING)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+ if(p == NULL)
+ return ENOMEM;
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_PADDING)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = _key_schedule(context, dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+
+ ret = (*et->encrypt)(context, dkey, p, len, 1, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ /* now copy data back to buffers */
+ q = p;
+ memcpy(hiv->data.data, q, hiv->data.length);
+ q += hiv->data.length;
+
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_PADDING)
+ continue;
+ memcpy(data[i].data.data, q, data[i].data.length);
+ q += data[i].data.length;
+ }
+ free(p);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_iov_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ krb5_crypto_iov *data,
+ size_t num_data,
+ void *ivec)
+{
+ size_t headersz, trailersz, len;
+ size_t i, sz, block_sz, pad_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ struct encryption_type *et = crypto->et;
+ krb5_crypto_iov *tiv, *hiv;
+
+ if(!derived_crypto(context, crypto)) {
+ krb5_clear_error_string(context);
+ return KRB5_CRYPTO_INTERNAL;
+ }
+
+ headersz = et->confoundersize;
+ trailersz = CHECKSUMSIZE(et->keyed_checksum);
+
+ for (len = 0, i = 0; i < num_data; i++)
+ if (data[i].flags == KRB5_CRYPTO_TYPE_DATA)
+ len += data[i].data.length;
+
+ sz = headersz + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+
+ pad_sz = block_sz - sz;
+ trailersz += pad_sz;
+
+ /* header */
+
+ hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ if (hiv == NULL || hiv->data.length < headersz)
+ return KRB5_BAD_MSIZE;
+ hiv->data.length = headersz;
+
+ /* trailer */
+
+ tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (tiv == NULL || tiv->data.length < trailersz)
+ return KRB5_BAD_MSIZE;
+ tiv->data.length = trailersz;
+
+ /* body */
+
+ /* XXX replace with EVP_Cipher */
+
+ for (len = 0, i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_HEADER &&
+ data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+ if (p == NULL)
+ return ENOMEM;
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = _key_schedule(context, dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+
+ ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ /* XXX now copy data back to buffers */
+ q = p;
+ memcpy(hiv->data.data, q, hiv->data.length);
+ q += hiv->data.length;
+ len -= hiv->data.length;
+
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ if (len < data[i].data.length)
+ data[i].data.length = len;
+ memcpy(data[i].data.data, q, data[i].data.length);
+ q += data[i].data.length;
+ len -= data[i].data.length;
+ }
+ free(p);
+ if (len)
+ krb5_abortx(context, "data still in the buffer");
+
+ len = hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ cksum.checksum.data = tiv->data.data;
+ cksum.checksum.length = tiv->data.length;
+ cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum);
+
+ ret = verify_checksum(context,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ len,
+ &cksum);
+ free(p);
+ if(ret)
+ return ret;
+
+ return 0;
+}
+
+size_t KRB5_LIB_FUNCTION
+krb5_crypto_length(krb5_context context,
+ krb5_crypto crypto,
+ int type)
+{
+ if (!derived_crypto(context, crypto))
+ return (size_t)-1;
+ switch(type) {
+ case KRB5_CRYPTO_TYPE_EMPTY:
+ return 0;
+ case KRB5_CRYPTO_TYPE_HEADER:
+ return crypto->et->blocksize;
+ case KRB5_CRYPTO_TYPE_PADDING:
+ if (crypto->et->padsize > 1)
+ return crypto->et->padsize;
+ return 0;
+ case KRB5_CRYPTO_TYPE_TRAILER:
+ return CHECKSUMSIZE(crypto->et->keyed_checksum);
+ }
+ return (size_t)-1;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ if(derived_crypto(context, crypto))
+ return encrypt_internal_derived(context, crypto, usage,
+ data, len, result, ivec);
+ else if (special_crypto(context, crypto))
+ return encrypt_internal_special (context, crypto, usage,
+ data, len, result, ivec);
+ else
+ return encrypt_internal(context, crypto, data, len, result, ivec);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const void *data,
+ size_t len,
+ krb5_data *result)
+{
+ return krb5_encrypt_ivec(context, crypto, usage, data, len, result, NULL);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt_EncryptedData(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ int kvno,
+ EncryptedData *result)
+{
+ result->etype = CRYPTO_ETYPE(crypto);
+ if(kvno){
+ ALLOC(result->kvno, 1);
+ *result->kvno = kvno;
+ }else
+ result->kvno = NULL;
+ return krb5_encrypt(context, crypto, usage, data, len, &result->cipher);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ if(derived_crypto(context, crypto))
+ return decrypt_internal_derived(context, crypto, usage,
+ data, len, result, ivec);
+ else if (special_crypto (context, crypto))
+ return decrypt_internal_special(context, crypto, usage,
+ data, len, result, ivec);
+ else
+ return decrypt_internal(context, crypto, data, len, result, ivec);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ krb5_data *result)
+{
+ return krb5_decrypt_ivec (context, crypto, usage, data, len, result,
+ NULL);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_EncryptedData(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const EncryptedData *e,
+ krb5_data *result)
+{
+ return krb5_decrypt(context, crypto, usage,
+ e->cipher.data, e->cipher.length, result);
+}
+
+/************************************************************
+ * *
+ ************************************************************/
+
+#define ENTROPY_NEEDED 128
+
+static int
+seed_something(void)
+{
+ char buf[1024], seedfile[256];
+
+ /* If there is a seed file, load it. But such a file cannot be trusted,
+ so use 0 for the entropy estimate */
+ if (RAND_file_name(seedfile, sizeof(seedfile))) {
+ int fd;
+ fd = open(seedfile, O_RDONLY | O_BINARY | O_CLOEXEC);
+ if (fd >= 0) {
+ ssize_t ret;
+ rk_cloexec(fd);
+ ret = read(fd, buf, sizeof(buf));
+ if (ret > 0)
+ RAND_add(buf, ret, 0.0);
+ close(fd);
+ } else
+ seedfile[0] = '\0';
+ } else
+ seedfile[0] = '\0';
+
+ /* Calling RAND_status() will try to use /dev/urandom if it exists so
+ we do not have to deal with it. */
+ if (RAND_status() != 1) {
+ krb5_context context;
+ const char *p;
+
+ /* Try using egd */
+ if (!krb5_init_context(&context)) {
+ p = krb5_config_get_string(context, NULL, "libdefaults",
+ "egd_socket", NULL);
+ if (p != NULL)
+ RAND_egd_bytes(p, ENTROPY_NEEDED);
+ krb5_free_context(context);
+ }
+ }
+
+ if (RAND_status() == 1) {
+ /* Update the seed file */
+ if (seedfile[0])
+ RAND_write_file(seedfile);
+
+ return 0;
+ } else
+ return -1;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_generate_random_block(void *buf, size_t len)
+{
+ static int rng_initialized = 0;
+
+ HEIMDAL_MUTEX_lock(&crypto_mutex);
+ if (!rng_initialized) {
+ if (seed_something())
+ krb5_abortx(NULL, "Fatal: could not seed the "
+ "random number generator");
+
+ rng_initialized = 1;
+ }
+ HEIMDAL_MUTEX_unlock(&crypto_mutex);
+ if (RAND_bytes(buf, len) != 1)
+ krb5_abortx(NULL, "Failed to generate random block");
+}
+
+static void
+DES3_postproc(krb5_context context,
+ unsigned char *k, size_t len, struct key_data *key)
+{
+ DES3_random_to_key(context, key->key, k, len);
+
+ if (key->schedule) {
+ krb5_free_data(context, key->schedule);
+ key->schedule = NULL;
+ }
+}
+
+static krb5_error_code
+derive_key(krb5_context context,
+ struct encryption_type *et,
+ struct key_data *key,
+ const void *constant,
+ size_t len)
+{
+ unsigned char *k;
+ unsigned int nblocks = 0, i;
+ krb5_error_code ret = 0;
+ struct key_type *kt = et->keytype;
+
+ ret = _key_schedule(context, key);
+ if(ret)
+ return ret;
+ if(et->blocksize * 8 < kt->bits || len != et->blocksize) {
+ nblocks = (kt->bits + et->blocksize * 8 - 1) / (et->blocksize * 8);
+ k = malloc(nblocks * et->blocksize);
+ if(k == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = _krb5_n_fold(constant, len, k, et->blocksize);
+ if (ret) {
+ free(k);
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ return ret;
+ }
+ for(i = 0; i < nblocks; i++) {
+ if(i > 0)
+ memcpy(k + i * et->blocksize,
+ k + (i - 1) * et->blocksize,
+ et->blocksize);
+ (*et->encrypt)(context, key, k + i * et->blocksize, et->blocksize,
+ 1, 0, NULL);
+ }
+ } else {
+ /* this case is probably broken, but won't be run anyway */
+ void *c = malloc(len);
+ size_t res_len = (kt->bits + 7) / 8;
+
+ if(len != 0 && c == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(c, constant, len);
+ (*et->encrypt)(context, key, c, len, 1, 0, NULL);
+ k = malloc(res_len);
+ if(res_len != 0 && k == NULL) {
+ free(c);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = _krb5_n_fold(c, len, k, res_len);
+ if (ret) {
+ free(k);
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ return ret;
+ }
+ free(c);
+ }
+
+ /* XXX keytype dependent post-processing */
+ switch(kt->type) {
+ case KEYTYPE_DES3:
+ DES3_postproc(context, k, nblocks * et->blocksize, key);
+ break;
+ case KEYTYPE_AES128:
+ case KEYTYPE_AES256:
+ memcpy(key->key->keyvalue.data, k, key->key->keyvalue.length);
+ break;
+ default:
+ ret = KRB5_CRYPTO_INTERNAL;
+ krb5_set_error_message(context, ret,
+ "derive_key() called with unknown keytype (%u)",
+ kt->type);
+ break;
+ }
+ if (key->schedule) {
+ krb5_free_data(context, key->schedule);
+ key->schedule = NULL;
+ }
+ memset(k, 0, nblocks * et->blocksize);
+ free(k);
+ return ret;
+}
+
+static struct key_data *
+_new_derived_key(krb5_crypto crypto, unsigned usage)
+{
+ struct key_usage *d = crypto->key_usage;
+ d = realloc(d, (crypto->num_key_usage + 1) * sizeof(*d));
+ if(d == NULL)
+ return NULL;
+ crypto->key_usage = d;
+ d += crypto->num_key_usage++;
+ memset(d, 0, sizeof(*d));
+ d->usage = usage;
+ return &d->key;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_derive_key(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_enctype etype,
+ const void *constant,
+ size_t constant_len,
+ krb5_keyblock **derived_key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et;
+ struct key_data d;
+
+ *derived_key = NULL;
+
+ et = _find_enctype (etype);
+ if (et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ ret = krb5_copy_keyblock(context, key, &d.key);
+ if (ret)
+ return ret;
+
+ d.schedule = NULL;
+ ret = derive_key(context, et, &d, constant, constant_len);
+ if (ret == 0)
+ ret = krb5_copy_keyblock(context, d.key, derived_key);
+ free_key_data(context, &d, et);
+ return ret;
+}
+
+static krb5_error_code
+_get_derived_key(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ struct key_data **key)
+{
+ int i;
+ struct key_data *d;
+ unsigned char constant[5];
+
+ for(i = 0; i < crypto->num_key_usage; i++)
+ if(crypto->key_usage[i].usage == usage) {
+ *key = &crypto->key_usage[i].key;
+ return 0;
+ }
+ d = _new_derived_key(crypto, usage);
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_copy_keyblock(context, crypto->key.key, &d->key);
+ _krb5_put_int(constant, usage, 5);
+ derive_key(context, crypto->et, d, constant, sizeof(constant));
+ *key = d;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_init(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_enctype etype,
+ krb5_crypto *crypto)
+{
+ krb5_error_code ret;
+ ALLOC(*crypto, 1);
+ if(*crypto == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ if(etype == ETYPE_NULL)
+ etype = key->keytype;
+ (*crypto)->et = _find_enctype(etype);
+ if((*crypto)->et == NULL || ((*crypto)->et->flags & F_DISABLED)) {
+ free(*crypto);
+ *crypto = NULL;
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if((*crypto)->et->keytype->size != key->keyvalue.length) {
+ free(*crypto);
+ *crypto = NULL;
+ krb5_set_error_message (context, KRB5_BAD_KEYSIZE,
+ "encryption key has bad length");
+ return KRB5_BAD_KEYSIZE;
+ }
+ ret = krb5_copy_keyblock(context, key, &(*crypto)->key.key);
+ if(ret) {
+ free(*crypto);
+ *crypto = NULL;
+ return ret;
+ }
+ (*crypto)->key.schedule = NULL;
+ (*crypto)->num_key_usage = 0;
+ (*crypto)->key_usage = NULL;
+ return 0;
+}
+
+static void
+free_key_data(krb5_context context, struct key_data *key,
+ struct encryption_type *et)
+{
+ krb5_free_keyblock(context, key->key);
+ if(key->schedule) {
+ if (et->keytype->cleanup)
+ (*et->keytype->cleanup)(context, key);
+ memset(key->schedule->data, 0, key->schedule->length);
+ krb5_free_data(context, key->schedule);
+ }
+}
+
+static void
+free_key_usage(krb5_context context, struct key_usage *ku,
+ struct encryption_type *et)
+{
+ free_key_data(context, &ku->key, et);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_destroy(krb5_context context,
+ krb5_crypto crypto)
+{
+ int i;
+
+ for(i = 0; i < crypto->num_key_usage; i++)
+ free_key_usage(context, &crypto->key_usage[i], crypto->et);
+ free(crypto->key_usage);
+ free_key_data(context, &crypto->key, crypto->et);
+ free (crypto);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getblocksize(krb5_context context,
+ krb5_crypto crypto,
+ size_t *blocksize)
+{
+ *blocksize = crypto->et->blocksize;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getenctype(krb5_context context,
+ krb5_crypto crypto,
+ krb5_enctype *enctype)
+{
+ *enctype = crypto->et->type;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getpadsize(krb5_context context,
+ krb5_crypto crypto,
+ size_t *padsize)
+{
+ *padsize = crypto->et->padsize;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getconfoundersize(krb5_context context,
+ krb5_crypto crypto,
+ size_t *confoundersize)
+{
+ *confoundersize = crypto->et->confoundersize;
+ return 0;
+}
+
+
+/**
+ * Disable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to disable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_disable(krb5_context context,
+ krb5_enctype enctype)
+{
+ struct encryption_type *et = _find_enctype(enctype);
+ if(et == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ et->flags |= F_DISABLED;
+ return 0;
+}
+
+/**
+ * Enable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to enable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_enable(krb5_context context,
+ krb5_enctype enctype)
+{
+ struct encryption_type *et = _find_enctype(enctype);
+ if(et == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ et->flags &= ~F_DISABLED;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_derived(krb5_context context,
+ const void *str,
+ size_t len,
+ krb5_enctype etype,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et = _find_enctype(etype);
+ krb5_error_code ret;
+ struct key_data kd;
+ size_t keylen;
+ u_char *tmp;
+
+ if(et == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ keylen = et->keytype->bits / 8;
+
+ ALLOC(kd.key, 1);
+ if(kd.key == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if(ret) {
+ free(kd.key);
+ return ret;
+ }
+ kd.key->keytype = etype;
+ tmp = malloc (keylen);
+ if(tmp == NULL) {
+ krb5_free_keyblock(context, kd.key);
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = _krb5_n_fold(str, len, tmp, keylen);
+ if (ret) {
+ free(tmp);
+ krb5_set_error_message (context, ENOMEM, "malloc: out of memory");
+ return ret;
+ }
+ kd.schedule = NULL;
+ DES3_postproc (context, tmp, keylen, &kd); /* XXX */
+ memset(tmp, 0, keylen);
+ free(tmp);
+ ret = derive_key(context,
+ et,
+ &kd,
+ "kerberos", /* XXX well known constant */
+ strlen("kerberos"));
+ if (ret) {
+ free_key_data(context, &kd, et);
+ return ret;
+ }
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ free_key_data(context, &kd, et);
+ return ret;
+}
+
+static size_t
+wrapped_length (krb5_context context,
+ krb5_crypto crypto,
+ size_t data_len)
+{
+ struct encryption_type *et = crypto->et;
+ size_t padsize = et->padsize;
+ size_t checksumsize = CHECKSUMSIZE(et->checksum);
+ size_t res;
+
+ res = et->confoundersize + checksumsize + data_len;
+ res = (res + padsize - 1) / padsize * padsize;
+ return res;
+}
+
+static size_t
+wrapped_length_dervied (krb5_context context,
+ krb5_crypto crypto,
+ size_t data_len)
+{
+ struct encryption_type *et = crypto->et;
+ size_t padsize = et->padsize;
+ size_t res;
+
+ res = et->confoundersize + data_len;
+ res = (res + padsize - 1) / padsize * padsize;
+ if (et->keyed_checksum)
+ res += et->keyed_checksum->checksumsize;
+ else
+ res += et->checksum->checksumsize;
+ return res;
+}
+
+/*
+ * Return the size of an encrypted packet of length `data_len'
+ */
+
+size_t
+krb5_get_wrapped_length (krb5_context context,
+ krb5_crypto crypto,
+ size_t data_len)
+{
+ if (derived_crypto (context, crypto))
+ return wrapped_length_dervied (context, crypto, data_len);
+ else
+ return wrapped_length (context, crypto, data_len);
+}
+
+/*
+ * Return the size of an encrypted packet of length `data_len'
+ */
+
+static size_t
+crypto_overhead (krb5_context context,
+ krb5_crypto crypto)
+{
+ struct encryption_type *et = crypto->et;
+ size_t res;
+
+ res = CHECKSUMSIZE(et->checksum);
+ res += et->confoundersize;
+ if (et->padsize > 1)
+ res += et->padsize;
+ return res;
+}
+
+static size_t
+crypto_overhead_dervied (krb5_context context,
+ krb5_crypto crypto)
+{
+ struct encryption_type *et = crypto->et;
+ size_t res;
+
+ if (et->keyed_checksum)
+ res = CHECKSUMSIZE(et->keyed_checksum);
+ else
+ res = CHECKSUMSIZE(et->checksum);
+ res += et->confoundersize;
+ if (et->padsize > 1)
+ res += et->padsize;
+ return res;
+}
+
+size_t
+krb5_crypto_overhead (krb5_context context, krb5_crypto crypto)
+{
+ if (derived_crypto (context, crypto))
+ return crypto_overhead_dervied (context, crypto);
+ else
+ return crypto_overhead (context, crypto);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_random_to_key(krb5_context context,
+ krb5_enctype type,
+ const void *data,
+ size_t size,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if ((et->keytype->bits + 7) / 8 > size) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption key %s needs %d bytes "
+ "of random to make an encryption key out of it",
+ et->name, (int)et->keytype->size);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ ret = krb5_data_alloc(&key->keyvalue, et->keytype->size);
+ if(ret)
+ return ret;
+ key->keytype = type;
+ if (et->keytype->random_to_key)
+ (*et->keytype->random_to_key)(context, key, data, size);
+ else
+ memcpy(key->keyvalue.data, data, et->keytype->size);
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_pk_octetstring2key(krb5_context context,
+ krb5_enctype type,
+ const void *dhdata,
+ size_t dhsize,
+ const heim_octet_string *c_n,
+ const heim_octet_string *k_n,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et = _find_enctype(type);
+ krb5_error_code ret;
+ size_t keylen, offset;
+ void *keydata;
+ unsigned char counter;
+ unsigned char shaoutput[20];
+
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ keylen = (et->keytype->bits + 7) / 8;
+
+ keydata = malloc(keylen);
+ if (keydata == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ counter = 0;
+ offset = 0;
+ do {
+ SHA_CTX m;
+
+ SHA1_Init(&m);
+ SHA1_Update(&m, &counter, 1);
+ SHA1_Update(&m, dhdata, dhsize);
+ if (c_n)
+ SHA1_Update(&m, c_n->data, c_n->length);
+ if (k_n)
+ SHA1_Update(&m, k_n->data, k_n->length);
+ SHA1_Final(shaoutput, &m);
+
+ memcpy((unsigned char *)keydata + offset,
+ shaoutput,
+ min(keylen - offset, sizeof(shaoutput)));
+
+ offset += sizeof(shaoutput);
+ counter++;
+ } while(offset < keylen);
+ memset(shaoutput, 0, sizeof(shaoutput));
+
+ ret = krb5_random_to_key(context, type, keydata, keylen, key);
+ memset(keydata, 0, sizeof(keylen));
+ free(keydata);
+ return ret;
+}
+
+static krb5_error_code
+encode_uvinfo(krb5_context context, krb5_const_principal p, krb5_data *data)
+{
+ KRB5PrincipalName pn;
+ krb5_error_code ret;
+ size_t size;
+
+ pn.principalName = p->name;
+ pn.realm = p->realm;
+
+ ASN1_MALLOC_ENCODE(KRB5PrincipalName, data->data, data->length,
+ &pn, &size, ret);
+ if (ret) {
+ krb5_data_zero(data);
+ krb5_set_error_message(context, ret,
+ "Failed to encode KRB5PrincipalName");
+ return ret;
+ }
+ if (data->length != size)
+ krb5_abortx(context, "asn1 compiler internal error");
+ return 0;
+}
+
+static krb5_error_code
+encode_otherinfo(krb5_context context,
+ const AlgorithmIdentifier *ai,
+ krb5_const_principal client,
+ krb5_const_principal server,
+ krb5_enctype enctype,
+ const krb5_data *as_req,
+ const krb5_data *pk_as_rep,
+ const Ticket *ticket,
+ krb5_data *other)
+{
+ PkinitSP80056AOtherInfo otherinfo;
+ PkinitSuppPubInfo pubinfo;
+ krb5_error_code ret;
+ krb5_data pub;
+ size_t size;
+
+ krb5_data_zero(other);
+ memset(&otherinfo, 0, sizeof(otherinfo));
+ memset(&pubinfo, 0, sizeof(pubinfo));
+
+ pubinfo.enctype = enctype;
+ pubinfo.as_REQ = *as_req;
+ pubinfo.pk_as_rep = *pk_as_rep;
+ pubinfo.ticket = *ticket;
+ ASN1_MALLOC_ENCODE(PkinitSuppPubInfo, pub.data, pub.length,
+ &pubinfo, &size, ret);
+ if (ret) {
+ krb5_set_error_message(context, ret, "out of memory");
+ return ret;
+ }
+ if (pub.length != size)
+ krb5_abortx(context, "asn1 compiler internal error");
+
+ ret = encode_uvinfo(context, client, &otherinfo.partyUInfo);
+ if (ret) {
+ free(pub.data);
+ return ret;
+ }
+ ret = encode_uvinfo(context, server, &otherinfo.partyVInfo);
+ if (ret) {
+ free(otherinfo.partyUInfo.data);
+ free(pub.data);
+ return ret;
+ }
+
+ otherinfo.algorithmID = *ai;
+ otherinfo.suppPubInfo = &pub;
+
+ ASN1_MALLOC_ENCODE(PkinitSP80056AOtherInfo, other->data, other->length,
+ &otherinfo, &size, ret);
+ free(otherinfo.partyUInfo.data);
+ free(otherinfo.partyVInfo.data);
+ free(pub.data);
+ if (ret) {
+ krb5_set_error_message(context, ret, "out of memory");
+ return ret;
+ }
+ if (other->length != size)
+ krb5_abortx(context, "asn1 compiler internal error");
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_pk_kdf(krb5_context context,
+ const struct AlgorithmIdentifier *ai,
+ const void *dhdata,
+ size_t dhsize,
+ krb5_const_principal client,
+ krb5_const_principal server,
+ krb5_enctype enctype,
+ const krb5_data *as_req,
+ const krb5_data *pk_as_rep,
+ const Ticket *ticket,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et;
+ krb5_error_code ret;
+ krb5_data other;
+ size_t keylen, offset;
+ uint32_t counter;
+ unsigned char *keydata;
+ unsigned char shaoutput[20];
+
+ if (der_heim_oid_cmp(oid_id_pkinit_kdf_ah_sha1(), &ai->algorithm) != 0) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "kdf not supported");
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if (ai->parameters != NULL &&
+ (ai->parameters->length != 2 ||
+ memcmp(ai->parameters->data, "\x05\x00", 2) != 0))
+ {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "kdf params not NULL or the NULL-type");
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ et = _find_enctype(enctype);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ keylen = (et->keytype->bits + 7) / 8;
+
+ keydata = malloc(keylen);
+ if (keydata == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = encode_otherinfo(context, ai, client, server,
+ enctype, as_req, pk_as_rep, ticket, &other);
+ if (ret) {
+ free(keydata);
+ return ret;
+ }
+
+ offset = 0;
+ counter = 1;
+ do {
+ unsigned char cdata[4];
+ SHA_CTX m;
+
+ SHA1_Init(&m);
+ _krb5_put_int(cdata, counter, 4);
+ SHA1_Update(&m, cdata, 4);
+ SHA1_Update(&m, dhdata, dhsize);
+ SHA1_Update(&m, other.data, other.length);
+ SHA1_Final(shaoutput, &m);
+
+ memcpy((unsigned char *)keydata + offset,
+ shaoutput,
+ min(keylen - offset, sizeof(shaoutput)));
+
+ offset += sizeof(shaoutput);
+ counter++;
+ } while(offset < keylen);
+ memset(shaoutput, 0, sizeof(shaoutput));
+
+ free(other.data);
+
+ ret = krb5_random_to_key(context, enctype, keydata, keylen, key);
+ memset(keydata, 0, sizeof(keylen));
+ free(keydata);
+
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_prf_length(krb5_context context,
+ krb5_enctype type,
+ size_t *length)
+{
+ struct encryption_type *et = _find_enctype(type);
+
+ if(et == NULL || et->prf_length == 0) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ *length = et->prf_length;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_prf(krb5_context context,
+ const krb5_crypto crypto,
+ const krb5_data *input,
+ krb5_data *output)
+{
+ struct encryption_type *et = crypto->et;
+
+ krb5_data_zero(output);
+
+ if(et->prf == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "kerberos prf for %s not supported",
+ et->name);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ return (*et->prf)(context, crypto, input, output);
+}
+
+#ifndef HEIMDAL_SMALLER
+
+/*
+ * First take the configured list of etypes for `keytype' if available,
+ * else, do `krb5_keytype_to_enctypes'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytype_to_enctypes_default (krb5_context context,
+ krb5_keytype keytype,
+ unsigned *len,
+ krb5_enctype **val)
+ __attribute__((deprecated))
+{
+ unsigned int i, n;
+ krb5_enctype *ret;
+
+ if (keytype != KEYTYPE_DES || context->etypes_des == NULL)
+ return krb5_keytype_to_enctypes (context, keytype, len, val);
+
+ for (n = 0; context->etypes_des[n]; ++n)
+ ;
+ ret = malloc (n * sizeof(*ret));
+ if (ret == NULL && n != 0) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ for (i = 0; i < n; ++i)
+ ret[i] = context->etypes_des[i];
+ *len = n;
+ *val = ret;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytype_to_string(krb5_context context,
+ krb5_keytype keytype,
+ char **string)
+ __attribute__((deprecated))
+{
+ struct key_type *kt = _find_keytype(keytype);
+ if(kt == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "key type %d not supported", keytype);
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+ }
+ *string = strdup(kt->name);
+ if(*string == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_keytype(krb5_context context,
+ const char *string,
+ krb5_keytype *keytype)
+ __attribute__((deprecated))
+{
+ char *end;
+ int i;
+
+ for(i = 0; i < num_keytypes; i++)
+ if(strcasecmp(keytypes[i]->name, string) == 0){
+ *keytype = keytypes[i]->type;
+ return 0;
+ }
+
+ /* check if the enctype is a number */
+ *keytype = strtol(string, &end, 0);
+ if(*end == '\0' && *keytype != 0) {
+ if (krb5_enctype_valid(context, *keytype) == 0)
+ return 0;
+ }
+
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "key type %s not supported", string);
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytype_to_enctypes (krb5_context context,
+ krb5_keytype keytype,
+ unsigned *len,
+ krb5_enctype **val)
+{
+ int i;
+ unsigned n = 0;
+ krb5_enctype *ret;
+
+ for (i = num_etypes - 1; i >= 0; --i) {
+ if (etypes[i]->keytype->type == keytype
+ && !(etypes[i]->flags & F_PSEUDO))
+ ++n;
+ }
+ ret = malloc(n * sizeof(*ret));
+ if (ret == NULL && n != 0) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ n = 0;
+ for (i = num_etypes - 1; i >= 0; --i) {
+ if (etypes[i]->keytype->type == keytype
+ && !(etypes[i]->flags & F_PSEUDO))
+ ret[n++] = etypes[i]->type;
+ }
+ *len = n;
+ *val = ret;
+ return 0;
+}
+
+#endif
diff --git a/source4/heimdal/lib/krb5/data.c b/source4/heimdal/lib/krb5/data.c
new file mode 100644
index 0000000000..0286316214
--- /dev/null
+++ b/source4/heimdal/lib/krb5/data.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Reset the (potentially uninitalized) krb5_data structure.
+ *
+ * @param p krb5_data to reset.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_data_zero(krb5_data *p)
+{
+ p->length = 0;
+ p->data = NULL;
+}
+
+/**
+ * Free the content of krb5_data structure, its ok to free a zeroed
+ * structure. When done, the structure will be zeroed.
+ *
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_data_free(krb5_data *p)
+{
+ if(p->data != NULL)
+ free(p->data);
+ krb5_data_zero(p);
+}
+
+/**
+ * Same as krb5_data_free().
+ *
+ * @param context Kerberos 5 context.
+ * @param data krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_data_contents(krb5_context context, krb5_data *data)
+{
+ krb5_data_free(data);
+}
+
+/**
+ * Free krb5_data (and its content).
+ *
+ * @param context Kerberos 5 context.
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_data(krb5_context context,
+ krb5_data *p)
+{
+ krb5_data_free(p);
+ free(p);
+}
+
+/**
+ * Allocate data of and krb5_data.
+ *
+ * @param p krb5_data to free.
+ * @param len size to allocate.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_data_alloc(krb5_data *p, int len)
+{
+ p->data = malloc(len);
+ if(len && p->data == NULL)
+ return ENOMEM;
+ p->length = len;
+ return 0;
+}
+
+/**
+ * Grow (or shrink) the content of krb5_data to a new size.
+ *
+ * @param p krb5_data to free.
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_data_realloc(krb5_data *p, int len)
+{
+ void *tmp;
+ tmp = realloc(p->data, len);
+ if(len && !tmp)
+ return ENOMEM;
+ p->data = tmp;
+ p->length = len;
+ return 0;
+}
+
+/**
+ * Copy the data of len into the krb5_data.
+ *
+ * @param p krb5_data to copy into.
+ * @param data data to copy..
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_data_copy(krb5_data *p, const void *data, size_t len)
+{
+ if (len) {
+ if(krb5_data_alloc(p, len))
+ return ENOMEM;
+ memmove(p->data, data, len);
+ } else
+ p->data = NULL;
+ p->length = len;
+ return 0;
+}
+
+/**
+ * Copy the data into a newly allocated krb5_data.
+ *
+ * @param context Kerberos 5 context.
+ * @param indata the krb5_data data to copy
+ * @param outdata new krb5_date to copy too. Free with krb5_free_data().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_data(krb5_context context,
+ const krb5_data *indata,
+ krb5_data **outdata)
+{
+ krb5_error_code ret;
+ ALLOC(*outdata, 1);
+ if(*outdata == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = der_copy_octet_string(indata, *outdata);
+ if(ret) {
+ krb5_clear_error_string (context);
+ free(*outdata);
+ *outdata = NULL;
+ }
+ return ret;
+}
+
+/**
+ * Compare to data.
+ *
+ * @param data1 krb5_data to compare
+ * @param data2 krb5_data to compare
+ *
+ * @return return the same way as memcmp(), useful when sorting.
+ *
+ * @ingroup krb5
+ */
+
+int KRB5_LIB_FUNCTION
+krb5_data_cmp(const krb5_data *data1, const krb5_data *data2)
+{
+ if (data1->length != data2->length)
+ return data1->length - data2->length;
+ return memcmp(data1->data, data2->data, data1->length);
+}
diff --git a/source4/heimdal/lib/krb5/eai_to_heim_errno.c b/source4/heimdal/lib/krb5/eai_to_heim_errno.c
new file mode 100644
index 0000000000..c06e8fb9bb
--- /dev/null
+++ b/source4/heimdal/lib/krb5/eai_to_heim_errno.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2000 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+/**
+ * Convert the getaddrinfo() error code to a Kerberos et error code.
+ *
+ * @param eai_errno contains the error code from getaddrinfo().
+ * @param system_error should have the value of errno after the failed getaddrinfo().
+ *
+ * @return Kerberos error code representing the EAI errors.
+ *
+ * @ingroup krb5_error
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_eai_to_heim_errno(int eai_errno, int system_error)
+{
+ switch(eai_errno) {
+ case EAI_NOERROR:
+ return 0;
+#ifdef EAI_ADDRFAMILY
+ case EAI_ADDRFAMILY:
+ return HEIM_EAI_ADDRFAMILY;
+#endif
+ case EAI_AGAIN:
+ return HEIM_EAI_AGAIN;
+ case EAI_BADFLAGS:
+ return HEIM_EAI_BADFLAGS;
+ case EAI_FAIL:
+ return HEIM_EAI_FAIL;
+ case EAI_FAMILY:
+ return HEIM_EAI_FAMILY;
+ case EAI_MEMORY:
+ return HEIM_EAI_MEMORY;
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
+ case EAI_NODATA:
+ return HEIM_EAI_NODATA;
+#endif
+ case EAI_NONAME:
+ return HEIM_EAI_NONAME;
+ case EAI_SERVICE:
+ return HEIM_EAI_SERVICE;
+ case EAI_SOCKTYPE:
+ return HEIM_EAI_SOCKTYPE;
+ case EAI_SYSTEM:
+ return system_error;
+ default:
+ return HEIM_EAI_UNKNOWN; /* XXX */
+ }
+}
+
+/**
+ * Convert the gethostname() error code (h_error) to a Kerberos et
+ * error code.
+ *
+ * @param eai_errno contains the error code from gethostname().
+ *
+ * @return Kerberos error code representing the gethostname errors.
+ *
+ * @ingroup krb5_error
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_h_errno_to_heim_errno(int eai_errno)
+{
+ switch(eai_errno) {
+ case 0:
+ return 0;
+ case HOST_NOT_FOUND:
+ return HEIM_EAI_NONAME;
+ case TRY_AGAIN:
+ return HEIM_EAI_AGAIN;
+ case NO_RECOVERY:
+ return HEIM_EAI_FAIL;
+ case NO_DATA:
+ return HEIM_EAI_NONAME;
+ default:
+ return HEIM_EAI_UNKNOWN; /* XXX */
+ }
+}
diff --git a/source4/heimdal/lib/krb5/error_string.c b/source4/heimdal/lib/krb5/error_string.c
new file mode 100644
index 0000000000..17bc30572b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/error_string.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2001, 2003, 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#undef __attribute__
+#define __attribute__(X)
+
+void KRB5_LIB_FUNCTION
+krb5_clear_error_string(krb5_context context)
+{
+ HEIMDAL_MUTEX_lock(context->mutex);
+ if (context->error_string)
+ free(context->error_string);
+ context->error_code = 0;
+ context->error_string = NULL;
+ HEIMDAL_MUTEX_unlock(context->mutex);
+}
+
+/**
+ * Set the context full error string for a specific error code.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param ... printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_error_message(krb5_context context, krb5_error_code ret,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ krb5_vset_error_message (context, ret, fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * Set the context full error string for a specific error code.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param args printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
+
+
+void KRB5_LIB_FUNCTION
+krb5_vset_error_message (krb5_context context, krb5_error_code ret,
+ const char *fmt, va_list args)
+ __attribute__ ((format (printf, 3, 0)))
+{
+
+ krb5_clear_error_string(context);
+ HEIMDAL_MUTEX_lock(context->mutex);
+ context->error_code = ret;
+ vasprintf(&context->error_string, fmt, args);
+ HEIMDAL_MUTEX_unlock(context->mutex);
+}
+
+
+/**
+ * Return the error message in context. On error or no error string,
+ * the function returns NULL.
+ *
+ * @param context Kerberos 5 context
+ *
+ * @return an error string, needs to be freed with
+ * krb5_free_error_message(). The functions return NULL on error.
+ *
+ * @ingroup krb5_error
+ */
+
+char * KRB5_LIB_FUNCTION
+krb5_get_error_string(krb5_context context)
+{
+ char *ret = NULL;
+
+ HEIMDAL_MUTEX_lock(context->mutex);
+ if (context->error_string)
+ ret = strdup(context->error_string);
+ HEIMDAL_MUTEX_unlock(context->mutex);
+ return ret;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_have_error_string(krb5_context context)
+{
+ char *str;
+ HEIMDAL_MUTEX_lock(context->mutex);
+ str = context->error_string;
+ HEIMDAL_MUTEX_unlock(context->mutex);
+ return str != NULL;
+}
+
+/**
+ * Return the error message for `code' in context. On memory
+ * allocation error the function returns NULL.
+ *
+ * @param context Kerberos 5 context
+ * @param code Error code related to the error
+ *
+ * @return an error string, needs to be freed with
+ * krb5_free_error_message(). The functions return NULL on error.
+ *
+ * @ingroup krb5_error
+ */
+
+const char * KRB5_LIB_FUNCTION
+krb5_get_error_message(krb5_context context, krb5_error_code code)
+{
+ const char *cstr;
+ char *str;
+
+ HEIMDAL_MUTEX_lock(context->mutex);
+ if (context->error_string &&
+ (code == context->error_code || context->error_code == 0))
+ {
+ str = strdup(context->error_string);
+ if (str) {
+ HEIMDAL_MUTEX_unlock(context->mutex);
+ return str;
+ }
+ }
+ HEIMDAL_MUTEX_unlock(context->mutex);
+
+ cstr = krb5_get_err_text(context, code);
+ if (cstr)
+ return strdup(cstr);
+
+ if (asprintf(&str, "<unknown error: %d>", (int)code) == -1)
+ return NULL;
+
+ return str;
+}
+
+
+/**
+ * Free the error message returned by krb5_get_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free, returned byg
+ * krb5_get_error_message().
+ *
+ * @ingroup krb5_error
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_error_message(krb5_context context, const char *msg)
+{
+ free(rk_UNCONST(msg));
+}
+
+#ifndef HEIMDAL_SMALLER
+
+/**
+ * Free the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_free_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free
+ *
+ * @ingroup krb5_deprecated
+ */
+
+void KRB5_LIB_FUNCTION __attribute__((deprecated))
+krb5_free_error_string(krb5_context context, char *str)
+{
+ krb5_free_error_message(context, str);
+}
+
+/**
+ * Set the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_set_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free
+ *
+ * @ingroup krb5_deprecated
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_error_string(krb5_context context, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3))) __attribute__((deprecated))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ krb5_vset_error_message (context, 0, fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+/**
+ * Set the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_set_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free
+ *
+ * @ingroup krb5_deprecated
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vset_error_string(krb5_context context, const char *fmt, va_list args)
+ __attribute__ ((format (printf, 2, 0))) __attribute__((deprecated))
+{
+ krb5_vset_error_message(context, 0, fmt, args);
+ return 0;
+}
+
+#endif /* !HEIMDAL_SMALLER */
diff --git a/source4/heimdal/lib/krb5/expand_hostname.c b/source4/heimdal/lib/krb5/expand_hostname.c
new file mode 100644
index 0000000000..4ada4b8110
--- /dev/null
+++ b/source4/heimdal/lib/krb5/expand_hostname.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+static krb5_error_code
+copy_hostname(krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname)
+{
+ *new_hostname = strdup (orig_hostname);
+ if (*new_hostname == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ strlwr (*new_hostname);
+ return 0;
+}
+
+/*
+ * Try to make `orig_hostname' into a more canonical one in the newly
+ * allocated space returned in `new_hostname'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_expand_hostname (krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname)
+{
+ struct addrinfo *ai, *a, hints;
+ int error;
+
+ if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0)
+ return copy_hostname (context, orig_hostname, new_hostname);
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+
+ error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
+ if (error)
+ return copy_hostname (context, orig_hostname, new_hostname);
+ for (a = ai; a != NULL; a = a->ai_next) {
+ if (a->ai_canonname != NULL) {
+ *new_hostname = strdup (a->ai_canonname);
+ freeaddrinfo (ai);
+ if (*new_hostname == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ "malloc: out of memory");
+ return ENOMEM;
+ } else {
+ return 0;
+ }
+ }
+ }
+ freeaddrinfo (ai);
+ return copy_hostname (context, orig_hostname, new_hostname);
+}
+
+/*
+ * handle the case of the hostname being unresolvable and thus identical
+ */
+
+static krb5_error_code
+vanilla_hostname (krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname,
+ char ***realms)
+{
+ krb5_error_code ret;
+
+ ret = copy_hostname (context, orig_hostname, new_hostname);
+ if (ret)
+ return ret;
+ strlwr (*new_hostname);
+
+ ret = krb5_get_host_realm (context, *new_hostname, realms);
+ if (ret) {
+ free (*new_hostname);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * expand `hostname' to a name we believe to be a hostname in newly
+ * allocated space in `host' and return realms in `realms'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_expand_hostname_realms (krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname,
+ char ***realms)
+{
+ struct addrinfo *ai, *a, hints;
+ int error;
+ krb5_error_code ret = 0;
+
+ if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0)
+ return vanilla_hostname (context, orig_hostname, new_hostname,
+ realms);
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+
+ error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
+ if (error)
+ return vanilla_hostname (context, orig_hostname, new_hostname,
+ realms);
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ if (a->ai_canonname != NULL) {
+ ret = copy_hostname (context, a->ai_canonname, new_hostname);
+ if (ret) {
+ freeaddrinfo (ai);
+ return ret;
+ }
+ strlwr (*new_hostname);
+ ret = krb5_get_host_realm (context, *new_hostname, realms);
+ if (ret == 0) {
+ freeaddrinfo (ai);
+ return 0;
+ }
+ free (*new_hostname);
+ }
+ }
+ freeaddrinfo(ai);
+ return vanilla_hostname (context, orig_hostname, new_hostname, realms);
+}
diff --git a/source4/heimdal/lib/krb5/fcache.c b/source4/heimdal/lib/krb5/fcache.c
new file mode 100644
index 0000000000..fc11893452
--- /dev/null
+++ b/source4/heimdal/lib/krb5/fcache.c
@@ -0,0 +1,947 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_fcache{
+ char *filename;
+ int version;
+}krb5_fcache;
+
+struct fcc_cursor {
+ int fd;
+ krb5_storage *sp;
+};
+
+#define KRB5_FCC_FVNO_1 1
+#define KRB5_FCC_FVNO_2 2
+#define KRB5_FCC_FVNO_3 3
+#define KRB5_FCC_FVNO_4 4
+
+#define FCC_TAG_DELTATIME 1
+
+#define FCACHE(X) ((krb5_fcache*)(X)->data.data)
+
+#define FILENAME(X) (FCACHE(X)->filename)
+
+#define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
+
+static const char*
+fcc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return FILENAME(id);
+}
+
+int
+_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
+ const char *filename)
+{
+ int ret;
+#ifdef HAVE_FCNTL
+ struct flock l;
+
+ l.l_start = 0;
+ l.l_len = 0;
+ l.l_type = exclusive ? F_WRLCK : F_RDLCK;
+ l.l_whence = SEEK_SET;
+ ret = fcntl(fd, F_SETLKW, &l);
+#else
+ ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH);
+#endif
+ if(ret < 0)
+ ret = errno;
+ if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */
+ ret = EAGAIN;
+
+ switch (ret) {
+ case 0:
+ break;
+ case EINVAL: /* filesystem doesn't support locking, let the user have it */
+ ret = 0;
+ break;
+ case EAGAIN:
+ krb5_set_error_message(context, ret, "timed out locking cache file %s",
+ filename);
+ break;
+ default:
+ krb5_set_error_message(context, ret, "error locking cache file %s: %s",
+ filename, strerror(ret));
+ break;
+ }
+ return ret;
+}
+
+int
+_krb5_xunlock(krb5_context context, int fd)
+{
+ int ret;
+#ifdef HAVE_FCNTL
+ struct flock l;
+ l.l_start = 0;
+ l.l_len = 0;
+ l.l_type = F_UNLCK;
+ l.l_whence = SEEK_SET;
+ ret = fcntl(fd, F_SETLKW, &l);
+#else
+ ret = flock(fd, LOCK_UN);
+#endif
+ if (ret < 0)
+ ret = errno;
+ switch (ret) {
+ case 0:
+ break;
+ case EINVAL: /* filesystem doesn't support locking, let the user have it */
+ ret = 0;
+ break;
+ default:
+ krb5_set_error_message(context, ret,
+ "Failed to unlock file: %s",
+ strerror(ret));
+ break;
+ }
+ return ret;
+}
+
+static krb5_error_code
+write_storage(krb5_context context, krb5_storage *sp, int fd)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ ssize_t sret;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ return ret;
+ }
+ sret = write(fd, data.data, data.length);
+ ret = (sret != data.length);
+ krb5_data_free(&data);
+ if (ret) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "Failed to write FILE credential data");
+ return ret;
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+fcc_lock(krb5_context context, krb5_ccache id,
+ int fd, krb5_boolean exclusive)
+{
+ return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
+}
+
+static krb5_error_code
+fcc_unlock(krb5_context context, int fd)
+{
+ return _krb5_xunlock(context, fd);
+}
+
+static krb5_error_code
+fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ krb5_fcache *f;
+ f = malloc(sizeof(*f));
+ if(f == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ f->filename = strdup(res);
+ if(f->filename == NULL){
+ free(f);
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ f->version = 0;
+ (*id)->data.data = f;
+ (*id)->data.length = sizeof(*f);
+ return 0;
+}
+
+/*
+ * Try to scrub the contents of `filename' safely.
+ */
+
+static int
+scrub_file (int fd)
+{
+ off_t pos;
+ char buf[128];
+
+ pos = lseek(fd, 0, SEEK_END);
+ if (pos < 0)
+ return errno;
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return errno;
+ memset(buf, 0, sizeof(buf));
+ while(pos > 0) {
+ ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
+
+ if (tmp < 0)
+ return errno;
+ pos -= tmp;
+ }
+ fsync (fd);
+ return 0;
+}
+
+/*
+ * Erase `filename' if it exists, trying to remove the contents if
+ * it's `safe'. We always try to remove the file, it it exists. It's
+ * only overwritten if it's a regular file (not a symlink and not a
+ * hardlink)
+ */
+
+static krb5_error_code
+erase_file(krb5_context context, const char *filename)
+{
+ int fd;
+ struct stat sb1, sb2;
+ int ret;
+
+ ret = lstat (filename, &sb1);
+ if (ret < 0)
+ return errno;
+
+ fd = open(filename, O_RDWR | O_BINARY);
+ if(fd < 0) {
+ if(errno == ENOENT)
+ return 0;
+ else
+ return errno;
+ }
+ rk_cloexec(fd);
+ ret = _krb5_xlock(context, fd, 1, filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ if (unlink(filename) < 0) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return errno;
+ }
+ ret = fstat (fd, &sb2);
+ if (ret < 0) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return errno;
+ }
+
+ /* check if someone was playing with symlinks */
+
+ if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return EPERM;
+ }
+
+ /* there are still hard links to this file */
+
+ if (sb2.st_nlink != 0) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return 0;
+ }
+
+ ret = scrub_file (fd);
+ if (ret) {
+ _krb5_xunlock(context, fd);
+ close(fd);
+ return ret;
+ }
+ ret = _krb5_xunlock(context, fd);
+ close (fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_fcache *f;
+ int fd;
+ char *file;
+
+ f = malloc(sizeof(*f));
+ if(f == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
+ if(file == NULL) {
+ free(f);
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ fd = mkstemp(file);
+ if(fd < 0) {
+ int ret = errno;
+ krb5_set_error_message(context, ret, "mkstemp %s", file);
+ free(f);
+ free(file);
+ return ret;
+ }
+ close(fd);
+ f->filename = file;
+ f->version = 0;
+ (*id)->data.data = f;
+ (*id)->data.length = sizeof(*f);
+ return 0;
+}
+
+static void
+storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
+{
+ int flags = 0;
+ switch(vno) {
+ case KRB5_FCC_FVNO_1:
+ flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
+ flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
+ flags |= KRB5_STORAGE_HOST_BYTEORDER;
+ break;
+ case KRB5_FCC_FVNO_2:
+ flags |= KRB5_STORAGE_HOST_BYTEORDER;
+ break;
+ case KRB5_FCC_FVNO_3:
+ flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
+ break;
+ case KRB5_FCC_FVNO_4:
+ break;
+ default:
+ krb5_abortx(context,
+ "storage_set_flags called with bad vno (%x)", vno);
+ }
+ krb5_storage_set_flags(sp, flags);
+}
+
+static krb5_error_code
+fcc_open(krb5_context context,
+ krb5_ccache id,
+ int *fd_ret,
+ int flags,
+ mode_t mode)
+{
+ krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
+ (flags | O_RDWR) == flags);
+ krb5_error_code ret;
+ const char *filename = FILENAME(id);
+ int fd;
+ fd = open(filename, flags, mode);
+ if(fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", filename,
+ strerror(ret));
+ return ret;
+ }
+ rk_cloexec(fd);
+
+ if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
+ close(fd);
+ return ret;
+ }
+ *fd_ret = fd;
+ return 0;
+}
+
+static krb5_error_code
+fcc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_fcache *f = FCACHE(id);
+ int ret = 0;
+ int fd;
+ char *filename = f->filename;
+
+ unlink (filename);
+
+ ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ if(ret)
+ return ret;
+ {
+ krb5_storage *sp;
+ sp = krb5_storage_emem();
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ if(context->fcache_vno != 0)
+ f->version = context->fcache_vno;
+ else
+ f->version = KRB5_FCC_FVNO_4;
+ ret |= krb5_store_int8(sp, 5);
+ ret |= krb5_store_int8(sp, f->version);
+ storage_set_flags(context, sp, f->version);
+ if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
+ /* V4 stuff */
+ if (context->kdc_sec_offset) {
+ ret |= krb5_store_int16 (sp, 12); /* length */
+ ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
+ ret |= krb5_store_int16 (sp, 8); /* length of data */
+ ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
+ ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
+ } else {
+ ret |= krb5_store_int16 (sp, 0);
+ }
+ }
+ ret |= krb5_store_principal(sp, primary_principal);
+
+ ret |= write_storage(context, sp, fd);
+
+ krb5_storage_free(sp);
+ }
+ fcc_unlock(context, fd);
+ if (close(fd) < 0)
+ if (ret == 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "close %s: %s",
+ FILENAME(id), strerror(ret));
+ }
+ return ret;
+}
+
+static krb5_error_code
+fcc_close(krb5_context context,
+ krb5_ccache id)
+{
+ free (FILENAME(id));
+ krb5_data_free(&id->data);
+ return 0;
+}
+
+static krb5_error_code
+fcc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ erase_file(context, FILENAME(id));
+ return 0;
+}
+
+static krb5_error_code
+fcc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ int ret;
+ int fd;
+
+ ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0);
+ if(ret)
+ return ret;
+ {
+ krb5_storage *sp;
+
+ sp = krb5_storage_emem();
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ storage_set_flags(context, sp, FCACHE(id)->version);
+ if (!krb5_config_get_bool_default(context, NULL, TRUE,
+ "libdefaults",
+ "fcc-mit-ticketflags",
+ NULL))
+ krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
+ ret = krb5_store_creds(sp, creds);
+ if (ret == 0)
+ ret = write_storage(context, sp, fd);
+ krb5_storage_free(sp);
+ }
+ fcc_unlock(context, fd);
+ if (close(fd) < 0) {
+ if (ret == 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "close %s: %s",
+ FILENAME(id), strerror(ret));
+ }
+ }
+ return ret;
+}
+
+static krb5_error_code
+init_fcc (krb5_context context,
+ krb5_ccache id,
+ krb5_storage **ret_sp,
+ int *ret_fd)
+{
+ int fd;
+ int8_t pvno, tag;
+ krb5_storage *sp;
+ krb5_error_code ret;
+
+ ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ if(ret)
+ return ret;
+
+ sp = krb5_storage_from_fd(fd);
+ if(sp == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ ret = krb5_ret_int8(sp, &pvno);
+ if(ret != 0) {
+ if(ret == KRB5_CC_END) {
+ ret = ENOENT;
+ krb5_set_error_message(context, ret,
+ "Empty credential cache file: %s",
+ FILENAME(id));
+ } else
+ krb5_set_error_message(context, ret, "Error reading pvno in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ if(pvno != 5) {
+ ret = KRB5_CCACHE_BADVNO;
+ krb5_set_error_message(context, ret, "Bad version number in "
+ "credential cache file: %s",
+ FILENAME(id));
+ goto out;
+ }
+ ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
+ if(ret != 0) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret, "Error reading tag in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ FCACHE(id)->version = tag;
+ storage_set_flags(context, sp, FCACHE(id)->version);
+ switch (tag) {
+ case KRB5_FCC_FVNO_4: {
+ int16_t length;
+
+ ret = krb5_ret_int16 (sp, &length);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret,
+ "Error reading tag length in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ while(length > 0) {
+ int16_t dtag, data_len;
+ int i;
+ int8_t dummy;
+
+ ret = krb5_ret_int16 (sp, &dtag);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret, "Error reading dtag in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ ret = krb5_ret_int16 (sp, &data_len);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret, "Error reading dlength in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ switch (dtag) {
+ case FCC_TAG_DELTATIME :
+ ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret, "Error reading kdc_sec in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret, "Error reading kdc_usec in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ break;
+ default :
+ for (i = 0; i < data_len; ++i) {
+ ret = krb5_ret_int8 (sp, &dummy);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret,
+ "Error reading unknown "
+ "tag in cache file: %s",
+ FILENAME(id));
+ goto out;
+ }
+ }
+ break;
+ }
+ length -= 4 + data_len;
+ }
+ break;
+ }
+ case KRB5_FCC_FVNO_3:
+ case KRB5_FCC_FVNO_2:
+ case KRB5_FCC_FVNO_1:
+ break;
+ default :
+ ret = KRB5_CCACHE_BADVNO;
+ krb5_set_error_message(context, ret, "Unknown version number (%d) in "
+ "credential cache file: %s",
+ (int)tag, FILENAME(id));
+ goto out;
+ }
+ *ret_sp = sp;
+ *ret_fd = fd;
+
+ return 0;
+ out:
+ if(sp != NULL)
+ krb5_storage_free(sp);
+ fcc_unlock(context, fd);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ int fd;
+ krb5_storage *sp;
+
+ ret = init_fcc (context, id, &sp, &fd);
+ if (ret)
+ return ret;
+ ret = krb5_ret_principal(sp, principal);
+ if (ret)
+ krb5_clear_error_string(context);
+ krb5_storage_free(sp);
+ fcc_unlock(context, fd);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor);
+
+static krb5_error_code
+fcc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_error_code ret;
+ krb5_principal principal;
+
+ *cursor = malloc(sizeof(struct fcc_cursor));
+ if (*cursor == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memset(*cursor, 0, sizeof(struct fcc_cursor));
+
+ ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
+ &FCC_CURSOR(*cursor)->fd);
+ if (ret) {
+ free(*cursor);
+ *cursor = NULL;
+ return ret;
+ }
+ ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
+ if(ret) {
+ krb5_clear_error_string(context);
+ fcc_end_get(context, id, cursor);
+ return ret;
+ }
+ krb5_free_principal (context, principal);
+ fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
+ return 0;
+}
+
+static krb5_error_code
+fcc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
+ return ret;
+
+ ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
+ if (ret)
+ krb5_clear_error_string(context);
+
+ fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_storage_free(FCC_CURSOR(*cursor)->sp);
+ close (FCC_CURSOR(*cursor)->fd);
+ free(*cursor);
+ *cursor = NULL;
+ return 0;
+}
+
+static krb5_error_code
+fcc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ krb5_error_code ret;
+ krb5_ccache copy, newfile;
+
+ ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &copy);
+ if (ret)
+ return ret;
+
+ ret = krb5_cc_copy_cache(context, id, copy);
+ if (ret) {
+ krb5_cc_destroy(context, copy);
+ return ret;
+ }
+
+ ret = krb5_cc_remove_cred(context, copy, which, cred);
+ if (ret) {
+ krb5_cc_destroy(context, copy);
+ return ret;
+ }
+
+ ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &newfile);
+ if (ret) {
+ krb5_cc_destroy(context, copy);
+ return ret;
+ }
+
+ ret = krb5_cc_copy_cache(context, copy, newfile);
+ krb5_cc_destroy(context, copy);
+ if (ret) {
+ krb5_cc_destroy(context, newfile);
+ return ret;
+ }
+
+ return krb5_cc_move(context, newfile, id);
+}
+
+static krb5_error_code
+fcc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0; /* XXX */
+}
+
+static int
+fcc_get_version(krb5_context context,
+ krb5_ccache id)
+{
+ return FCACHE(id)->version;
+}
+
+struct fcache_iter {
+ int first;
+};
+
+static krb5_error_code
+fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct fcache_iter *iter;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ iter->first = 1;
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code
+fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct fcache_iter *iter = cursor;
+ krb5_error_code ret;
+ const char *fn;
+ char *expandedfn = NULL;
+
+ if (!iter->first) {
+ krb5_clear_error_string(context);
+ return KRB5_CC_END;
+ }
+ iter->first = 0;
+
+ fn = krb5_cc_default_name(context);
+ if (strncasecmp(fn, "FILE:", 5) != 0) {
+ ret = _krb5_expand_default_cc_name(context,
+ KRB5_DEFAULT_CCNAME_FILE,
+ &expandedfn);
+ if (ret)
+ return ret;
+ }
+ ret = krb5_cc_resolve(context, fn, id);
+ if (expandedfn)
+ free(expandedfn);
+
+ return ret;
+}
+
+static krb5_error_code
+fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct fcache_iter *iter = cursor;
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code
+fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_error_code ret = 0;
+
+ ret = rename(FILENAME(from), FILENAME(to));
+ if (ret && errno != EXDEV) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "Rename of file from %s to %s failed: %s",
+ FILENAME(from), FILENAME(to),
+ strerror(ret));
+ return ret;
+ } else if (ret && errno == EXDEV) {
+ /* make a copy and delete the orignal */
+ krb5_ssize_t sz1, sz2;
+ int fd1, fd2;
+ char buf[BUFSIZ];
+
+ ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ if(ret)
+ return ret;
+
+ unlink(FILENAME(to));
+
+ ret = fcc_open(context, to, &fd2,
+ O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ if(ret)
+ goto out1;
+
+ while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
+ sz2 = write(fd2, buf, sz1);
+ if (sz1 != sz2) {
+ ret = EIO;
+ krb5_set_error_message(context, ret,
+ "Failed to write data from one file "
+ "credential cache to the other");
+ goto out2;
+ }
+ }
+ if (sz1 < 0) {
+ ret = EIO;
+ krb5_set_error_message(context, ret,
+ "Failed to read data from one file "
+ "credential cache to the other");
+ goto out2;
+ }
+ out2:
+ fcc_unlock(context, fd2);
+ close(fd2);
+
+ out1:
+ fcc_unlock(context, fd1);
+ close(fd1);
+
+ erase_file(context, FILENAME(from));
+
+ if (ret) {
+ erase_file(context, FILENAME(to));
+ return ret;
+ }
+ }
+
+ /* make sure ->version is uptodate */
+ {
+ krb5_storage *sp;
+ int fd;
+ ret = init_fcc (context, to, &sp, &fd);
+ krb5_storage_free(sp);
+ fcc_unlock(context, fd);
+ close(fd);
+ }
+ return ret;
+}
+
+static krb5_error_code
+fcc_default_name(krb5_context context, char **str)
+{
+ return _krb5_expand_default_cc_name(context,
+ KRB5_DEFAULT_CCNAME_FILE,
+ str);
+}
+
+/**
+ * Variable containing the FILE based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "FILE",
+ fcc_get_name,
+ fcc_resolve,
+ fcc_gen_new,
+ fcc_initialize,
+ fcc_destroy,
+ fcc_close,
+ fcc_store_cred,
+ NULL, /* fcc_retrieve */
+ fcc_get_principal,
+ fcc_get_first,
+ fcc_get_next,
+ fcc_end_get,
+ fcc_remove_cred,
+ fcc_set_flags,
+ fcc_get_version,
+ fcc_get_cache_first,
+ fcc_get_cache_next,
+ fcc_end_cache_get,
+ fcc_move,
+ fcc_default_name
+};
diff --git a/source4/heimdal/lib/krb5/free.c b/source4/heimdal/lib/krb5/free.c
new file mode 100644
index 0000000000..d0eac84ca1
--- /dev/null
+++ b/source4/heimdal/lib/krb5/free.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1997 - 1999, 2004 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_kdc_rep(krb5_context context, krb5_kdc_rep *rep)
+{
+ free_KDC_REP(&rep->kdc_rep);
+ free_EncTGSRepPart(&rep->enc_part);
+ free_KRB_ERROR(&rep->error);
+ memset(rep, 0, sizeof(*rep));
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_xfree (void *ptr)
+{
+ free (ptr);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/free_host_realm.c b/source4/heimdal/lib/krb5/free_host_realm.c
new file mode 100644
index 0000000000..a9287de5fd
--- /dev/null
+++ b/source4/heimdal/lib/krb5/free_host_realm.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1997, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Free all memory allocated by `realmlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_host_realm(krb5_context context,
+ krb5_realm *realmlist)
+{
+ krb5_realm *p;
+
+ if(realmlist == NULL)
+ return 0;
+ for (p = realmlist; *p; ++p)
+ free (*p);
+ free (realmlist);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/generate_seq_number.c b/source4/heimdal/lib/krb5/generate_seq_number.c
new file mode 100644
index 0000000000..472fff7fd5
--- /dev/null
+++ b/source4/heimdal/lib/krb5/generate_seq_number.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_seq_number(krb5_context context,
+ const krb5_keyblock *key,
+ uint32_t *seqno)
+{
+ krb5_error_code ret;
+ krb5_keyblock *subkey;
+ uint32_t q;
+ u_char *p;
+ int i;
+
+ ret = krb5_generate_subkey (context, key, &subkey);
+ if (ret)
+ return ret;
+
+ q = 0;
+ for (p = (u_char *)subkey->keyvalue.data, i = 0;
+ i < subkey->keyvalue.length;
+ ++i, ++p)
+ q = (q << 8) | *p;
+ q &= 0xffffffff;
+ *seqno = q;
+ krb5_free_keyblock (context, subkey);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/generate_subkey.c b/source4/heimdal/lib/krb5/generate_subkey.c
new file mode 100644
index 0000000000..aa68d14df6
--- /dev/null
+++ b/source4/heimdal/lib/krb5/generate_subkey.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_subkey(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_keyblock **subkey)
+{
+ return krb5_generate_subkey_extended(context, key, key->keytype, subkey);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_subkey_extended(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_enctype etype,
+ krb5_keyblock **subkey)
+{
+ krb5_error_code ret;
+
+ ALLOC(*subkey, 1);
+ if (*subkey == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ if (etype == ETYPE_NULL)
+ etype = key->keytype; /* use session key etype */
+
+ /* XXX should we use the session key as input to the RF? */
+ ret = krb5_generate_random_keyblock(context, etype, *subkey);
+ if (ret != 0) {
+ free(*subkey);
+ *subkey = NULL;
+ }
+
+ return ret;
+}
+
diff --git a/source4/heimdal/lib/krb5/get_cred.c b/source4/heimdal/lib/krb5/get_cred.c
new file mode 100644
index 0000000000..c19a5e4abc
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_cred.c
@@ -0,0 +1,1423 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+/*
+ * Take the `body' and encode it into `padata' using the credentials
+ * in `creds'.
+ */
+
+static krb5_error_code
+make_pa_tgs_req(krb5_context context,
+ krb5_auth_context ac,
+ KDC_REQ_BODY *body,
+ PA_DATA *padata,
+ krb5_creds *creds)
+{
+ u_char *buf;
+ size_t buf_size;
+ size_t len;
+ krb5_data in_data;
+ krb5_error_code ret;
+
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
+ if (ret)
+ goto out;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ in_data.length = len;
+ in_data.data = buf;
+ ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
+ &padata->padata_value,
+ KRB5_KU_TGS_REQ_AUTH_CKSUM,
+ KRB5_KU_TGS_REQ_AUTH);
+ out:
+ free (buf);
+ if(ret)
+ return ret;
+ padata->padata_type = KRB5_PADATA_TGS_REQ;
+ return 0;
+}
+
+/*
+ * Set the `enc-authorization-data' in `req_body' based on `authdata'
+ */
+
+static krb5_error_code
+set_auth_data (krb5_context context,
+ KDC_REQ_BODY *req_body,
+ krb5_authdata *authdata,
+ krb5_keyblock *key)
+{
+ if(authdata->len) {
+ size_t len, buf_size;
+ unsigned char *buf;
+ krb5_crypto crypto;
+ krb5_error_code ret;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
+ &len, ret);
+ if (ret)
+ return ret;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ALLOC(req_body->enc_authorization_data, 1);
+ if (req_body->enc_authorization_data == NULL) {
+ free (buf);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free (buf);
+ free (req_body->enc_authorization_data);
+ req_body->enc_authorization_data = NULL;
+ return ret;
+ }
+ krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
+ /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
+ buf,
+ len,
+ 0,
+ req_body->enc_authorization_data);
+ free (buf);
+ krb5_crypto_destroy(context, crypto);
+ } else {
+ req_body->enc_authorization_data = NULL;
+ }
+ return 0;
+}
+
+/*
+ * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
+ * (if not-NULL), `in_creds', `krbtgt', and returning the generated
+ * subkey in `subkey'.
+ */
+
+static krb5_error_code
+init_tgs_req (krb5_context context,
+ krb5_ccache ccache,
+ krb5_addresses *addresses,
+ krb5_kdc_flags flags,
+ Ticket *second_ticket,
+ krb5_creds *in_creds,
+ krb5_creds *krbtgt,
+ unsigned nonce,
+ const METHOD_DATA *padata,
+ krb5_keyblock **subkey,
+ TGS_REQ *t)
+{
+ krb5_error_code ret = 0;
+
+ memset(t, 0, sizeof(*t));
+ t->pvno = 5;
+ t->msg_type = krb_tgs_req;
+ if (in_creds->session.keytype) {
+ ALLOC_SEQ(&t->req_body.etype, 1);
+ if(t->req_body.etype.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ t->req_body.etype.val[0] = in_creds->session.keytype;
+ } else {
+ ret = krb5_init_etype(context,
+ &t->req_body.etype.len,
+ &t->req_body.etype.val,
+ NULL);
+ }
+ if (ret)
+ goto fail;
+ t->req_body.addresses = addresses;
+ t->req_body.kdc_options = flags.b;
+ ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
+ if (ret)
+ goto fail;
+ ALLOC(t->req_body.sname, 1);
+ if (t->req_body.sname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+
+ /* some versions of some code might require that the client be
+ present in TGS-REQs, but this is clearly against the spec */
+
+ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
+ if (ret)
+ goto fail;
+
+ /* req_body.till should be NULL if there is no endtime specified,
+ but old MIT code (like DCE secd) doesn't like that */
+ ALLOC(t->req_body.till, 1);
+ if(t->req_body.till == NULL){
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ *t->req_body.till = in_creds->times.endtime;
+
+ t->req_body.nonce = nonce;
+ if(second_ticket){
+ ALLOC(t->req_body.additional_tickets, 1);
+ if (t->req_body.additional_tickets == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ ALLOC_SEQ(t->req_body.additional_tickets, 1);
+ if (t->req_body.additional_tickets->val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
+ if (ret)
+ goto fail;
+ }
+ ALLOC(t->padata, 1);
+ if (t->padata == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ ALLOC_SEQ(t->padata, 1 + padata->len);
+ if (t->padata->val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ {
+ int i;
+ for (i = 0; i < padata->len; i++) {
+ ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ }
+ }
+
+ {
+ krb5_auth_context ac;
+ krb5_keyblock *key = NULL;
+
+ ret = krb5_auth_con_init(context, &ac);
+ if(ret)
+ goto fail;
+
+ if (krb5_config_get_bool_default(context, NULL, FALSE,
+ "realms",
+ krbtgt->server->realm,
+ "tgs_require_subkey",
+ NULL))
+ {
+ ret = krb5_generate_subkey (context, &krbtgt->session, &key);
+ if (ret) {
+ krb5_auth_con_free (context, ac);
+ goto fail;
+ }
+
+ ret = krb5_auth_con_setlocalsubkey(context, ac, key);
+ if (ret) {
+ if (key)
+ krb5_free_keyblock (context, key);
+ krb5_auth_con_free (context, ac);
+ goto fail;
+ }
+ }
+
+ ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
+ key ? key : &krbtgt->session);
+ if (ret) {
+ if (key)
+ krb5_free_keyblock (context, key);
+ krb5_auth_con_free (context, ac);
+ goto fail;
+ }
+
+ ret = make_pa_tgs_req(context,
+ ac,
+ &t->req_body,
+ &t->padata->val[0],
+ krbtgt);
+ if(ret) {
+ if (key)
+ krb5_free_keyblock (context, key);
+ krb5_auth_con_free(context, ac);
+ goto fail;
+ }
+ *subkey = key;
+
+ krb5_auth_con_free(context, ac);
+ }
+fail:
+ if (ret) {
+ t->req_body.addresses = NULL;
+ free_TGS_REQ (t);
+ }
+ return ret;
+}
+
+krb5_error_code
+_krb5_get_krbtgt(krb5_context context,
+ krb5_ccache id,
+ krb5_realm realm,
+ krb5_creds **cred)
+{
+ krb5_error_code ret;
+ krb5_creds tmp_cred;
+
+ memset(&tmp_cred, 0, sizeof(tmp_cred));
+
+ ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
+ if (ret)
+ return ret;
+
+ ret = krb5_make_principal(context,
+ &tmp_cred.server,
+ realm,
+ KRB5_TGS_NAME,
+ realm,
+ NULL);
+ if(ret) {
+ krb5_free_principal(context, tmp_cred.client);
+ return ret;
+ }
+ ret = krb5_get_credentials(context,
+ KRB5_GC_CACHED,
+ id,
+ &tmp_cred,
+ cred);
+ krb5_free_principal(context, tmp_cred.client);
+ krb5_free_principal(context, tmp_cred.server);
+ if(ret)
+ return ret;
+ return 0;
+}
+
+/* DCE compatible decrypt proc */
+static krb5_error_code
+decrypt_tkt_with_subkey (krb5_context context,
+ krb5_keyblock *key,
+ krb5_key_usage usage,
+ krb5_const_pointer subkey,
+ krb5_kdc_rep *dec_rep)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage,
+ &dec_rep->kdc_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+ if(ret && subkey){
+ /* DCE compat -- try to decrypt with subkey */
+ ret = krb5_crypto_init(context, subkey, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
+ &dec_rep->kdc_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+ }
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_EncASRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ if (ret)
+ ret = krb5_decode_EncTGSRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ krb5_data_free (&data);
+ return ret;
+}
+
+static krb5_error_code
+get_cred_kdc(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_addresses *addresses,
+ krb5_creds *in_creds,
+ krb5_creds *krbtgt,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds *out_creds)
+{
+ TGS_REQ req;
+ krb5_data enc;
+ krb5_data resp;
+ krb5_kdc_rep rep;
+ KRB_ERROR error;
+ krb5_error_code ret;
+ unsigned nonce;
+ krb5_keyblock *subkey = NULL;
+ size_t len;
+ Ticket second_ticket_data;
+ METHOD_DATA padata;
+
+ krb5_data_zero(&resp);
+ krb5_data_zero(&enc);
+ padata.val = NULL;
+ padata.len = 0;
+
+ krb5_generate_random_block(&nonce, sizeof(nonce));
+ nonce &= 0xffffffff;
+
+ if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
+ ret = decode_Ticket(in_creds->second_ticket.data,
+ in_creds->second_ticket.length,
+ &second_ticket_data, &len);
+ if(ret)
+ return ret;
+ second_ticket = &second_ticket_data;
+ }
+
+
+ if (impersonate_principal) {
+ krb5_crypto crypto;
+ PA_S4U2Self self;
+ krb5_data data;
+ void *buf;
+ size_t size;
+
+ self.name = impersonate_principal->name;
+ self.realm = impersonate_principal->realm;
+ self.auth = estrdup("Kerberos");
+
+ ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
+ if (ret) {
+ free(self.auth);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
+ if (ret) {
+ free(self.auth);
+ krb5_data_free(&data);
+ goto out;
+ }
+
+ ret = krb5_create_checksum(context,
+ crypto,
+ KRB5_KU_OTHER_CKSUM,
+ 0,
+ data.data,
+ data.length,
+ &self.cksum);
+ krb5_crypto_destroy(context, crypto);
+ krb5_data_free(&data);
+ if (ret) {
+ free(self.auth);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
+ free(self.auth);
+ free_Checksum(&self.cksum);
+ if (ret)
+ goto out;
+ if (len != size)
+ krb5_abortx(context, "internal asn1 error");
+
+ ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len);
+ if (ret)
+ goto out;
+ }
+
+ ret = init_tgs_req (context,
+ id,
+ addresses,
+ flags,
+ second_ticket,
+ in_creds,
+ krbtgt,
+ nonce,
+ &padata,
+ &subkey,
+ &req);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
+ if (ret)
+ goto out;
+ if(enc.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ /* don't free addresses */
+ req.req_body.addresses = NULL;
+ free_TGS_REQ(&req);
+
+ /*
+ * Send and receive
+ */
+ {
+ krb5_sendto_ctx stctx;
+ ret = krb5_sendto_ctx_alloc(context, &stctx);
+ if (ret)
+ return ret;
+ krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
+
+ ret = krb5_sendto_context (context, stctx, &enc,
+ krbtgt->server->name.name_string.val[1],
+ &resp);
+ krb5_sendto_ctx_free(context, stctx);
+ }
+ if(ret)
+ goto out;
+
+ memset(&rep, 0, sizeof(rep));
+ if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
+ unsigned eflags = 0;
+
+ ret = krb5_copy_principal(context,
+ in_creds->client,
+ &out_creds->client);
+ if(ret)
+ goto out2;
+ ret = krb5_copy_principal(context,
+ in_creds->server,
+ &out_creds->server);
+ if(ret)
+ goto out2;
+ /* this should go someplace else */
+ out_creds->times.endtime = in_creds->times.endtime;
+
+ /* XXX should do better testing */
+ if (flags.b.constrained_delegation || impersonate_principal)
+ eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
+
+ ret = _krb5_extract_ticket(context,
+ &rep,
+ out_creds,
+ &krbtgt->session,
+ NULL,
+ KRB5_KU_TGS_REP_ENC_PART_SESSION,
+ &krbtgt->addresses,
+ nonce,
+ eflags,
+ decrypt_tkt_with_subkey,
+ subkey);
+ out2:
+ krb5_free_kdc_rep(context, &rep);
+ } else if(krb5_rd_error(context, &resp, &error) == 0) {
+ ret = krb5_error_from_rd_error(context, &error, in_creds);
+ krb5_free_error_contents(context, &error);
+ } else if(resp.data && ((char*)resp.data)[0] == 4) {
+ ret = KRB5KRB_AP_ERR_V4_REPLY;
+ krb5_clear_error_string(context);
+ } else {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_string(context);
+ }
+
+out:
+ if (second_ticket == &second_ticket_data)
+ free_Ticket(&second_ticket_data);
+ free_METHOD_DATA(&padata);
+ krb5_data_free(&resp);
+ krb5_data_free(&enc);
+ if(subkey){
+ krb5_free_keyblock_contents(context, subkey);
+ free(subkey);
+ }
+ return ret;
+
+}
+
+/*
+ * same as above, just get local addresses first if the krbtgt have
+ * them and the realm is not addressless
+ */
+
+static krb5_error_code
+get_cred_kdc_address(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_addresses *addrs,
+ krb5_creds *in_creds,
+ krb5_creds *krbtgt,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds *out_creds)
+{
+ krb5_error_code ret;
+ krb5_addresses addresses = { 0, NULL };
+
+ /*
+ * Inherit the address-ness of the krbtgt if the address is not
+ * specified.
+ */
+
+ if (addrs == NULL && krbtgt->addresses.len != 0) {
+ krb5_boolean noaddr;
+
+ krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
+ "no-addresses", FALSE, &noaddr);
+
+ if (!noaddr) {
+ krb5_get_all_client_addrs(context, &addresses);
+ /* XXX this sucks. */
+ addrs = &addresses;
+ if(addresses.len == 0)
+ addrs = NULL;
+ }
+ }
+ ret = get_cred_kdc(context, id, flags, addrs, in_creds,
+ krbtgt, impersonate_principal,
+ second_ticket, out_creds);
+ krb5_free_addresses(context, &addresses);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_kdc_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_addresses *addresses,
+ Ticket *second_ticket,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds
+ )
+{
+ krb5_error_code ret;
+ krb5_creds *krbtgt;
+
+ *out_creds = calloc(1, sizeof(**out_creds));
+ if(*out_creds == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = _krb5_get_krbtgt (context,
+ id,
+ in_creds->server->realm,
+ &krbtgt);
+ if(ret) {
+ free(*out_creds);
+ return ret;
+ }
+ ret = get_cred_kdc(context, id, flags, addresses,
+ in_creds, krbtgt, NULL, NULL, *out_creds);
+ krb5_free_creds (context, krbtgt);
+ if(ret)
+ free(*out_creds);
+ return ret;
+}
+
+static int
+not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
+{
+ krb5_error_code ret;
+ char *str;
+
+ ret = krb5_unparse_name(context, p, &str);
+ if(ret) {
+ krb5_clear_error_string(context);
+ return code;
+ }
+ krb5_set_error_message(context, code, "Matching credential (%s) not found", str);
+ free(str);
+ return code;
+}
+
+static krb5_error_code
+find_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_principal server,
+ krb5_creds **tgts,
+ krb5_creds *out_creds)
+{
+ krb5_error_code ret;
+ krb5_creds mcreds;
+
+ krb5_cc_clear_mcred(&mcreds);
+ mcreds.server = server;
+ ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
+ &mcreds, out_creds);
+ if(ret == 0)
+ return 0;
+ while(tgts && *tgts){
+ if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
+ &mcreds, *tgts)){
+ ret = krb5_copy_creds_contents(context, *tgts, out_creds);
+ return ret;
+ }
+ tgts++;
+ }
+ return not_found(context, server, KRB5_CC_NOTFOUND);
+}
+
+static krb5_error_code
+add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
+{
+ int i;
+ krb5_error_code ret;
+ krb5_creds **tmp = *tgts;
+
+ for(i = 0; tmp && tmp[i]; i++); /* XXX */
+ tmp = realloc(tmp, (i+2)*sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *tgts = tmp;
+ ret = krb5_copy_creds(context, tkt, &tmp[i]);
+ tmp[i+1] = NULL;
+ return ret;
+}
+
+/*
+get_cred(server)
+ creds = cc_get_cred(server)
+ if(creds) return creds
+ tgt = cc_get_cred(krbtgt/server_realm@any_realm)
+ if(tgt)
+ return get_cred_tgt(server, tgt)
+ if(client_realm == server_realm)
+ return NULL
+ tgt = get_cred(krbtgt/server_realm@client_realm)
+ while(tgt_inst != server_realm)
+ tgt = get_cred(krbtgt/server_realm@tgt_inst)
+ return get_cred_tgt(server, tgt)
+ */
+
+static krb5_error_code
+get_cred_kdc_capath(krb5_context context,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ krb5_error_code ret;
+ krb5_creds *tgt, tmp_creds;
+ krb5_const_realm client_realm, server_realm, try_realm;
+
+ *out_creds = NULL;
+
+ client_realm = krb5_principal_get_realm(context, in_creds->client);
+ server_realm = krb5_principal_get_realm(context, in_creds->server);
+ memset(&tmp_creds, 0, sizeof(tmp_creds));
+ ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
+ if(ret)
+ return ret;
+
+ try_realm = krb5_config_get_string(context, NULL, "capaths",
+ client_realm, server_realm, NULL);
+ if (try_realm == NULL)
+ try_realm = client_realm;
+
+ ret = krb5_make_principal(context,
+ &tmp_creds.server,
+ try_realm,
+ KRB5_TGS_NAME,
+ server_realm,
+ NULL);
+ if(ret){
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ {
+ krb5_creds tgts;
+
+ ret = find_cred(context, ccache, tmp_creds.server,
+ *ret_tgts, &tgts);
+ if(ret == 0){
+ *out_creds = calloc(1, sizeof(**out_creds));
+ if(*out_creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ } else {
+ ret = get_cred_kdc_address(context, ccache, flags, NULL,
+ in_creds, &tgts,
+ impersonate_principal,
+ second_ticket,
+ *out_creds);
+ if (ret) {
+ free (*out_creds);
+ *out_creds = NULL;
+ }
+ }
+ krb5_free_cred_contents(context, &tgts);
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ }
+ if(krb5_realm_compare(context, in_creds->client, in_creds->server))
+ return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
+
+ /* XXX this can loop forever */
+ while(1){
+ heim_general_string tgt_inst;
+
+ ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
+ NULL, NULL, &tgt, ret_tgts);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ ret = add_cred(context, tgt, ret_tgts);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ tgt_inst = tgt->server->name.name_string.val[1];
+ if(strcmp(tgt_inst, server_realm) == 0)
+ break;
+ krb5_free_principal(context, tmp_creds.server);
+ ret = krb5_make_principal(context, &tmp_creds.server,
+ tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ ret = krb5_free_creds(context, tgt);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ }
+
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ *out_creds = calloc(1, sizeof(**out_creds));
+ if(*out_creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ } else {
+ ret = get_cred_kdc_address (context, ccache, flags, NULL,
+ in_creds, tgt, impersonate_principal,
+ second_ticket, *out_creds);
+ if (ret) {
+ free (*out_creds);
+ *out_creds = NULL;
+ }
+ }
+ krb5_free_creds(context, tgt);
+ return ret;
+}
+
+static krb5_error_code
+get_cred_kdc_referral(krb5_context context,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ krb5_const_realm client_realm;
+ krb5_error_code ret;
+ krb5_creds tgt, referral, ticket;
+ int loop = 0;
+
+ memset(&tgt, 0, sizeof(tgt));
+ memset(&ticket, 0, sizeof(ticket));
+
+ flags.b.canonicalize = 1;
+
+ *out_creds = NULL;
+
+ client_realm = krb5_principal_get_realm(context, in_creds->client);
+
+ /* find tgt for the clients base realm */
+ {
+ krb5_principal tgtname;
+
+ ret = krb5_make_principal(context, &tgtname,
+ client_realm,
+ KRB5_TGS_NAME,
+ client_realm,
+ NULL);
+ if(ret)
+ return ret;
+
+ ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
+ krb5_free_principal(context, tgtname);
+ if (ret)
+ return ret;
+ }
+
+ referral = *in_creds;
+ ret = krb5_copy_principal(context, in_creds->server, &referral.server);
+ if (ret) {
+ krb5_free_cred_contents(context, &tgt);
+ return ret;
+ }
+ ret = krb5_principal_set_realm(context, referral.server, client_realm);
+ if (ret) {
+ krb5_free_cred_contents(context, &tgt);
+ krb5_free_principal(context, referral.server);
+ return ret;
+ }
+
+ while (loop++ < 17) {
+ krb5_creds **tickets;
+ krb5_creds mcreds;
+ char *referral_realm;
+
+ /* Use cache if we are not doing impersonation or contrainte deleg */
+ if (impersonate_principal == NULL || flags.b.constrained_delegation) {
+ krb5_cc_clear_mcred(&mcreds);
+ mcreds.server = referral.server;
+ ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
+ } else
+ ret = EINVAL;
+
+ if (ret) {
+ ret = get_cred_kdc_address (context, ccache, flags, NULL,
+ &referral, &tgt, impersonate_principal,
+ second_ticket, &ticket);
+ if (ret)
+ goto out;
+ }
+
+ /* Did we get the right ticket ? */
+ if (krb5_principal_compare_any_realm(context,
+ referral.server,
+ ticket.server))
+ break;
+
+ if (ticket.server->name.name_string.len != 2 &&
+ strcmp(ticket.server->name.name_string.val[0], KRB5_TGS_NAME) != 0)
+ {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
+ "Got back an non krbtgt ticket referrals");
+ krb5_free_cred_contents(context, &ticket);
+ return KRB5KRB_AP_ERR_NOT_US;
+ }
+
+ referral_realm = ticket.server->name.name_string.val[1];
+
+ /* check that there are no referrals loops */
+ tickets = *ret_tgts;
+
+ krb5_cc_clear_mcred(&mcreds);
+ mcreds.server = ticket.server;
+
+ while(tickets && *tickets){
+ if(krb5_compare_creds(context,
+ KRB5_TC_DONT_MATCH_REALM,
+ &mcreds,
+ *tickets))
+ {
+ krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
+ "Referral from %s loops back to realm %s",
+ tgt.server->realm,
+ referral_realm);
+ krb5_free_cred_contents(context, &ticket);
+ return KRB5_GET_IN_TKT_LOOP;
+ }
+ tickets++;
+ }
+
+ ret = add_cred(context, &ticket, ret_tgts);
+ if (ret) {
+ krb5_free_cred_contents(context, &ticket);
+ goto out;
+ }
+
+ /* try realm in the referral */
+ ret = krb5_principal_set_realm(context,
+ referral.server,
+ referral_realm);
+ krb5_free_cred_contents(context, &tgt);
+ tgt = ticket;
+ memset(&ticket, 0, sizeof(ticket));
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_copy_creds(context, &ticket, out_creds);
+
+out:
+ krb5_free_principal(context, referral.server);
+ krb5_free_cred_contents(context, &tgt);
+ return ret;
+}
+
+
+/*
+ * Glue function between referrals version and old client chasing
+ * codebase.
+ */
+
+static krb5_error_code
+get_cred_kdc_any(krb5_context context,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ krb5_error_code ret;
+
+ ret = get_cred_kdc_referral(context,
+ flags,
+ ccache,
+ in_creds,
+ impersonate_principal,
+ second_ticket,
+ out_creds,
+ ret_tgts);
+ if (ret == 0 || flags.b.canonicalize)
+ return ret;
+ return get_cred_kdc_capath(context,
+ flags,
+ ccache,
+ in_creds,
+ impersonate_principal,
+ second_ticket,
+ out_creds,
+ ret_tgts);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_cred_from_kdc_opt(krb5_context context,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts,
+ krb5_flags flags)
+{
+ krb5_kdc_flags f;
+ f.i = flags;
+ return get_cred_kdc_any(context, f, ccache,
+ in_creds, NULL, NULL,
+ out_creds, ret_tgts);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_cred_from_kdc(krb5_context context,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ return krb5_get_cred_from_kdc_opt(context, ccache,
+ in_creds, out_creds, ret_tgts, 0);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_credentials_with_flags(krb5_context context,
+ krb5_flags options,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds)
+{
+ krb5_error_code ret;
+ krb5_creds **tgts;
+ krb5_creds *res_creds;
+ int i;
+
+ *out_creds = NULL;
+ res_creds = calloc(1, sizeof(*res_creds));
+ if (res_creds == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ if (in_creds->session.keytype)
+ options |= KRB5_TC_MATCH_KEYTYPE;
+
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it.
+ */
+ ret = krb5_cc_retrieve_cred(context,
+ ccache,
+ in_creds->session.keytype ?
+ KRB5_TC_MATCH_KEYTYPE : 0,
+ in_creds, res_creds);
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
+ */
+ if (ret == 0) {
+ krb5_timestamp timeret;
+
+ /* If expired ok, don't bother checking */
+ if(options & KRB5_GC_EXPIRED_OK) {
+ *out_creds = res_creds;
+ return 0;
+ }
+
+ krb5_timeofday(context, &timeret);
+ if(res_creds->times.endtime > timeret) {
+ *out_creds = res_creds;
+ return 0;
+ }
+ if(options & KRB5_GC_CACHED)
+ krb5_cc_remove_cred(context, ccache, 0, res_creds);
+
+ } else if(ret != KRB5_CC_END) {
+ free(res_creds);
+ return ret;
+ }
+ free(res_creds);
+ if(options & KRB5_GC_CACHED)
+ return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
+
+ if(options & KRB5_GC_USER_USER)
+ flags.b.enc_tkt_in_skey = 1;
+ if (flags.b.enc_tkt_in_skey)
+ options |= KRB5_GC_NO_STORE;
+
+ tgts = NULL;
+ ret = get_cred_kdc_any(context, flags, ccache,
+ in_creds, NULL, NULL, out_creds, &tgts);
+ for(i = 0; tgts && tgts[i]; i++) {
+ krb5_cc_store_cred(context, ccache, tgts[i]);
+ krb5_free_creds(context, tgts[i]);
+ }
+ free(tgts);
+ if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
+ krb5_cc_store_cred(context, ccache, *out_creds);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_credentials(krb5_context context,
+ krb5_flags options,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds)
+{
+ krb5_kdc_flags flags;
+ flags.i = 0;
+ return krb5_get_credentials_with_flags(context, options, flags,
+ ccache, in_creds, out_creds);
+}
+
+struct krb5_get_creds_opt_data {
+ krb5_principal self;
+ krb5_flags options;
+ krb5_enctype enctype;
+ Ticket *ticket;
+};
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
+{
+ *opt = calloc(1, sizeof(**opt));
+ if (*opt == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
+{
+ if (opt->self)
+ krb5_free_principal(context, opt->self);
+ memset(opt, 0, sizeof(*opt));
+ free(opt);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_options(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_flags options)
+{
+ opt->options = options;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_add_options(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_flags options)
+{
+ opt->options |= options;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_enctype(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_enctype enctype)
+{
+ opt->enctype = enctype;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_impersonate(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_const_principal self)
+{
+ if (opt->self)
+ krb5_free_principal(context, opt->self);
+ return krb5_copy_principal(context, self, &opt->self);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_ticket(krb5_context context,
+ krb5_get_creds_opt opt,
+ const Ticket *ticket)
+{
+ if (opt->ticket) {
+ free_Ticket(opt->ticket);
+ free(opt->ticket);
+ opt->ticket = NULL;
+ }
+ if (ticket) {
+ krb5_error_code ret;
+
+ opt->ticket = malloc(sizeof(*ticket));
+ if (opt->ticket == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = copy_Ticket(ticket, opt->ticket);
+ if (ret) {
+ free(opt->ticket);
+ opt->ticket = NULL;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_ccache ccache,
+ krb5_const_principal inprinc,
+ krb5_creds **out_creds)
+{
+ krb5_kdc_flags flags;
+ krb5_flags options;
+ krb5_creds in_creds;
+ krb5_error_code ret;
+ krb5_creds **tgts;
+ krb5_creds *res_creds;
+ int i;
+
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.server = rk_UNCONST(inprinc);
+
+ ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
+ if (ret)
+ return ret;
+
+ options = opt->options;
+ flags.i = 0;
+
+ *out_creds = NULL;
+ res_creds = calloc(1, sizeof(*res_creds));
+ if (res_creds == NULL) {
+ krb5_free_principal(context, in_creds.client);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ if (opt->enctype) {
+ in_creds.session.keytype = opt->enctype;
+ options |= KRB5_TC_MATCH_KEYTYPE;
+ }
+
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it.
+ */
+ ret = krb5_cc_retrieve_cred(context,
+ ccache,
+ opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0,
+ &in_creds, res_creds);
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
+ */
+ if (ret == 0) {
+ krb5_timestamp timeret;
+
+ /* If expired ok, don't bother checking */
+ if(options & KRB5_GC_EXPIRED_OK) {
+ *out_creds = res_creds;
+ krb5_free_principal(context, in_creds.client);
+ return 0;
+ }
+
+ krb5_timeofday(context, &timeret);
+ if(res_creds->times.endtime > timeret) {
+ *out_creds = res_creds;
+ krb5_free_principal(context, in_creds.client);
+ return 0;
+ }
+ if(options & KRB5_GC_CACHED)
+ krb5_cc_remove_cred(context, ccache, 0, res_creds);
+
+ } else if(ret != KRB5_CC_END) {
+ free(res_creds);
+ krb5_free_principal(context, in_creds.client);
+ return ret;
+ }
+ free(res_creds);
+ if(options & KRB5_GC_CACHED) {
+ krb5_free_principal(context, in_creds.client);
+ return not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
+ }
+ if(options & KRB5_GC_USER_USER) {
+ flags.b.enc_tkt_in_skey = 1;
+ options |= KRB5_GC_NO_STORE;
+ }
+ if (options & KRB5_GC_FORWARDABLE)
+ flags.b.forwardable = 1;
+ if (options & KRB5_GC_NO_TRANSIT_CHECK)
+ flags.b.disable_transited_check = 1;
+ if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
+ flags.b.request_anonymous = 1; /* XXX ARGH confusion */
+ flags.b.constrained_delegation = 1;
+ }
+ if (options & KRB5_GC_CANONICALIZE)
+ flags.b.canonicalize = 1;
+
+ tgts = NULL;
+ ret = get_cred_kdc_any(context, flags, ccache,
+ &in_creds, opt->self, opt->ticket,
+ out_creds, &tgts);
+ krb5_free_principal(context, in_creds.client);
+ for(i = 0; tgts && tgts[i]; i++) {
+ krb5_cc_store_cred(context, ccache, tgts[i]);
+ krb5_free_creds(context, tgts[i]);
+ }
+ free(tgts);
+ if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
+ krb5_cc_store_cred(context, ccache, *out_creds);
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_renewed_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_const_principal client,
+ krb5_ccache ccache,
+ const char *in_tkt_service)
+{
+ krb5_error_code ret;
+ krb5_kdc_flags flags;
+ krb5_creds in, *template, *out = NULL;
+
+ memset(&in, 0, sizeof(in));
+ memset(creds, 0, sizeof(*creds));
+
+ ret = krb5_copy_principal(context, client, &in.client);
+ if (ret)
+ return ret;
+
+ if (in_tkt_service) {
+ ret = krb5_parse_name(context, in_tkt_service, &in.server);
+ if (ret) {
+ krb5_free_principal(context, in.client);
+ return ret;
+ }
+ } else {
+ const char *realm = krb5_principal_get_realm(context, client);
+
+ ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
+ realm, NULL);
+ if (ret) {
+ krb5_free_principal(context, in.client);
+ return ret;
+ }
+ }
+
+ flags.i = 0;
+ flags.b.renewable = flags.b.renew = 1;
+
+ /*
+ * Get template from old credential cache for the same entry, if
+ * this failes, no worries.
+ */
+ ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
+ if (ret == 0) {
+ flags.b.forwardable = template->flags.b.forwardable;
+ flags.b.proxiable = template->flags.b.proxiable;
+ krb5_free_creds (context, template);
+ }
+
+ ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
+ krb5_free_principal(context, in.client);
+ krb5_free_principal(context, in.server);
+ if (ret)
+ return ret;
+
+ ret = krb5_copy_creds_contents(context, out, creds);
+ krb5_free_creds(context, out);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_default_principal.c b/source4/heimdal/lib/krb5/get_default_principal.c
new file mode 100644
index 0000000000..6a56218ed7
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_default_principal.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Try to find out what's a reasonable default principal.
+ */
+
+static const char*
+get_env_user(void)
+{
+ const char *user = getenv("USER");
+ if(user == NULL)
+ user = getenv("LOGNAME");
+ if(user == NULL)
+ user = getenv("USERNAME");
+ return user;
+}
+
+/*
+ * Will only use operating-system dependant operation to get the
+ * default principal, for use of functions that in ccache layer to
+ * avoid recursive calls.
+ */
+
+krb5_error_code
+_krb5_get_default_principal_local (krb5_context context,
+ krb5_principal *princ)
+{
+ krb5_error_code ret;
+ const char *user;
+ uid_t uid;
+
+ *princ = NULL;
+
+ uid = getuid();
+ if(uid == 0) {
+ user = getlogin();
+ if(user == NULL)
+ user = get_env_user();
+ if(user != NULL && strcmp(user, "root") != 0)
+ ret = krb5_make_principal(context, princ, NULL, user, "root", NULL);
+ else
+ ret = krb5_make_principal(context, princ, NULL, "root", NULL);
+ } else {
+ struct passwd *pw = getpwuid(uid);
+ if(pw != NULL)
+ user = pw->pw_name;
+ else {
+ user = get_env_user();
+ if(user == NULL)
+ user = getlogin();
+ }
+ if(user == NULL) {
+ krb5_set_error_message(context, ENOTTY,
+ "unable to figure out current principal");
+ return ENOTTY; /* XXX */
+ }
+ ret = krb5_make_principal(context, princ, NULL, user, NULL);
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_principal (krb5_context context,
+ krb5_principal *princ)
+{
+ krb5_error_code ret;
+ krb5_ccache id;
+
+ *princ = NULL;
+
+ ret = krb5_cc_default (context, &id);
+ if (ret == 0) {
+ ret = krb5_cc_get_principal (context, id, princ);
+ krb5_cc_close (context, id);
+ if (ret == 0)
+ return 0;
+ }
+
+ return _krb5_get_default_principal_local(context, princ);
+}
diff --git a/source4/heimdal/lib/krb5/get_default_realm.c b/source4/heimdal/lib/krb5/get_default_realm.c
new file mode 100644
index 0000000000..8e8c1ef974
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_default_realm.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1997 - 2001, 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Return a NULL-terminated list of default realms in `realms'.
+ * Free this memory with krb5_free_host_realm.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_realms (krb5_context context,
+ krb5_realm **realms)
+{
+ if (context->default_realms == NULL) {
+ krb5_error_code ret = krb5_set_default_realm (context, NULL);
+ if (ret)
+ return KRB5_CONFIG_NODEFREALM;
+ }
+
+ return krb5_copy_host_realm (context,
+ context->default_realms,
+ realms);
+}
+
+/*
+ * Return the first default realm. For compatibility.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_realm(krb5_context context,
+ krb5_realm *realm)
+{
+ krb5_error_code ret;
+ char *res;
+
+ if (context->default_realms == NULL
+ || context->default_realms[0] == NULL) {
+ krb5_clear_error_string(context);
+ ret = krb5_set_default_realm (context, NULL);
+ if (ret)
+ return ret;
+ }
+
+ res = strdup (context->default_realms[0]);
+ if (res == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *realm = res;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/get_for_creds.c b/source4/heimdal/lib/krb5/get_for_creds.c
new file mode 100644
index 0000000000..f005460e3f
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_for_creds.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+add_addrs(krb5_context context,
+ krb5_addresses *addr,
+ struct addrinfo *ai)
+{
+ krb5_error_code ret;
+ unsigned n, i;
+ void *tmp;
+ struct addrinfo *a;
+
+ n = 0;
+ for (a = ai; a != NULL; a = a->ai_next)
+ ++n;
+
+ tmp = realloc(addr->val, (addr->len + n) * sizeof(*addr->val));
+ if (tmp == NULL && (addr->len + n) != 0) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ addr->val = tmp;
+ for (i = addr->len; i < (addr->len + n); ++i) {
+ addr->val[i].addr_type = 0;
+ krb5_data_zero(&addr->val[i].address);
+ }
+ i = addr->len;
+ for (a = ai; a != NULL; a = a->ai_next) {
+ krb5_address ad;
+
+ ret = krb5_sockaddr2address (context, a->ai_addr, &ad);
+ if (ret == 0) {
+ if (krb5_address_search(context, &ad, addr))
+ krb5_free_address(context, &ad);
+ else
+ addr->val[i++] = ad;
+ }
+ else if (ret == KRB5_PROG_ATYPE_NOSUPP)
+ krb5_clear_error_string (context);
+ else
+ goto fail;
+ addr->len = i;
+ }
+ return 0;
+fail:
+ krb5_free_addresses (context, addr);
+ return ret;
+}
+
+/**
+ * Forward credentials for client to host hostname , making them
+ * forwardable if forwardable, and returning the blob of data to sent
+ * in out_data. If hostname == NULL, pick it from server.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context the auth context with the key to encrypt the out_data.
+ * @param hostname the host to forward the tickets too.
+ * @param client the client to delegate from.
+ * @param server the server to delegate the credential too.
+ * @param ccache credential cache to use.
+ * @param forwardable make the forwarded ticket forwabledable.
+ * @param out_data the resulting credential.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_fwd_tgt_creds (krb5_context context,
+ krb5_auth_context auth_context,
+ const char *hostname,
+ krb5_principal client,
+ krb5_principal server,
+ krb5_ccache ccache,
+ int forwardable,
+ krb5_data *out_data)
+{
+ krb5_flags flags = 0;
+ krb5_creds creds;
+ krb5_error_code ret;
+ krb5_const_realm client_realm;
+
+ flags |= KDC_OPT_FORWARDED;
+
+ if (forwardable)
+ flags |= KDC_OPT_FORWARDABLE;
+
+ if (hostname == NULL &&
+ krb5_principal_get_type(context, server) == KRB5_NT_SRV_HST) {
+ const char *inst = krb5_principal_get_comp_string(context, server, 0);
+ const char *host = krb5_principal_get_comp_string(context, server, 1);
+
+ if (inst != NULL &&
+ strcmp(inst, "host") == 0 &&
+ host != NULL &&
+ krb5_principal_get_comp_string(context, server, 2) == NULL)
+ hostname = host;
+ }
+
+ client_realm = krb5_principal_get_realm(context, client);
+
+ memset (&creds, 0, sizeof(creds));
+ creds.client = client;
+
+ ret = krb5_build_principal(context,
+ &creds.server,
+ strlen(client_realm),
+ client_realm,
+ KRB5_TGS_NAME,
+ client_realm,
+ NULL);
+ if (ret)
+ return ret;
+
+ ret = krb5_get_forwarded_creds (context,
+ auth_context,
+ ccache,
+ flags,
+ hostname,
+ &creds,
+ out_data);
+ return ret;
+}
+
+/**
+ * Gets tickets forwarded to hostname. If the tickets that are
+ * forwarded are address-less, the forwarded tickets will also be
+ * address-less.
+ *
+ * If the ticket have any address, hostname will be used for figure
+ * out the address to forward the ticket too. This since this might
+ * use DNS, its insecure and also doesn't represent configured all
+ * addresses of the host. For example, the host might have two
+ * adresses, one IPv4 and one IPv6 address where the later is not
+ * published in DNS. This IPv6 address might be used communications
+ * and thus the resulting ticket useless.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context the auth context with the key to encrypt the out_data.
+ * @param ccache credential cache to use
+ * @param flags the flags to control the resulting ticket flags
+ * @param hostname the host to forward the tickets too.
+ * @param in_creds the in client and server ticket names. The client
+ * and server components forwarded to the remote host.
+ * @param out_data the resulting credential.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_forwarded_creds (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_ccache ccache,
+ krb5_flags flags,
+ const char *hostname,
+ krb5_creds *in_creds,
+ krb5_data *out_data)
+{
+ krb5_error_code ret;
+ krb5_creds *out_creds;
+ krb5_addresses addrs, *paddrs;
+ KRB_CRED cred;
+ KrbCredInfo *krb_cred_info;
+ EncKrbCredPart enc_krb_cred_part;
+ size_t len;
+ unsigned char *buf;
+ size_t buf_size;
+ krb5_kdc_flags kdc_flags;
+ krb5_crypto crypto;
+ struct addrinfo *ai;
+ krb5_creds *ticket;
+
+ paddrs = NULL;
+ addrs.len = 0;
+ addrs.val = NULL;
+
+ ret = krb5_get_credentials(context, 0, ccache, in_creds, &ticket);
+ if(ret == 0) {
+ if (ticket->addresses.len)
+ paddrs = &addrs;
+ krb5_free_creds (context, ticket);
+ } else {
+ krb5_boolean noaddr;
+ krb5_appdefault_boolean(context, NULL,
+ krb5_principal_get_realm(context,
+ in_creds->client),
+ "no-addresses", KRB5_ADDRESSLESS_DEFAULT,
+ &noaddr);
+ if (!noaddr)
+ paddrs = &addrs;
+ }
+
+ /*
+ * If tickets have addresses, get the address of the remote host.
+ */
+
+ if (paddrs != NULL) {
+
+ ret = getaddrinfo (hostname, NULL, NULL, &ai);
+ if (ret) {
+ krb5_error_code ret2 = krb5_eai_to_heim_errno(ret, errno);
+ krb5_set_error_message(context, ret2, "resolving %s: %s",
+ hostname, gai_strerror(ret));
+ return ret2;
+ }
+
+ ret = add_addrs (context, &addrs, ai);
+ freeaddrinfo (ai);
+ if (ret)
+ return ret;
+ }
+
+ kdc_flags.b = int2KDCOptions(flags);
+
+ ret = krb5_get_kdc_cred (context,
+ ccache,
+ kdc_flags,
+ paddrs,
+ NULL,
+ in_creds,
+ &out_creds);
+ krb5_free_addresses (context, &addrs);
+ if (ret)
+ return ret;
+
+ memset (&cred, 0, sizeof(cred));
+ cred.pvno = 5;
+ cred.msg_type = krb_cred;
+ ALLOC_SEQ(&cred.tickets, 1);
+ if (cred.tickets.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out2;
+ }
+ ret = decode_Ticket(out_creds->ticket.data,
+ out_creds->ticket.length,
+ cred.tickets.val, &len);
+ if (ret)
+ goto out3;
+
+ memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
+ ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1);
+ if (enc_krb_cred_part.ticket_info.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out4;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+ int32_t usec;
+
+ krb5_us_timeofday (context, &sec, &usec);
+
+ ALLOC(enc_krb_cred_part.timestamp, 1);
+ if (enc_krb_cred_part.timestamp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out4;
+ }
+ *enc_krb_cred_part.timestamp = sec;
+ ALLOC(enc_krb_cred_part.usec, 1);
+ if (enc_krb_cred_part.usec == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out4;
+ }
+ *enc_krb_cred_part.usec = usec;
+ } else {
+ enc_krb_cred_part.timestamp = NULL;
+ enc_krb_cred_part.usec = NULL;
+ }
+
+ if (auth_context->local_address && auth_context->local_port && paddrs) {
+
+ ret = krb5_make_addrport (context,
+ &enc_krb_cred_part.s_address,
+ auth_context->local_address,
+ auth_context->local_port);
+ if (ret)
+ goto out4;
+ }
+
+ if (auth_context->remote_address) {
+ if (auth_context->remote_port) {
+ krb5_boolean noaddr;
+ krb5_const_realm srealm;
+
+ srealm = krb5_principal_get_realm(context, out_creds->server);
+ /* Is this correct, and should we use the paddrs == NULL
+ trick here as well? Having an address-less ticket may
+ indicate that we don't know our own global address, but
+ it does not necessary mean that we don't know the
+ server's. */
+ krb5_appdefault_boolean(context, NULL, srealm, "no-addresses",
+ FALSE, &noaddr);
+ if (!noaddr) {
+ ret = krb5_make_addrport (context,
+ &enc_krb_cred_part.r_address,
+ auth_context->remote_address,
+ auth_context->remote_port);
+ if (ret)
+ goto out4;
+ }
+ } else {
+ ALLOC(enc_krb_cred_part.r_address, 1);
+ if (enc_krb_cred_part.r_address == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out4;
+ }
+
+ ret = krb5_copy_address (context, auth_context->remote_address,
+ enc_krb_cred_part.r_address);
+ if (ret)
+ goto out4;
+ }
+ }
+
+ /* fill ticket_info.val[0] */
+
+ enc_krb_cred_part.ticket_info.len = 1;
+
+ krb_cred_info = enc_krb_cred_part.ticket_info.val;
+
+ copy_EncryptionKey (&out_creds->session, &krb_cred_info->key);
+ ALLOC(krb_cred_info->prealm, 1);
+ copy_Realm (&out_creds->client->realm, krb_cred_info->prealm);
+ ALLOC(krb_cred_info->pname, 1);
+ copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname);
+ ALLOC(krb_cred_info->flags, 1);
+ *krb_cred_info->flags = out_creds->flags.b;
+ ALLOC(krb_cred_info->authtime, 1);
+ *krb_cred_info->authtime = out_creds->times.authtime;
+ ALLOC(krb_cred_info->starttime, 1);
+ *krb_cred_info->starttime = out_creds->times.starttime;
+ ALLOC(krb_cred_info->endtime, 1);
+ *krb_cred_info->endtime = out_creds->times.endtime;
+ ALLOC(krb_cred_info->renew_till, 1);
+ *krb_cred_info->renew_till = out_creds->times.renew_till;
+ ALLOC(krb_cred_info->srealm, 1);
+ copy_Realm (&out_creds->server->realm, krb_cred_info->srealm);
+ ALLOC(krb_cred_info->sname, 1);
+ copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname);
+ ALLOC(krb_cred_info->caddr, 1);
+ copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr);
+
+ krb5_free_creds (context, out_creds);
+
+ /* encode EncKrbCredPart */
+
+ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size,
+ &enc_krb_cred_part, &len, ret);
+ free_EncKrbCredPart (&enc_krb_cred_part);
+ if (ret) {
+ free_KRB_CRED(&cred);
+ return ret;
+ }
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ /**
+ * Some older of the MIT gssapi library used clear-text tickets
+ * (warped inside AP-REQ encryption), use the krb5_auth_context
+ * flag KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED to support those
+ * tickets. The session key is used otherwise to encrypt the
+ * forwarded ticket.
+ */
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) {
+ cred.enc_part.etype = ENCTYPE_NULL;
+ cred.enc_part.kvno = NULL;
+ cred.enc_part.cipher.data = buf;
+ cred.enc_part.cipher.length = buf_size;
+ } else {
+ /*
+ * Here older versions then 0.7.2 of Heimdal used the local or
+ * remote subkey. That is wrong, the session key should be
+ * used. Heimdal 0.7.2 and newer have code to try both in the
+ * receiving end.
+ */
+
+ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
+ if (ret) {
+ free(buf);
+ free_KRB_CRED(&cred);
+ return ret;
+ }
+ ret = krb5_encrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ buf,
+ len,
+ 0,
+ &cred.enc_part);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_KRB_CRED(&cred);
+ return ret;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret);
+ free_KRB_CRED (&cred);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ out_data->length = len;
+ out_data->data = buf;
+ return 0;
+ out4:
+ free_EncKrbCredPart(&enc_krb_cred_part);
+ out3:
+ free_KRB_CRED(&cred);
+ out2:
+ krb5_free_creds (context, out_creds);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_host_realm.c b/source4/heimdal/lib/krb5/get_host_realm.c
new file mode 100644
index 0000000000..e226598101
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_host_realm.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <resolve.h>
+
+RCSID("$Id$");
+
+/* To automagically find the correct realm of a host (without
+ * [domain_realm] in krb5.conf) add a text record for your domain with
+ * the name of your realm, like this:
+ *
+ * _kerberos IN TXT "FOO.SE"
+ *
+ * The search is recursive, so you can add entries for specific
+ * hosts. To find the realm of host a.b.c, it first tries
+ * _kerberos.a.b.c, then _kerberos.b.c and so on.
+ *
+ * This method is described in draft-ietf-cat-krb-dns-locate-03.txt.
+ *
+ */
+
+static int
+copy_txt_to_realms (struct resource_record *head,
+ krb5_realm **realms)
+{
+ struct resource_record *rr;
+ unsigned int n, i;
+
+ for(n = 0, rr = head; rr; rr = rr->next)
+ if (rr->type == T_TXT)
+ ++n;
+
+ if (n == 0)
+ return -1;
+
+ *realms = malloc ((n + 1) * sizeof(krb5_realm));
+ if (*realms == NULL)
+ return -1;
+
+ for (i = 0; i < n + 1; ++i)
+ (*realms)[i] = NULL;
+
+ for (i = 0, rr = head; rr; rr = rr->next) {
+ if (rr->type == T_TXT) {
+ char *tmp;
+
+ tmp = strdup(rr->u.txt);
+ if (tmp == NULL) {
+ for (i = 0; i < n; ++i)
+ free ((*realms)[i]);
+ free (*realms);
+ return -1;
+ }
+ (*realms)[i] = tmp;
+ ++i;
+ }
+ }
+ return 0;
+}
+
+static int
+dns_find_realm(krb5_context context,
+ const char *domain,
+ krb5_realm **realms)
+{
+ static const char *default_labels[] = { "_kerberos", NULL };
+ char dom[MAXHOSTNAMELEN];
+ struct dns_reply *r;
+ const char **labels;
+ char **config_labels;
+ int i, ret;
+
+ config_labels = krb5_config_get_strings(context, NULL, "libdefaults",
+ "dns_lookup_realm_labels", NULL);
+ if(config_labels != NULL)
+ labels = (const char **)config_labels;
+ else
+ labels = default_labels;
+ if(*domain == '.')
+ domain++;
+ for (i = 0; labels[i] != NULL; i++) {
+ ret = snprintf(dom, sizeof(dom), "%s.%s.", labels[i], domain);
+ if(ret < 0 || ret >= sizeof(dom)) {
+ if (config_labels)
+ krb5_config_free_strings(config_labels);
+ return -1;
+ }
+ r = dns_lookup(dom, "TXT");
+ if(r != NULL) {
+ ret = copy_txt_to_realms (r->head, realms);
+ dns_free_data(r);
+ if(ret == 0) {
+ if (config_labels)
+ krb5_config_free_strings(config_labels);
+ return 0;
+ }
+ }
+ }
+ if (config_labels)
+ krb5_config_free_strings(config_labels);
+ return -1;
+}
+
+/*
+ * Try to figure out what realms host in `domain' belong to from the
+ * configuration file.
+ */
+
+static int
+config_find_realm(krb5_context context,
+ const char *domain,
+ krb5_realm **realms)
+{
+ char **tmp = krb5_config_get_strings (context, NULL,
+ "domain_realm",
+ domain,
+ NULL);
+
+ if (tmp == NULL)
+ return -1;
+ *realms = tmp;
+ return 0;
+}
+
+/*
+ * This function assumes that `host' is a FQDN (and doesn't handle the
+ * special case of host == NULL either).
+ * Try to find mapping in the config file or DNS and it that fails,
+ * fall back to guessing
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_get_host_realm_int (krb5_context context,
+ const char *host,
+ krb5_boolean use_dns,
+ krb5_realm **realms)
+{
+ const char *p, *q;
+ krb5_boolean dns_locate_enable;
+
+ dns_locate_enable = krb5_config_get_bool_default(context, NULL, TRUE,
+ "libdefaults", "dns_lookup_realm", NULL);
+ for (p = host; p != NULL; p = strchr (p + 1, '.')) {
+ if(config_find_realm(context, p, realms) == 0) {
+ if(strcasecmp(*realms[0], "dns_locate") == 0) {
+ if(use_dns)
+ for (q = host; q != NULL; q = strchr(q + 1, '.'))
+ if(dns_find_realm(context, q, realms) == 0)
+ return 0;
+ continue;
+ } else
+ return 0;
+ }
+ else if(use_dns && dns_locate_enable) {
+ if(dns_find_realm(context, p, realms) == 0)
+ return 0;
+ }
+ }
+ p = strchr(host, '.');
+ if(p != NULL) {
+ p++;
+ *realms = malloc(2 * sizeof(krb5_realm));
+ if (*realms == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*realms)[0] = strdup(p);
+ if((*realms)[0] == NULL) {
+ free(*realms);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ strupr((*realms)[0]);
+ (*realms)[1] = NULL;
+ return 0;
+ }
+ krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
+ "unable to find realm of host %s", host);
+ return KRB5_ERR_HOST_REALM_UNKNOWN;
+}
+
+/*
+ * Return the realm(s) of `host' as a NULL-terminated list in
+ * `realms'. Free `realms' with krb5_free_host_realm().
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_host_realm(krb5_context context,
+ const char *targethost,
+ krb5_realm **realms)
+{
+ const char *host = targethost;
+ char hostname[MAXHOSTNAMELEN];
+ krb5_error_code ret;
+ int use_dns;
+
+ if (host == NULL) {
+ if (gethostname (hostname, sizeof(hostname))) {
+ *realms = NULL;
+ return errno;
+ }
+ host = hostname;
+ }
+
+ /*
+ * If our local hostname is without components, don't even try to dns.
+ */
+
+ use_dns = (strchr(host, '.') != NULL);
+
+ ret = _krb5_get_host_realm_int (context, host, use_dns, realms);
+ if (ret && targethost != NULL) {
+ /*
+ * If there was no realm mapping for the host (and we wasn't
+ * looking for ourself), guess at the local realm, maybe our
+ * KDC knows better then we do and we get a referral back.
+ */
+ ret = krb5_get_default_realms(context, realms);
+ if (ret) {
+ krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
+ "Unable to find realm of host %s",
+ host);
+ return KRB5_ERR_HOST_REALM_UNKNOWN;
+ }
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_in_tkt.c b/source4/heimdal/lib/krb5/get_in_tkt.c
new file mode 100644
index 0000000000..c835a9a29e
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_in_tkt.c
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_init_etype (krb5_context context,
+ unsigned *len,
+ krb5_enctype **val,
+ const krb5_enctype *etypes)
+{
+ unsigned int i;
+ krb5_error_code ret;
+ krb5_enctype *tmp = NULL;
+
+ ret = 0;
+ if (etypes == NULL) {
+ ret = krb5_get_default_in_tkt_etypes(context,
+ &tmp);
+ if (ret)
+ return ret;
+ etypes = tmp;
+ }
+
+ for (i = 0; etypes[i]; ++i)
+ ;
+ *len = i;
+ *val = malloc(i * sizeof(**val));
+ if (i != 0 && *val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto cleanup;
+ }
+ memmove (*val,
+ etypes,
+ i * sizeof(*tmp));
+cleanup:
+ if (tmp != NULL)
+ free (tmp);
+ return ret;
+}
+
+static krb5_error_code
+check_server_referral(krb5_context context,
+ krb5_kdc_rep *rep,
+ unsigned flags,
+ krb5_const_principal requested,
+ krb5_const_principal returned,
+ const krb5_keyblock const * key)
+{
+ krb5_error_code ret;
+ PA_ServerReferralData ref;
+ krb5_crypto session;
+ EncryptedData ed;
+ size_t len;
+ krb5_data data;
+ PA_DATA *pa;
+ int i = 0, cmp;
+
+ if (rep->kdc_rep.padata == NULL)
+ goto noreferral;
+
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_SERVER_REFERRAL, &i);
+ if (pa == NULL)
+ goto noreferral;
+
+ memset(&ed, 0, sizeof(ed));
+ memset(&ref, 0, sizeof(ref));
+
+ ret = decode_EncryptedData(pa->padata_value.data,
+ pa->padata_value.length,
+ &ed, &len);
+ if (ret)
+ return ret;
+ if (len != pa->padata_value.length) {
+ free_EncryptedData(&ed);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, "Referral EncryptedData wrong");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &session);
+ if (ret) {
+ free_EncryptedData(&ed);
+ return ret;
+ }
+
+ ret = krb5_decrypt_EncryptedData(context, session,
+ KRB5_KU_PA_SERVER_REFERRAL,
+ &ed, &data);
+ free_EncryptedData(&ed);
+ krb5_crypto_destroy(context, session);
+ if (ret)
+ return ret;
+
+ ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
+ if (ret) {
+ krb5_data_free(&data);
+ return ret;
+ }
+ krb5_data_free(&data);
+
+ if (strcmp(requested->realm, returned->realm) != 0) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "server ref realm mismatch");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (returned->name.name_string.len == 2 &&
+ strcmp(returned->name.name_string.val[0], KRB5_TGS_NAME) == 0)
+ {
+ const char *realm = returned->name.name_string.val[1];
+
+ if (ref.referred_realm == NULL
+ || strcmp(*ref.referred_realm, realm) != 0)
+ {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "tgt returned with wrong ref");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ } else if (krb5_principal_compare(context, returned, requested) == 0) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "req princ no same as returned");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (ref.requested_principal_name) {
+ cmp = _krb5_principal_compare_PrincipalName(context,
+ requested,
+ ref.requested_principal_name);
+ if (!cmp) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "compare requested failed");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ } else if (flags & EXTRACT_TICKET_AS_REQ) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "Requested principal missing on AS-REQ");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ free_PA_ServerReferralData(&ref);
+
+ return ret;
+noreferral:
+ if (krb5_principal_compare(context, requested, returned) == FALSE) {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "Not same server principal returned "
+ "as requested");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ return 0;
+}
+
+
+/*
+ * Verify referral data
+ */
+
+
+static krb5_error_code
+check_client_referral(krb5_context context,
+ krb5_kdc_rep *rep,
+ krb5_const_principal requested,
+ krb5_const_principal mapped,
+ krb5_keyblock const * key)
+{
+ krb5_error_code ret;
+ PA_ClientCanonicalized canon;
+ krb5_crypto crypto;
+ krb5_data data;
+ PA_DATA *pa;
+ size_t len;
+ int i = 0;
+
+ if (rep->kdc_rep.padata == NULL)
+ goto noreferral;
+
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_CLIENT_CANONICALIZED, &i);
+ if (pa == NULL)
+ goto noreferral;
+
+ ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
+ pa->padata_value.length,
+ &canon, &len);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode "
+ "PA_ClientCanonicalized");
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
+ &canon.names, &len, ret);
+ if (ret) {
+ free_PA_ClientCanonicalized(&canon);
+ return ret;
+ }
+ if (data.length != len)
+ krb5_abortx(context, "internal asn.1 error");
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ free_PA_ClientCanonicalized(&canon);
+ return ret;
+ }
+
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
+ data.data, data.length,
+ &canon.canon_checksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to verify "
+ "client canonicalized data");
+ free_PA_ClientCanonicalized(&canon);
+ return ret;
+ }
+
+ if (!_krb5_principal_compare_PrincipalName(context,
+ requested,
+ &canon.names.requested_name))
+ {
+ free_PA_ClientCanonicalized(&canon);
+ krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
+ "Requested name doesn't match"
+ " in client referral");
+ return KRB5_PRINC_NOMATCH;
+ }
+ if (!_krb5_principal_compare_PrincipalName(context,
+ mapped,
+ &canon.names.mapped_name))
+ {
+ free_PA_ClientCanonicalized(&canon);
+ krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
+ "Mapped name doesn't match"
+ " in client referral");
+ return KRB5_PRINC_NOMATCH;
+ }
+
+ return 0;
+
+noreferral:
+ if (krb5_principal_compare(context, requested, mapped) == FALSE) {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "Not same client principal returned "
+ "as requested");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ return 0;
+}
+
+
+
+static krb5_error_code
+decrypt_tkt (krb5_context context,
+ krb5_keyblock *key,
+ krb5_key_usage usage,
+ krb5_const_pointer decrypt_arg,
+ krb5_kdc_rep *dec_rep)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage,
+ &dec_rep->kdc_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_EncASRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ if (ret)
+ ret = krb5_decode_EncTGSRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ krb5_data_free (&data);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+int
+_krb5_extract_ticket(krb5_context context,
+ krb5_kdc_rep *rep,
+ krb5_creds *creds,
+ krb5_keyblock *key,
+ krb5_const_pointer keyseed,
+ krb5_key_usage key_usage,
+ krb5_addresses *addrs,
+ unsigned nonce,
+ unsigned flags,
+ krb5_decrypt_proc decrypt_proc,
+ krb5_const_pointer decryptarg)
+{
+ krb5_error_code ret;
+ krb5_principal tmp_principal;
+ size_t len;
+ time_t tmp_time;
+ krb5_timestamp sec_now;
+
+ /* decrypt */
+
+ if (decrypt_proc == NULL)
+ decrypt_proc = decrypt_tkt;
+
+ ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
+ if (ret)
+ goto out;
+
+ /* save session key */
+
+ creds->session.keyvalue.length = 0;
+ creds->session.keyvalue.data = NULL;
+ creds->session.keytype = rep->enc_part.key.keytype;
+ ret = krb5_data_copy (&creds->session.keyvalue,
+ rep->enc_part.key.keyvalue.data,
+ rep->enc_part.key.keyvalue.length);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ /*
+ * HACK:
+ * this is really a ugly hack, to support using the Netbios Domain Name
+ * as realm against windows KDC's, they always return the full realm
+ * based on the DNS Name.
+ */
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+ flags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
+
+ /* compare client and save */
+ ret = _krb5_principalname2krb5_principal (context,
+ &tmp_principal,
+ rep->kdc_rep.cname,
+ rep->kdc_rep.crealm);
+ if (ret)
+ goto out;
+
+ /* check client referral and save principal */
+ /* anonymous here ? */
+ if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
+ ret = check_client_referral(context, rep,
+ creds->client,
+ tmp_principal,
+ &creds->session);
+ if (ret) {
+ krb5_free_principal (context, tmp_principal);
+ goto out;
+ }
+ }
+ krb5_free_principal (context, creds->client);
+ creds->client = tmp_principal;
+
+ /* check server referral and save principal */
+ ret = _krb5_principalname2krb5_principal (context,
+ &tmp_principal,
+ rep->kdc_rep.ticket.sname,
+ rep->kdc_rep.ticket.realm);
+ if (ret)
+ goto out;
+ if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
+ ret = check_server_referral(context,
+ rep,
+ flags,
+ creds->server,
+ tmp_principal,
+ &creds->session);
+ if (ret) {
+ krb5_free_principal (context, tmp_principal);
+ goto out;
+ }
+ }
+ krb5_free_principal(context, creds->server);
+ creds->server = tmp_principal;
+
+ /* verify names */
+ if(flags & EXTRACT_TICKET_MATCH_REALM){
+ const char *srealm = krb5_principal_get_realm(context, creds->server);
+ const char *crealm = krb5_principal_get_realm(context, creds->client);
+
+ if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
+ strcmp(rep->enc_part.srealm, crealm) != 0)
+ {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ /* compare nonces */
+
+ if (nonce != rep->enc_part.nonce) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ /* set kdc-offset */
+
+ krb5_timeofday (context, &sec_now);
+ if (rep->enc_part.flags.initial
+ && context->kdc_sec_offset == 0
+ && krb5_config_get_bool (context, NULL,
+ "libdefaults",
+ "kdc_timesync",
+ NULL)) {
+ context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
+ krb5_timeofday (context, &sec_now);
+ }
+
+ /* check all times */
+
+ if (rep->enc_part.starttime) {
+ tmp_time = *rep->enc_part.starttime;
+ } else
+ tmp_time = rep->enc_part.authtime;
+
+ if (creds->times.starttime == 0
+ && abs(tmp_time - sec_now) > context->max_skew) {
+ ret = KRB5KRB_AP_ERR_SKEW;
+ krb5_set_error_message (context, ret,
+ "time skew (%d) larger than max (%d)",
+ abs(tmp_time - sec_now),
+ (int)context->max_skew);
+ goto out;
+ }
+
+ if (creds->times.starttime != 0
+ && tmp_time != creds->times.starttime) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.starttime = tmp_time;
+
+ if (rep->enc_part.renew_till) {
+ tmp_time = *rep->enc_part.renew_till;
+ } else
+ tmp_time = 0;
+
+ if (creds->times.renew_till != 0
+ && tmp_time > creds->times.renew_till) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.renew_till = tmp_time;
+
+ creds->times.authtime = rep->enc_part.authtime;
+
+ if (creds->times.endtime != 0
+ && rep->enc_part.endtime > creds->times.endtime) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.endtime = rep->enc_part.endtime;
+
+ if(rep->enc_part.caddr)
+ krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
+ else if(addrs)
+ krb5_copy_addresses (context, addrs, &creds->addresses);
+ else {
+ creds->addresses.len = 0;
+ creds->addresses.val = NULL;
+ }
+ creds->flags.b = rep->enc_part.flags;
+
+ creds->authdata.len = 0;
+ creds->authdata.val = NULL;
+
+ /* extract ticket */
+ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
+ &rep->kdc_rep.ticket, &len, ret);
+ if(ret)
+ goto out;
+ if (creds->ticket.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ creds->second_ticket.length = 0;
+ creds->second_ticket.data = NULL;
+
+
+out:
+ memset (rep->enc_part.key.keyvalue.data, 0,
+ rep->enc_part.key.keyvalue.length);
+ return ret;
+}
+
+
+static krb5_error_code
+make_pa_enc_timestamp(krb5_context context, PA_DATA *pa,
+ krb5_enctype etype, krb5_keyblock *key)
+{
+ PA_ENC_TS_ENC p;
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ EncryptedData encdata;
+ krb5_error_code ret;
+ int32_t usec;
+ int usec2;
+ krb5_crypto crypto;
+
+ krb5_us_timeofday (context, &p.patimestamp, &usec);
+ usec2 = usec;
+ p.pausec = &usec2;
+
+ ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_PA_ENC_TIMESTAMP,
+ buf,
+ len,
+ 0,
+ &encdata);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
+ free_EncryptedData(&encdata);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP;
+ pa->padata_value.length = len;
+ pa->padata_value.data = buf;
+ return 0;
+}
+
+static krb5_error_code
+add_padata(krb5_context context,
+ METHOD_DATA *md,
+ krb5_principal client,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_enctype *enctypes,
+ unsigned netypes,
+ krb5_salt *salt)
+{
+ krb5_error_code ret;
+ PA_DATA *pa2;
+ krb5_salt salt2;
+ krb5_enctype *ep;
+ int i;
+
+ if(salt == NULL) {
+ /* default to standard salt */
+ ret = krb5_get_pw_salt (context, client, &salt2);
+ salt = &salt2;
+ }
+ if (!enctypes) {
+ enctypes = context->etypes;
+ netypes = 0;
+ for (ep = enctypes; *ep != ETYPE_NULL; ep++)
+ netypes++;
+ }
+ pa2 = realloc (md->val, (md->len + netypes) * sizeof(*md->val));
+ if (pa2 == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ md->val = pa2;
+
+ for (i = 0; i < netypes; ++i) {
+ krb5_keyblock *key;
+
+ ret = (*key_proc)(context, enctypes[i], *salt, keyseed, &key);
+ if (ret)
+ continue;
+ ret = make_pa_enc_timestamp (context, &md->val[md->len],
+ enctypes[i], key);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ return ret;
+ ++md->len;
+ }
+ if(salt == &salt2)
+ krb5_free_salt(context, salt2);
+ return 0;
+}
+
+static krb5_error_code
+init_as_req (krb5_context context,
+ KDCOptions opts,
+ krb5_creds *creds,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *ptypes,
+ const krb5_preauthdata *preauth,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ unsigned nonce,
+ AS_REQ *a)
+{
+ krb5_error_code ret;
+ krb5_salt salt;
+
+ memset(a, 0, sizeof(*a));
+
+ a->pvno = 5;
+ a->msg_type = krb_as_req;
+ a->req_body.kdc_options = opts;
+ a->req_body.cname = malloc(sizeof(*a->req_body.cname));
+ if (a->req_body.cname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ a->req_body.sname = malloc(sizeof(*a->req_body.sname));
+ if (a->req_body.sname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
+ if (ret)
+ goto fail;
+ ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
+ if (ret)
+ goto fail;
+ ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
+ if (ret)
+ goto fail;
+
+ if(creds->times.starttime) {
+ a->req_body.from = malloc(sizeof(*a->req_body.from));
+ if (a->req_body.from == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ *a->req_body.from = creds->times.starttime;
+ }
+ if(creds->times.endtime){
+ ALLOC(a->req_body.till, 1);
+ *a->req_body.till = creds->times.endtime;
+ }
+ if(creds->times.renew_till){
+ a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
+ if (a->req_body.rtime == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ *a->req_body.rtime = creds->times.renew_till;
+ }
+ a->req_body.nonce = nonce;
+ ret = krb5_init_etype (context,
+ &a->req_body.etype.len,
+ &a->req_body.etype.val,
+ etypes);
+ if (ret)
+ goto fail;
+
+ /*
+ * This means no addresses
+ */
+
+ if (addrs && addrs->len == 0) {
+ a->req_body.addresses = NULL;
+ } else {
+ a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
+ if (a->req_body.addresses == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+
+ if (addrs)
+ ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
+ else {
+ ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
+ if(ret == 0 && a->req_body.addresses->len == 0) {
+ free(a->req_body.addresses);
+ a->req_body.addresses = NULL;
+ }
+ }
+ if (ret)
+ return ret;
+ }
+
+ a->req_body.enc_authorization_data = NULL;
+ a->req_body.additional_tickets = NULL;
+
+ if(preauth != NULL) {
+ int i;
+ ALLOC(a->padata, 1);
+ if(a->padata == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ a->padata->val = NULL;
+ a->padata->len = 0;
+ for(i = 0; i < preauth->len; i++) {
+ if(preauth->val[i].type == KRB5_PADATA_ENC_TIMESTAMP){
+ int j;
+
+ for(j = 0; j < preauth->val[i].info.len; j++) {
+ krb5_salt *sp = &salt;
+ if(preauth->val[i].info.val[j].salttype)
+ salt.salttype = *preauth->val[i].info.val[j].salttype;
+ else
+ salt.salttype = KRB5_PW_SALT;
+ if(preauth->val[i].info.val[j].salt)
+ salt.saltvalue = *preauth->val[i].info.val[j].salt;
+ else
+ if(salt.salttype == KRB5_PW_SALT)
+ sp = NULL;
+ else
+ krb5_data_zero(&salt.saltvalue);
+ ret = add_padata(context, a->padata, creds->client,
+ key_proc, keyseed,
+ &preauth->val[i].info.val[j].etype, 1,
+ sp);
+ if (ret == 0)
+ break;
+ }
+ }
+ }
+ } else
+ /* not sure this is the way to use `ptypes' */
+ if (ptypes == NULL || *ptypes == KRB5_PADATA_NONE)
+ a->padata = NULL;
+ else if (*ptypes == KRB5_PADATA_ENC_TIMESTAMP) {
+ ALLOC(a->padata, 1);
+ if (a->padata == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ a->padata->len = 0;
+ a->padata->val = NULL;
+
+ /* make a v5 salted pa-data */
+ add_padata(context, a->padata, creds->client,
+ key_proc, keyseed, a->req_body.etype.val,
+ a->req_body.etype.len, NULL);
+
+ /* make a v4 salted pa-data */
+ salt.salttype = KRB5_PW_SALT;
+ krb5_data_zero(&salt.saltvalue);
+ add_padata(context, a->padata, creds->client,
+ key_proc, keyseed, a->req_body.etype.val,
+ a->req_body.etype.len, &salt);
+ } else {
+ ret = KRB5_PREAUTH_BAD_TYPE;
+ krb5_set_error_message (context, ret, "pre-auth type %d not supported",
+ *ptypes);
+ goto fail;
+ }
+ return 0;
+fail:
+ free_AS_REQ(a);
+ return ret;
+}
+
+static int
+set_ptypes(krb5_context context,
+ KRB_ERROR *error,
+ const krb5_preauthtype **ptypes,
+ krb5_preauthdata **preauth)
+{
+ static krb5_preauthdata preauth2;
+ static krb5_preauthtype ptypes2[] = { KRB5_PADATA_ENC_TIMESTAMP, KRB5_PADATA_NONE };
+
+ if(error->e_data) {
+ METHOD_DATA md;
+ int i;
+ decode_METHOD_DATA(error->e_data->data,
+ error->e_data->length,
+ &md,
+ NULL);
+ for(i = 0; i < md.len; i++){
+ switch(md.val[i].padata_type){
+ case KRB5_PADATA_ENC_TIMESTAMP:
+ *ptypes = ptypes2;
+ break;
+ case KRB5_PADATA_ETYPE_INFO:
+ *preauth = &preauth2;
+ ALLOC_SEQ(*preauth, 1);
+ (*preauth)->val[0].type = KRB5_PADATA_ENC_TIMESTAMP;
+ krb5_decode_ETYPE_INFO(context,
+ md.val[i].padata_value.data,
+ md.val[i].padata_value.length,
+ &(*preauth)->val[0].info,
+ NULL);
+ break;
+ default:
+ break;
+ }
+ }
+ free_METHOD_DATA(&md);
+ } else {
+ *ptypes = ptypes2;
+ }
+ return(1);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_in_cred(krb5_context context,
+ krb5_flags options,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *ptypes,
+ const krb5_preauthdata *preauth,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_decrypt_proc decrypt_proc,
+ krb5_const_pointer decryptarg,
+ krb5_creds *creds,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_error_code ret;
+ AS_REQ a;
+ krb5_kdc_rep rep;
+ krb5_data req, resp;
+ size_t len;
+ krb5_salt salt;
+ krb5_keyblock *key;
+ size_t size;
+ KDCOptions opts;
+ PA_DATA *pa;
+ krb5_enctype etype;
+ krb5_preauthdata *my_preauth = NULL;
+ unsigned nonce;
+ int done;
+
+ opts = int2KDCOptions(options);
+
+ krb5_generate_random_block (&nonce, sizeof(nonce));
+ nonce &= 0xffffffff;
+
+ do {
+ done = 1;
+ ret = init_as_req (context,
+ opts,
+ creds,
+ addrs,
+ etypes,
+ ptypes,
+ preauth,
+ key_proc,
+ keyseed,
+ nonce,
+ &a);
+ if (my_preauth) {
+ free_ETYPE_INFO(&my_preauth->val[0].info);
+ free (my_preauth->val);
+ my_preauth = NULL;
+ }
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(AS_REQ, req.data, req.length, &a, &len, ret);
+ free_AS_REQ(&a);
+ if (ret)
+ return ret;
+ if(len != req.length)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_sendto_kdc (context, &req, &creds->client->realm, &resp);
+ krb5_data_free(&req);
+ if (ret)
+ return ret;
+
+ memset (&rep, 0, sizeof(rep));
+ ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
+ if(ret) {
+ /* let's try to parse it as a KRB-ERROR */
+ KRB_ERROR error;
+ int ret2;
+
+ ret2 = krb5_rd_error(context, &resp, &error);
+ if(ret2 && resp.data && ((char*)resp.data)[0] == 4)
+ ret = KRB5KRB_AP_ERR_V4_REPLY;
+ krb5_data_free(&resp);
+ if (ret2 == 0) {
+ ret = krb5_error_from_rd_error(context, &error, creds);
+ /* if no preauth was set and KDC requires it, give it
+ one more try */
+ if (!ptypes && !preauth
+ && ret == KRB5KDC_ERR_PREAUTH_REQUIRED
+#if 0
+ || ret == KRB5KDC_ERR_BADOPTION
+#endif
+ && set_ptypes(context, &error, &ptypes, &my_preauth)) {
+ done = 0;
+ preauth = my_preauth;
+ krb5_free_error_contents(context, &error);
+ krb5_clear_error_string(context);
+ continue;
+ }
+ if(ret_as_reply)
+ ret_as_reply->error = error;
+ else
+ free_KRB_ERROR (&error);
+ return ret;
+ }
+ return ret;
+ }
+ krb5_data_free(&resp);
+ } while(!done);
+
+ pa = NULL;
+ etype = rep.kdc_rep.enc_part.etype;
+ if(rep.kdc_rep.padata){
+ int i = 0;
+ pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len,
+ KRB5_PADATA_PW_SALT, &i);
+ if(pa == NULL) {
+ i = 0;
+ pa = krb5_find_padata(rep.kdc_rep.padata->val,
+ rep.kdc_rep.padata->len,
+ KRB5_PADATA_AFS3_SALT, &i);
+ }
+ }
+ if(pa) {
+ salt.salttype = pa->padata_type;
+ salt.saltvalue = pa->padata_value;
+
+ ret = (*key_proc)(context, etype, salt, keyseed, &key);
+ } else {
+ /* make a v5 salted pa-data */
+ ret = krb5_get_pw_salt (context, creds->client, &salt);
+
+ if (ret)
+ goto out;
+ ret = (*key_proc)(context, etype, salt, keyseed, &key);
+ krb5_free_salt(context, salt);
+ }
+ if (ret)
+ goto out;
+
+ {
+ unsigned flags = 0;
+ if (opts.request_anonymous)
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+
+ ret = _krb5_extract_ticket(context,
+ &rep,
+ creds,
+ key,
+ keyseed,
+ KRB5_KU_AS_REP_ENC_PART,
+ NULL,
+ nonce,
+ flags,
+ decrypt_proc,
+ decryptarg);
+ }
+ memset (key->keyvalue.data, 0, key->keyvalue.length);
+ krb5_free_keyblock_contents (context, key);
+ free (key);
+
+out:
+ if (ret == 0 && ret_as_reply)
+ *ret_as_reply = rep;
+ else
+ krb5_free_kdc_rep (context, &rep);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_in_tkt(krb5_context context,
+ krb5_flags options,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *ptypes,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_decrypt_proc decrypt_proc,
+ krb5_const_pointer decryptarg,
+ krb5_creds *creds,
+ krb5_ccache ccache,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_error_code ret;
+
+ ret = krb5_get_in_cred (context,
+ options,
+ addrs,
+ etypes,
+ ptypes,
+ NULL,
+ key_proc,
+ keyseed,
+ decrypt_proc,
+ decryptarg,
+ creds,
+ ret_as_reply);
+ if(ret)
+ return ret;
+ if (ccache)
+ ret = krb5_cc_store_cred (context, ccache, creds);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c b/source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c
new file mode 100644
index 0000000000..78a1c340ac
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytab_key_proc (krb5_context context,
+ krb5_enctype enctype,
+ krb5_salt salt,
+ krb5_const_pointer keyseed,
+ krb5_keyblock **key)
+{
+ krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
+ krb5_keytab keytab = args->keytab;
+ krb5_principal principal = args->principal;
+ krb5_error_code ret;
+ krb5_keytab real_keytab;
+ krb5_keytab_entry entry;
+
+ if(keytab == NULL)
+ krb5_kt_default(context, &real_keytab);
+ else
+ real_keytab = keytab;
+
+ ret = krb5_kt_get_entry (context, real_keytab, principal,
+ 0, enctype, &entry);
+
+ if (keytab == NULL)
+ krb5_kt_close (context, real_keytab);
+
+ if (ret)
+ return ret;
+
+ ret = krb5_copy_keyblock (context, &entry.keyblock, key);
+ krb5_kt_free_entry(context, &entry);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_in_tkt_with_keytab (krb5_context context,
+ krb5_flags options,
+ krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *pre_auth_types,
+ krb5_keytab keytab,
+ krb5_ccache ccache,
+ krb5_creds *creds,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_keytab_key_proc_args a;
+
+ a.principal = creds->client;
+ a.keytab = keytab;
+
+ return krb5_get_in_tkt (context,
+ options,
+ addrs,
+ etypes,
+ pre_auth_types,
+ krb5_keytab_key_proc,
+ &a,
+ NULL,
+ NULL,
+ creds,
+ ccache,
+ ret_as_reply);
+}
diff --git a/source4/heimdal/lib/krb5/get_port.c b/source4/heimdal/lib/krb5/get_port.c
new file mode 100644
index 0000000000..895c21a433
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_port.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+int KRB5_LIB_FUNCTION
+krb5_getportbyname (krb5_context context,
+ const char *service,
+ const char *proto,
+ int default_port)
+{
+ struct servent *sp;
+
+ if ((sp = roken_getservbyname (service, proto)) == NULL) {
+#if 0
+ krb5_warnx(context, "%s/%s unknown service, using default port %d",
+ service, proto, default_port);
+#endif
+ return htons(default_port);
+ } else
+ return sp->s_port;
+}
diff --git a/source4/heimdal/lib/krb5/heim_err.et b/source4/heimdal/lib/krb5/heim_err.et
new file mode 100644
index 0000000000..547a14e04c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/heim_err.et
@@ -0,0 +1,44 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table heim
+
+prefix HEIM_ERR
+
+error_code LOG_PARSE, "Error parsing log destination"
+error_code V4_PRINC_NO_CONV, "Failed to convert v4 principal"
+error_code SALTTYPE_NOSUPP, "Salt type is not supported by enctype"
+error_code NOHOST, "Host not found"
+error_code OPNOTSUPP, "Operation not supported"
+error_code EOF, "End of file"
+error_code BAD_MKEY, "Failed to get the master key"
+error_code SERVICE_NOMATCH, "Unacceptable service used"
+
+index 64
+prefix HEIM_PKINIT
+error_code NO_CERTIFICATE, "Certificate missing"
+error_code NO_PRIVATE_KEY, "Private key missing"
+error_code NO_VALID_CA, "No valid certificate authority"
+error_code CERTIFICATE_INVALID, "Certificate invalid"
+error_code PRIVATE_KEY_INVALID, "Private key invalid"
+
+index 128
+prefix HEIM_EAI
+#error_code NOERROR, "no error"
+error_code UNKNOWN, "unknown error from getaddrinfo"
+error_code ADDRFAMILY, "address family for nodename not supported"
+error_code AGAIN, "temporary failure in name resolution"
+error_code BADFLAGS, "invalid value for ai_flags"
+error_code FAIL, "non-recoverable failure in name resolution"
+error_code FAMILY, "ai_family not supported"
+error_code MEMORY, "memory allocation failure"
+error_code NODATA, "no address associated with nodename"
+error_code NONAME, "nodename nor servname provided, or not known"
+error_code SERVICE, "servname not supported for ai_socktype"
+error_code SOCKTYPE, "ai_socktype not supported"
+error_code SYSTEM, "system error returned in errno"
+end
diff --git a/source4/heimdal/lib/krb5/heim_threads.h b/source4/heimdal/lib/krb5/heim_threads.h
new file mode 100644
index 0000000000..c550499499
--- /dev/null
+++ b/source4/heimdal/lib/krb5/heim_threads.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/*
+ * Provide wrapper macros for thread synchronization primitives so we
+ * can use native thread functions for those operating system that
+ * supports it.
+ *
+ * This is so libkrb5.so (or more importantly, libgssapi.so) can have
+ * thread support while the program that that dlopen(3)s the library
+ * don't need to be linked to libpthread.
+ */
+
+#ifndef HEIM_THREADS_H
+#define HEIM_THREADS_H 1
+
+/* assume headers already included */
+
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 106120000 && __NetBSD_Version__< 299001200 && defined(ENABLE_PTHREAD_SUPPORT)
+
+/*
+ * NetBSD have a thread lib that we can use that part of libc that
+ * works regardless if application are linked to pthreads or not.
+ * NetBSD newer then 2.99.11 just use pthread.h, and the same thing
+ * will happen.
+ */
+#include <threadlib.h>
+
+#define HEIMDAL_MUTEX mutex_t
+#define HEIMDAL_MUTEX_INITIALIZER MUTEX_INITIALIZER
+#define HEIMDAL_MUTEX_init(m) mutex_init(m, NULL)
+#define HEIMDAL_MUTEX_lock(m) mutex_lock(m)
+#define HEIMDAL_MUTEX_unlock(m) mutex_unlock(m)
+#define HEIMDAL_MUTEX_destroy(m) mutex_destroy(m)
+
+#define HEIMDAL_RWLOCK rwlock_t
+#define HEIMDAL_RWLOCK_INITIALIZER RWLOCK_INITIALIZER
+#define HEIMDAL_RWLOCK_init(l) rwlock_init(l, NULL)
+#define HEIMDAL_RWLOCK_rdlock(l) rwlock_rdlock(l)
+#define HEIMDAL_RWLOCK_wrlock(l) rwlock_wrlock(l)
+#define HEIMDAL_RWLOCK_tryrdlock(l) rwlock_tryrdlock(l)
+#define HEIMDAL_RWLOCK_trywrlock(l) rwlock_trywrlock(l)
+#define HEIMDAL_RWLOCK_unlock(l) rwlock_unlock(l)
+#define HEIMDAL_RWLOCK_destroy(l) rwlock_destroy(l)
+
+#define HEIMDAL_thread_key thread_key_t
+#define HEIMDAL_key_create(k,d,r) do { r = thr_keycreate(k,d); } while(0)
+#define HEIMDAL_setspecific(k,s,r) do { r = thr_setspecific(k,s); } while(0)
+#define HEIMDAL_getspecific(k) thr_getspecific(k)
+#define HEIMDAL_key_delete(k) thr_keydelete(k)
+
+#elif defined(ENABLE_PTHREAD_SUPPORT) && (!defined(__NetBSD__) || __NetBSD_Version__ >= 299001200)
+
+#include <pthread.h>
+
+#define HEIMDAL_MUTEX pthread_mutex_t
+#define HEIMDAL_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define HEIMDAL_MUTEX_init(m) pthread_mutex_init(m, NULL)
+#define HEIMDAL_MUTEX_lock(m) pthread_mutex_lock(m)
+#define HEIMDAL_MUTEX_unlock(m) pthread_mutex_unlock(m)
+#define HEIMDAL_MUTEX_destroy(m) pthread_mutex_destroy(m)
+
+#define HEIMDAL_RWLOCK rwlock_t
+#define HEIMDAL_RWLOCK_INITIALIZER RWLOCK_INITIALIZER
+#define HEIMDAL_RWLOCK_init(l) pthread_rwlock_init(l, NULL)
+#define HEIMDAL_RWLOCK_rdlock(l) pthread_rwlock_rdlock(l)
+#define HEIMDAL_RWLOCK_wrlock(l) pthread_rwlock_wrlock(l)
+#define HEIMDAL_RWLOCK_tryrdlock(l) pthread_rwlock_tryrdlock(l)
+#define HEIMDAL_RWLOCK_trywrlock(l) pthread_rwlock_trywrlock(l)
+#define HEIMDAL_RWLOCK_unlock(l) pthread_rwlock_unlock(l)
+#define HEIMDAL_RWLOCK_destroy(l) pthread_rwlock_destroy(l)
+
+#define HEIMDAL_thread_key pthread_key_t
+#define HEIMDAL_key_create(k,d,r) do { r = pthread_key_create(k,d); } while(0)
+#define HEIMDAL_setspecific(k,s,r) do { r = pthread_setspecific(k,s); } while(0)
+#define HEIMDAL_getspecific(k) pthread_getspecific(k)
+#define HEIMDAL_key_delete(k) pthread_key_delete(k)
+
+#elif defined(HEIMDAL_DEBUG_THREADS)
+
+/* no threads support, just do consistency checks */
+#include <stdlib.h>
+
+#define HEIMDAL_MUTEX int
+#define HEIMDAL_MUTEX_INITIALIZER 0
+#define HEIMDAL_MUTEX_init(m) do { (*(m)) = 0; } while(0)
+#define HEIMDAL_MUTEX_lock(m) do { if ((*(m))++ != 0) abort(); } while(0)
+#define HEIMDAL_MUTEX_unlock(m) do { if ((*(m))-- != 1) abort(); } while(0)
+#define HEIMDAL_MUTEX_destroy(m) do {if ((*(m)) != 0) abort(); } while(0)
+
+#define HEIMDAL_RWLOCK rwlock_t int
+#define HEIMDAL_RWLOCK_INITIALIZER 0
+#define HEIMDAL_RWLOCK_init(l) do { } while(0)
+#define HEIMDAL_RWLOCK_rdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_wrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_tryrdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_trywrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_unlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_destroy(l) do { } while(0)
+
+#define HEIMDAL_internal_thread_key 1
+
+#else /* no thread support, no debug case */
+
+#define HEIMDAL_MUTEX int
+#define HEIMDAL_MUTEX_INITIALIZER 0
+#define HEIMDAL_MUTEX_init(m) do { (void)(m); } while(0)
+#define HEIMDAL_MUTEX_lock(m) do { (void)(m); } while(0)
+#define HEIMDAL_MUTEX_unlock(m) do { (void)(m); } while(0)
+#define HEIMDAL_MUTEX_destroy(m) do { (void)(m); } while(0)
+
+#define HEIMDAL_RWLOCK rwlock_t int
+#define HEIMDAL_RWLOCK_INITIALIZER 0
+#define HEIMDAL_RWLOCK_init(l) do { } while(0)
+#define HEIMDAL_RWLOCK_rdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_wrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_tryrdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_trywrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_unlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_destroy(l) do { } while(0)
+
+#define HEIMDAL_internal_thread_key 1
+
+#endif /* no thread support */
+
+#ifdef HEIMDAL_internal_thread_key
+
+typedef struct heim_thread_key {
+ void *value;
+ void (*destructor)(void *);
+} heim_thread_key;
+
+#define HEIMDAL_thread_key heim_thread_key
+#define HEIMDAL_key_create(k,d,r) \
+ do { (k)->value = NULL; (k)->destructor = (d); r = 0; } while(0)
+#define HEIMDAL_setspecific(k,s,r) do { (k).value = s ; r = 0; } while(0)
+#define HEIMDAL_getspecific(k) ((k).value)
+#define HEIMDAL_key_delete(k) do { (*(k).destructor)((k).value); } while(0)
+
+#undef HEIMDAL_internal_thread_key
+#endif /* HEIMDAL_internal_thread_key */
+
+#endif /* HEIM_THREADS_H */
diff --git a/source4/heimdal/lib/krb5/init_creds.c b/source4/heimdal/lib/krb5/init_creds.c
new file mode 100644
index 0000000000..b2b3b6550d
--- /dev/null
+++ b/source4/heimdal/lib/krb5/init_creds.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt)
+{
+ memset (opt, 0, sizeof(*opt));
+ opt->flags = 0;
+ opt->opt_private = NULL;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_alloc(krb5_context context,
+ krb5_get_init_creds_opt **opt)
+{
+ krb5_get_init_creds_opt *o;
+
+ *opt = NULL;
+ o = calloc(1, sizeof(*o));
+ if (o == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_get_init_creds_opt_init(o);
+ o->opt_private = calloc(1, sizeof(*o->opt_private));
+ if (o->opt_private == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ free(o);
+ return ENOMEM;
+ }
+ o->opt_private->refcount = 1;
+ *opt = o;
+ return 0;
+}
+
+krb5_error_code
+_krb5_get_init_creds_opt_copy(krb5_context context,
+ const krb5_get_init_creds_opt *in,
+ krb5_get_init_creds_opt **out)
+{
+ krb5_get_init_creds_opt *opt;
+
+ *out = NULL;
+ opt = calloc(1, sizeof(*opt));
+ if (opt == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ if (in)
+ *opt = *in;
+ if(opt->opt_private == NULL) {
+ opt->opt_private = calloc(1, sizeof(*opt->opt_private));
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ free(opt);
+ return ENOMEM;
+ }
+ opt->opt_private->refcount = 1;
+ } else
+ opt->opt_private->refcount++;
+ *out = opt;
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+_krb5_get_init_creds_opt_free_krb5_error(krb5_get_init_creds_opt *opt)
+{
+ if (opt->opt_private == NULL || opt->opt_private->error == NULL)
+ return;
+ free_KRB_ERROR(opt->opt_private->error);
+ free(opt->opt_private->error);
+ opt->opt_private->error = NULL;
+}
+
+void KRB5_LIB_FUNCTION
+_krb5_get_init_creds_opt_set_krb5_error(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const KRB_ERROR *error)
+{
+ krb5_error_code ret;
+
+ if (opt->opt_private == NULL)
+ return;
+
+ _krb5_get_init_creds_opt_free_krb5_error(opt);
+
+ opt->opt_private->error = malloc(sizeof(*opt->opt_private->error));
+ if (opt->opt_private->error == NULL)
+ return;
+ ret = copy_KRB_ERROR(error, opt->opt_private->error);
+ if (ret) {
+ free(opt->opt_private->error);
+ opt->opt_private->error = NULL;
+ }
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_free(krb5_context context,
+ krb5_get_init_creds_opt *opt)
+{
+ if (opt == NULL || opt->opt_private == NULL)
+ return;
+ if (opt->opt_private->refcount < 1) /* abort ? */
+ return;
+ if (--opt->opt_private->refcount == 0) {
+ _krb5_get_init_creds_opt_free_krb5_error(opt);
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ free(opt->opt_private);
+ }
+ memset(opt, 0, sizeof(*opt));
+ free(opt);
+}
+
+static int
+get_config_time (krb5_context context,
+ const char *realm,
+ const char *name,
+ int def)
+{
+ int ret;
+
+ ret = krb5_config_get_time (context, NULL,
+ "realms",
+ realm,
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ ret = krb5_config_get_time (context, NULL,
+ "libdefaults",
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ return def;
+}
+
+static krb5_boolean
+get_config_bool (krb5_context context,
+ const char *realm,
+ const char *name)
+{
+ return krb5_config_get_bool (context,
+ NULL,
+ "realms",
+ realm,
+ name,
+ NULL)
+ || krb5_config_get_bool (context,
+ NULL,
+ "libdefaults",
+ name,
+ NULL);
+}
+
+/*
+ * set all the values in `opt' to the appropriate values for
+ * application `appname' (default to getprogname() if NULL), and realm
+ * `realm'. First looks in [appdefaults] but falls back to
+ * [realms] or [libdefaults] for some of the values.
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_default_flags(krb5_context context,
+ const char *appname,
+ krb5_const_realm realm,
+ krb5_get_init_creds_opt *opt)
+{
+ krb5_boolean b;
+ time_t t;
+
+ b = get_config_bool (context, realm, "forwardable");
+ krb5_appdefault_boolean(context, appname, realm, "forwardable", b, &b);
+ krb5_get_init_creds_opt_set_forwardable(opt, b);
+
+ b = get_config_bool (context, realm, "proxiable");
+ krb5_appdefault_boolean(context, appname, realm, "proxiable", b, &b);
+ krb5_get_init_creds_opt_set_proxiable (opt, b);
+
+ krb5_appdefault_time(context, appname, realm, "ticket_lifetime", 0, &t);
+ if (t == 0)
+ t = get_config_time (context, realm, "ticket_lifetime", 0);
+ if(t != 0)
+ krb5_get_init_creds_opt_set_tkt_life(opt, t);
+
+ krb5_appdefault_time(context, appname, realm, "renew_lifetime", 0, &t);
+ if (t == 0)
+ t = get_config_time (context, realm, "renew_lifetime", 0);
+ if(t != 0)
+ krb5_get_init_creds_opt_set_renew_life(opt, t);
+
+ krb5_appdefault_boolean(context, appname, realm, "no-addresses",
+ KRB5_ADDRESSLESS_DEFAULT, &b);
+ krb5_get_init_creds_opt_set_addressless (context, opt, b);
+
+#if 0
+ krb5_appdefault_boolean(context, appname, realm, "anonymous", FALSE, &b);
+ krb5_get_init_creds_opt_set_anonymous (opt, b);
+
+ krb5_get_init_creds_opt_set_etype_list(opt, enctype,
+ etype_str.num_strings);
+
+ krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt,
+ krb5_data *salt);
+
+ krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt,
+ krb5_preauthtype *preauth_list,
+ int preauth_list_length);
+#endif
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt,
+ krb5_deltat tkt_life)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_TKT_LIFE;
+ opt->tkt_life = tkt_life;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt,
+ krb5_deltat renew_life)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE;
+ opt->renew_life = renew_life;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt,
+ int forwardable)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE;
+ opt->forwardable = forwardable;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt,
+ int proxiable)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE;
+ opt->proxiable = proxiable;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt,
+ krb5_enctype *etype_list,
+ int etype_list_length)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST;
+ opt->etype_list = etype_list;
+ opt->etype_list_length = etype_list_length;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt,
+ krb5_addresses *addresses)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST;
+ opt->address_list = addresses;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt,
+ krb5_preauthtype *preauth_list,
+ int preauth_list_length)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST;
+ opt->preauth_list_length = preauth_list_length;
+ opt->preauth_list = preauth_list;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt,
+ krb5_data *salt)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT;
+ opt->salt = salt;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_anonymous(krb5_get_init_creds_opt *opt,
+ int anonymous)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_ANONYMOUS;
+ opt->anonymous = anonymous;
+}
+
+static krb5_error_code
+require_ext_opt(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const char *type)
+{
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, EINVAL, "%s on non extendable opt", type);
+ return EINVAL;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_pa_password(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const char *password,
+ krb5_s2k_proc key_proc)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_pa_password");
+ if (ret)
+ return ret;
+ opt->opt_private->password = password;
+ opt->opt_private->key_proc = key_proc;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_pac_request(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean req_pac)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_pac_req");
+ if (ret)
+ return ret;
+ opt->opt_private->req_pac = req_pac ?
+ KRB5_INIT_CREDS_TRISTATE_TRUE :
+ KRB5_INIT_CREDS_TRISTATE_FALSE;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_get_error(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ KRB_ERROR **error)
+{
+ krb5_error_code ret;
+
+ *error = NULL;
+
+ ret = require_ext_opt(context, opt, "init_creds_opt_get_error");
+ if (ret)
+ return ret;
+
+ if (opt->opt_private->error == NULL)
+ return 0;
+
+ *error = malloc(sizeof(**error));
+ if (*error == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = copy_KRB_ERROR(opt->opt_private->error, *error);
+ if (ret)
+ krb5_clear_error_string(context);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_addressless(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean addressless)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_pac_req");
+ if (ret)
+ return ret;
+ if (addressless)
+ opt->opt_private->addressless = KRB5_INIT_CREDS_TRISTATE_TRUE;
+ else
+ opt->opt_private->addressless = KRB5_INIT_CREDS_TRISTATE_FALSE;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_canonicalize(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean req)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_canonicalize");
+ if (ret)
+ return ret;
+ if (req)
+ opt->opt_private->flags |= KRB5_INIT_CREDS_CANONICALIZE;
+ else
+ opt->opt_private->flags &= ~KRB5_INIT_CREDS_CANONICALIZE;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_win2k(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean req)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_win2k");
+ if (ret)
+ return ret;
+ if (req)
+ opt->opt_private->flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
+ else
+ opt->opt_private->flags &= ~KRB5_INIT_CREDS_NO_C_CANON_CHECK;
+ return 0;
+}
+
diff --git a/source4/heimdal/lib/krb5/init_creds_pw.c b/source4/heimdal/lib/krb5/init_creds_pw.c
new file mode 100644
index 0000000000..f56d069b37
--- /dev/null
+++ b/source4/heimdal/lib/krb5/init_creds_pw.c
@@ -0,0 +1,1602 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_get_init_creds_ctx {
+ KDCOptions flags;
+ krb5_creds cred;
+ krb5_addresses *addrs;
+ krb5_enctype *etypes;
+ krb5_preauthtype *pre_auth_types;
+ const char *in_tkt_service;
+ unsigned nonce;
+ unsigned pk_nonce;
+
+ krb5_data req_buffer;
+ AS_REQ as_req;
+ int pa_counter;
+
+ const char *password;
+ krb5_s2k_proc key_proc;
+
+ krb5_get_init_creds_tristate req_pac;
+
+ krb5_pk_init_ctx pk_init_ctx;
+ int ic_flags;
+} krb5_get_init_creds_ctx;
+
+static krb5_error_code
+default_s2k_func(krb5_context context, krb5_enctype type,
+ krb5_const_pointer keyseed,
+ krb5_salt salt, krb5_data *s2kparms,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+ krb5_data password;
+ krb5_data opaque;
+
+ password.data = rk_UNCONST(keyseed);
+ password.length = strlen(keyseed);
+ if (s2kparms)
+ opaque = *s2kparms;
+ else
+ krb5_data_zero(&opaque);
+
+ *key = malloc(sizeof(**key));
+ if (*key == NULL)
+ return ENOMEM;
+ ret = krb5_string_to_key_data_salt_opaque(context, type, password,
+ salt, opaque, *key);
+ if (ret) {
+ free(*key);
+ *key = NULL;
+ }
+ return ret;
+}
+
+static void
+free_init_creds_ctx(krb5_context context, krb5_get_init_creds_ctx *ctx)
+{
+ if (ctx->etypes)
+ free(ctx->etypes);
+ if (ctx->pre_auth_types)
+ free (ctx->pre_auth_types);
+ free_AS_REQ(&ctx->as_req);
+ memset(&ctx->as_req, 0, sizeof(ctx->as_req));
+}
+
+static int
+get_config_time (krb5_context context,
+ const char *realm,
+ const char *name,
+ int def)
+{
+ int ret;
+
+ ret = krb5_config_get_time (context, NULL,
+ "realms",
+ realm,
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ ret = krb5_config_get_time (context, NULL,
+ "libdefaults",
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ return def;
+}
+
+static krb5_error_code
+init_cred (krb5_context context,
+ krb5_creds *cred,
+ krb5_principal client,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ krb5_error_code ret;
+ krb5_const_realm client_realm;
+ int tmp;
+ krb5_timestamp now;
+
+ krb5_timeofday (context, &now);
+
+ memset (cred, 0, sizeof(*cred));
+
+ if (client)
+ krb5_copy_principal(context, client, &cred->client);
+ else {
+ ret = krb5_get_default_principal (context,
+ &cred->client);
+ if (ret)
+ goto out;
+ }
+
+ client_realm = krb5_principal_get_realm (context, cred->client);
+
+ if (start_time)
+ cred->times.starttime = now + start_time;
+
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
+ tmp = options->tkt_life;
+ else
+ tmp = 10 * 60 * 60;
+ cred->times.endtime = now + tmp;
+
+ if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
+ options->renew_life > 0) {
+ cred->times.renew_till = now + options->renew_life;
+ }
+
+ if (in_tkt_service) {
+ ret = krb5_parse_name (context, in_tkt_service, &cred->server);
+ if (ret)
+ goto out;
+ krb5_principal_set_realm (context, cred->server, client_realm);
+ } else {
+ ret = krb5_make_principal(context, &cred->server,
+ client_realm, KRB5_TGS_NAME, client_realm,
+ NULL);
+ if (ret)
+ goto out;
+ }
+ return 0;
+
+out:
+ krb5_free_cred_contents (context, cred);
+ return ret;
+}
+
+/*
+ * Print a message (str) to the user about the expiration in `lr'
+ */
+
+static void
+report_expiration (krb5_context context,
+ krb5_prompter_fct prompter,
+ krb5_data *data,
+ const char *str,
+ time_t now)
+{
+ char *p;
+
+ asprintf (&p, "%s%s", str, ctime(&now));
+ (*prompter) (context, data, NULL, p, 0, NULL);
+ free (p);
+}
+
+/*
+ * Parse the last_req data and show it to the user if it's interesting
+ */
+
+static void
+print_expire (krb5_context context,
+ krb5_const_realm realm,
+ krb5_kdc_rep *rep,
+ krb5_prompter_fct prompter,
+ krb5_data *data)
+{
+ int i;
+ LastReq *lr = &rep->enc_part.last_req;
+ krb5_timestamp sec;
+ time_t t;
+ krb5_boolean reported = FALSE;
+
+ krb5_timeofday (context, &sec);
+
+ t = sec + get_config_time (context,
+ realm,
+ "warn_pwexpire",
+ 7 * 24 * 60 * 60);
+
+ for (i = 0; i < lr->len; ++i) {
+ if (lr->val[i].lr_value <= t) {
+ switch (abs(lr->val[i].lr_type)) {
+ case LR_PW_EXPTIME :
+ report_expiration(context, prompter, data,
+ "Your password will expire at ",
+ lr->val[i].lr_value);
+ reported = TRUE;
+ break;
+ case LR_ACCT_EXPTIME :
+ report_expiration(context, prompter, data,
+ "Your account will expire at ",
+ lr->val[i].lr_value);
+ reported = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!reported
+ && rep->enc_part.key_expiration
+ && *rep->enc_part.key_expiration <= t) {
+ report_expiration(context, prompter, data,
+ "Your password/account will expire at ",
+ *rep->enc_part.key_expiration);
+ }
+}
+
+static krb5_addresses no_addrs = { 0, NULL };
+
+static krb5_error_code
+get_init_creds_common(krb5_context context,
+ krb5_principal client,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options,
+ krb5_get_init_creds_ctx *ctx)
+{
+ krb5_get_init_creds_opt default_opt;
+ krb5_error_code ret;
+ krb5_enctype *etypes;
+ krb5_preauthtype *pre_auth_types;
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ if (options == NULL) {
+ krb5_get_init_creds_opt_init (&default_opt);
+ options = &default_opt;
+ } else {
+ _krb5_get_init_creds_opt_free_krb5_error(options);
+ }
+
+ if (options->opt_private) {
+ ctx->password = options->opt_private->password;
+ ctx->key_proc = options->opt_private->key_proc;
+ ctx->req_pac = options->opt_private->req_pac;
+ ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
+ ctx->ic_flags = options->opt_private->flags;
+ } else
+ ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
+
+ if (ctx->key_proc == NULL)
+ ctx->key_proc = default_s2k_func;
+
+ if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
+ ctx->flags.canonicalize = 1;
+
+ ctx->pre_auth_types = NULL;
+ ctx->addrs = NULL;
+ ctx->etypes = NULL;
+ ctx->pre_auth_types = NULL;
+ ctx->in_tkt_service = in_tkt_service;
+
+ ret = init_cred (context, &ctx->cred, client, start_time,
+ in_tkt_service, options);
+ if (ret)
+ return ret;
+
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
+ ctx->flags.forwardable = options->forwardable;
+
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
+ ctx->flags.proxiable = options->proxiable;
+
+ if (start_time)
+ ctx->flags.postdated = 1;
+ if (ctx->cred.times.renew_till)
+ ctx->flags.renewable = 1;
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
+ ctx->addrs = options->address_list;
+ } else if (options->opt_private) {
+ switch (options->opt_private->addressless) {
+ case KRB5_INIT_CREDS_TRISTATE_UNSET:
+#if KRB5_ADDRESSLESS_DEFAULT == TRUE
+ ctx->addrs = &no_addrs;
+#else
+ ctx->addrs = NULL;
+#endif
+ break;
+ case KRB5_INIT_CREDS_TRISTATE_FALSE:
+ ctx->addrs = NULL;
+ break;
+ case KRB5_INIT_CREDS_TRISTATE_TRUE:
+ ctx->addrs = &no_addrs;
+ break;
+ }
+ }
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
+ etypes = malloc((options->etype_list_length + 1)
+ * sizeof(krb5_enctype));
+ if (etypes == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy (etypes, options->etype_list,
+ options->etype_list_length * sizeof(krb5_enctype));
+ etypes[options->etype_list_length] = ETYPE_NULL;
+ ctx->etypes = etypes;
+ }
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
+ pre_auth_types = malloc((options->preauth_list_length + 1)
+ * sizeof(krb5_preauthtype));
+ if (pre_auth_types == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy (pre_auth_types, options->preauth_list,
+ options->preauth_list_length * sizeof(krb5_preauthtype));
+ pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
+ ctx->pre_auth_types = pre_auth_types;
+ }
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)
+ ; /* XXX */
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
+ ctx->flags.request_anonymous = options->anonymous;
+ return 0;
+}
+
+static krb5_error_code
+change_password (krb5_context context,
+ krb5_principal client,
+ const char *password,
+ char *newpw,
+ size_t newpw_sz,
+ krb5_prompter_fct prompter,
+ void *data,
+ krb5_get_init_creds_opt *old_options)
+{
+ krb5_prompt prompts[2];
+ krb5_error_code ret;
+ krb5_creds cpw_cred;
+ char buf1[BUFSIZ], buf2[BUFSIZ];
+ krb5_data password_data[2];
+ int result_code;
+ krb5_data result_code_string;
+ krb5_data result_string;
+ char *p;
+ krb5_get_init_creds_opt options;
+
+ memset (&cpw_cred, 0, sizeof(cpw_cred));
+
+ krb5_get_init_creds_opt_init (&options);
+ krb5_get_init_creds_opt_set_tkt_life (&options, 60);
+ krb5_get_init_creds_opt_set_forwardable (&options, FALSE);
+ krb5_get_init_creds_opt_set_proxiable (&options, FALSE);
+ if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
+ krb5_get_init_creds_opt_set_preauth_list (&options,
+ old_options->preauth_list,
+ old_options->preauth_list_length);
+
+ krb5_data_zero (&result_code_string);
+ krb5_data_zero (&result_string);
+
+ ret = krb5_get_init_creds_password (context,
+ &cpw_cred,
+ client,
+ password,
+ prompter,
+ data,
+ 0,
+ "kadmin/changepw",
+ &options);
+ if (ret)
+ goto out;
+
+ for(;;) {
+ password_data[0].data = buf1;
+ password_data[0].length = sizeof(buf1);
+
+ prompts[0].hidden = 1;
+ prompts[0].prompt = "New password: ";
+ prompts[0].reply = &password_data[0];
+ prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
+
+ password_data[1].data = buf2;
+ password_data[1].length = sizeof(buf2);
+
+ prompts[1].hidden = 1;
+ prompts[1].prompt = "Repeat new password: ";
+ prompts[1].reply = &password_data[1];
+ prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
+
+ ret = (*prompter) (context, data, NULL, "Changing password",
+ 2, prompts);
+ if (ret) {
+ memset (buf1, 0, sizeof(buf1));
+ memset (buf2, 0, sizeof(buf2));
+ goto out;
+ }
+
+ if (strcmp (buf1, buf2) == 0)
+ break;
+ memset (buf1, 0, sizeof(buf1));
+ memset (buf2, 0, sizeof(buf2));
+ }
+
+ ret = krb5_set_password (context,
+ &cpw_cred,
+ buf1,
+ client,
+ &result_code,
+ &result_code_string,
+ &result_string);
+ if (ret)
+ goto out;
+ asprintf (&p, "%s: %.*s\n",
+ result_code ? "Error" : "Success",
+ (int)result_string.length,
+ result_string.length > 0 ? (char*)result_string.data : "");
+
+ ret = (*prompter) (context, data, NULL, p, 0, NULL);
+ free (p);
+ if (result_code == 0) {
+ strlcpy (newpw, buf1, newpw_sz);
+ ret = 0;
+ } else {
+ ret = ENOTTY;
+ krb5_set_error_message(context, ret, "failed changing password");
+ }
+
+out:
+ memset (buf1, 0, sizeof(buf1));
+ memset (buf2, 0, sizeof(buf2));
+ krb5_data_free (&result_string);
+ krb5_data_free (&result_code_string);
+ krb5_free_cred_contents (context, &cpw_cred);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keyblock_key_proc (krb5_context context,
+ krb5_keytype type,
+ krb5_data *salt,
+ krb5_const_pointer keyseed,
+ krb5_keyblock **key)
+{
+ return krb5_copy_keyblock (context, keyseed, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_keytab(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_keytab keytab,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ krb5_get_init_creds_ctx ctx;
+ krb5_error_code ret;
+ krb5_keytab_key_proc_args *a;
+
+ ret = get_init_creds_common(context, client, start_time,
+ in_tkt_service, options, &ctx);
+ if (ret)
+ goto out;
+
+ a = malloc (sizeof(*a));
+ if (a == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ a->principal = ctx.cred.client;
+ a->keytab = keytab;
+
+ ret = krb5_get_in_cred (context,
+ KDCOptions2int(ctx.flags),
+ ctx.addrs,
+ ctx.etypes,
+ ctx.pre_auth_types,
+ NULL,
+ krb5_keytab_key_proc,
+ a,
+ NULL,
+ NULL,
+ &ctx.cred,
+ NULL);
+ free (a);
+
+ if (ret == 0 && creds)
+ *creds = ctx.cred;
+ else
+ krb5_free_cred_contents (context, &ctx.cred);
+
+ out:
+ free_init_creds_ctx(context, &ctx);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+init_creds_init_as_req (krb5_context context,
+ KDCOptions opts,
+ const krb5_creds *creds,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ AS_REQ *a)
+{
+ krb5_error_code ret;
+
+ memset(a, 0, sizeof(*a));
+
+ a->pvno = 5;
+ a->msg_type = krb_as_req;
+ a->req_body.kdc_options = opts;
+ a->req_body.cname = malloc(sizeof(*a->req_body.cname));
+ if (a->req_body.cname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ a->req_body.sname = malloc(sizeof(*a->req_body.sname));
+ if (a->req_body.sname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+
+ ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
+ if (ret)
+ goto fail;
+ ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
+ if (ret)
+ goto fail;
+
+ ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
+ if (ret)
+ goto fail;
+
+ if(creds->times.starttime) {
+ a->req_body.from = malloc(sizeof(*a->req_body.from));
+ if (a->req_body.from == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ *a->req_body.from = creds->times.starttime;
+ }
+ if(creds->times.endtime){
+ ALLOC(a->req_body.till, 1);
+ *a->req_body.till = creds->times.endtime;
+ }
+ if(creds->times.renew_till){
+ a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
+ if (a->req_body.rtime == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ *a->req_body.rtime = creds->times.renew_till;
+ }
+ a->req_body.nonce = 0;
+ ret = krb5_init_etype (context,
+ &a->req_body.etype.len,
+ &a->req_body.etype.val,
+ etypes);
+ if (ret)
+ goto fail;
+
+ /*
+ * This means no addresses
+ */
+
+ if (addrs && addrs->len == 0) {
+ a->req_body.addresses = NULL;
+ } else {
+ a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
+ if (a->req_body.addresses == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+
+ if (addrs)
+ ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
+ else {
+ ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
+ if(ret == 0 && a->req_body.addresses->len == 0) {
+ free(a->req_body.addresses);
+ a->req_body.addresses = NULL;
+ }
+ }
+ if (ret)
+ goto fail;
+ }
+
+ a->req_body.enc_authorization_data = NULL;
+ a->req_body.additional_tickets = NULL;
+
+ a->padata = NULL;
+
+ return 0;
+ fail:
+ free_AS_REQ(a);
+ memset(a, 0, sizeof(*a));
+ return ret;
+}
+
+struct pa_info_data {
+ krb5_enctype etype;
+ krb5_salt salt;
+ krb5_data *s2kparams;
+};
+
+static void
+free_paid(krb5_context context, struct pa_info_data *ppaid)
+{
+ krb5_free_salt(context, ppaid->salt);
+ if (ppaid->s2kparams)
+ krb5_free_data(context, ppaid->s2kparams);
+}
+
+
+static krb5_error_code
+set_paid(struct pa_info_data *paid, krb5_context context,
+ krb5_enctype etype,
+ krb5_salttype salttype, void *salt_string, size_t salt_len,
+ krb5_data *s2kparams)
+{
+ paid->etype = etype;
+ paid->salt.salttype = salttype;
+ paid->salt.saltvalue.data = malloc(salt_len + 1);
+ if (paid->salt.saltvalue.data == NULL) {
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+ memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
+ ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
+ paid->salt.saltvalue.length = salt_len;
+ if (s2kparams) {
+ krb5_error_code ret;
+
+ ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
+ if (ret) {
+ krb5_clear_error_string(context);
+ krb5_free_salt(context, paid->salt);
+ return ret;
+ }
+ } else
+ paid->s2kparams = NULL;
+
+ return 0;
+}
+
+static struct pa_info_data *
+pa_etype_info2(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ heim_octet_string *data)
+{
+ krb5_error_code ret;
+ ETYPE_INFO2 e;
+ size_t sz;
+ int i, j;
+
+ memset(&e, 0, sizeof(e));
+ ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
+ if (ret)
+ goto out;
+ if (e.len == 0)
+ goto out;
+ for (j = 0; j < asreq->req_body.etype.len; j++) {
+ for (i = 0; i < e.len; i++) {
+ if (asreq->req_body.etype.val[j] == e.val[i].etype) {
+ krb5_salt salt;
+ if (e.val[i].salt == NULL)
+ ret = krb5_get_pw_salt(context, client, &salt);
+ else {
+ salt.saltvalue.data = *e.val[i].salt;
+ salt.saltvalue.length = strlen(*e.val[i].salt);
+ ret = 0;
+ }
+ if (ret == 0)
+ ret = set_paid(paid, context, e.val[i].etype,
+ KRB5_PW_SALT,
+ salt.saltvalue.data,
+ salt.saltvalue.length,
+ e.val[i].s2kparams);
+ if (e.val[i].salt == NULL)
+ krb5_free_salt(context, salt);
+ if (ret == 0) {
+ free_ETYPE_INFO2(&e);
+ return paid;
+ }
+ }
+ }
+ }
+ out:
+ free_ETYPE_INFO2(&e);
+ return NULL;
+}
+
+static struct pa_info_data *
+pa_etype_info(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ heim_octet_string *data)
+{
+ krb5_error_code ret;
+ ETYPE_INFO e;
+ size_t sz;
+ int i, j;
+
+ memset(&e, 0, sizeof(e));
+ ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
+ if (ret)
+ goto out;
+ if (e.len == 0)
+ goto out;
+ for (j = 0; j < asreq->req_body.etype.len; j++) {
+ for (i = 0; i < e.len; i++) {
+ if (asreq->req_body.etype.val[j] == e.val[i].etype) {
+ krb5_salt salt;
+ salt.salttype = KRB5_PW_SALT;
+ if (e.val[i].salt == NULL)
+ ret = krb5_get_pw_salt(context, client, &salt);
+ else {
+ salt.saltvalue = *e.val[i].salt;
+ ret = 0;
+ }
+ if (e.val[i].salttype)
+ salt.salttype = *e.val[i].salttype;
+ if (ret == 0) {
+ ret = set_paid(paid, context, e.val[i].etype,
+ salt.salttype,
+ salt.saltvalue.data,
+ salt.saltvalue.length,
+ NULL);
+ if (e.val[i].salt == NULL)
+ krb5_free_salt(context, salt);
+ }
+ if (ret == 0) {
+ free_ETYPE_INFO(&e);
+ return paid;
+ }
+ }
+ }
+ }
+ out:
+ free_ETYPE_INFO(&e);
+ return NULL;
+}
+
+static struct pa_info_data *
+pa_pw_or_afs3_salt(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ heim_octet_string *data)
+{
+ krb5_error_code ret;
+ if (paid->etype == ENCTYPE_NULL)
+ return NULL;
+ ret = set_paid(paid, context,
+ paid->etype,
+ paid->salt.salttype,
+ data->data,
+ data->length,
+ NULL);
+ if (ret)
+ return NULL;
+ return paid;
+}
+
+
+struct pa_info {
+ krb5_preauthtype type;
+ struct pa_info_data *(*salt_info)(krb5_context,
+ const krb5_principal,
+ const AS_REQ *,
+ struct pa_info_data *,
+ heim_octet_string *);
+};
+
+static struct pa_info pa_prefs[] = {
+ { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
+ { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
+ { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
+ { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
+};
+
+static PA_DATA *
+find_pa_data(const METHOD_DATA *md, int type)
+{
+ int i;
+ if (md == NULL)
+ return NULL;
+ for (i = 0; i < md->len; i++)
+ if (md->val[i].padata_type == type)
+ return &md->val[i];
+ return NULL;
+}
+
+static struct pa_info_data *
+process_pa_info(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ METHOD_DATA *md)
+{
+ struct pa_info_data *p = NULL;
+ int i;
+
+ for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
+ PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
+ if (pa == NULL)
+ continue;
+ paid->salt.salttype = pa_prefs[i].type;
+ p = (*pa_prefs[i].salt_info)(context, client, asreq,
+ paid, &pa->padata_value);
+ }
+ return p;
+}
+
+static krb5_error_code
+make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
+ krb5_enctype etype, krb5_keyblock *key)
+{
+ PA_ENC_TS_ENC p;
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ EncryptedData encdata;
+ krb5_error_code ret;
+ int32_t usec;
+ int usec2;
+ krb5_crypto crypto;
+
+ krb5_us_timeofday (context, &p.patimestamp, &usec);
+ usec2 = usec;
+ p.pausec = &usec2;
+
+ ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_PA_ENC_TIMESTAMP,
+ buf,
+ len,
+ 0,
+ &encdata);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
+ free_EncryptedData(&encdata);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
+ if (ret)
+ free(buf);
+ return ret;
+}
+
+static krb5_error_code
+add_enc_ts_padata(krb5_context context,
+ METHOD_DATA *md,
+ krb5_principal client,
+ krb5_s2k_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_enctype *enctypes,
+ unsigned netypes,
+ krb5_salt *salt,
+ krb5_data *s2kparams)
+{
+ krb5_error_code ret;
+ krb5_salt salt2;
+ krb5_enctype *ep;
+ int i;
+
+ if(salt == NULL) {
+ /* default to standard salt */
+ ret = krb5_get_pw_salt (context, client, &salt2);
+ salt = &salt2;
+ }
+ if (!enctypes) {
+ enctypes = context->etypes;
+ netypes = 0;
+ for (ep = enctypes; *ep != ETYPE_NULL; ep++)
+ netypes++;
+ }
+
+ for (i = 0; i < netypes; ++i) {
+ krb5_keyblock *key;
+
+ ret = (*key_proc)(context, enctypes[i], keyseed,
+ *salt, s2kparams, &key);
+ if (ret)
+ continue;
+ ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ return ret;
+ }
+ if(salt == &salt2)
+ krb5_free_salt(context, salt2);
+ return 0;
+}
+
+static krb5_error_code
+pa_data_to_md_ts_enc(krb5_context context,
+ const AS_REQ *a,
+ const krb5_principal client,
+ krb5_get_init_creds_ctx *ctx,
+ struct pa_info_data *ppaid,
+ METHOD_DATA *md)
+{
+ if (ctx->key_proc == NULL || ctx->password == NULL)
+ return 0;
+
+ if (ppaid) {
+ add_enc_ts_padata(context, md, client,
+ ctx->key_proc, ctx->password,
+ &ppaid->etype, 1,
+ &ppaid->salt, ppaid->s2kparams);
+ } else {
+ krb5_salt salt;
+
+ /* make a v5 salted pa-data */
+ add_enc_ts_padata(context, md, client,
+ ctx->key_proc, ctx->password,
+ a->req_body.etype.val, a->req_body.etype.len,
+ NULL, NULL);
+
+ /* make a v4 salted pa-data */
+ salt.salttype = KRB5_PW_SALT;
+ krb5_data_zero(&salt.saltvalue);
+ add_enc_ts_padata(context, md, client,
+ ctx->key_proc, ctx->password,
+ a->req_body.etype.val, a->req_body.etype.len,
+ &salt, NULL);
+ }
+ return 0;
+}
+
+static krb5_error_code
+pa_data_to_key_plain(krb5_context context,
+ const krb5_principal client,
+ krb5_get_init_creds_ctx *ctx,
+ krb5_salt salt,
+ krb5_data *s2kparams,
+ krb5_enctype etype,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+
+ ret = (*ctx->key_proc)(context, etype, ctx->password,
+ salt, s2kparams, key);
+ return ret;
+}
+
+
+static krb5_error_code
+pa_data_to_md_pkinit(krb5_context context,
+ const AS_REQ *a,
+ const krb5_principal client,
+ krb5_get_init_creds_ctx *ctx,
+ METHOD_DATA *md)
+{
+ if (ctx->pk_init_ctx == NULL)
+ return 0;
+#ifdef PKINIT
+ return _krb5_pk_mk_padata(context,
+ ctx->pk_init_ctx,
+ &a->req_body,
+ ctx->pk_nonce,
+ md);
+#else
+ krb5_set_error_message(context, EINVAL, "no support for PKINIT compiled in");
+ return EINVAL;
+#endif
+}
+
+static krb5_error_code
+pa_data_add_pac_request(krb5_context context,
+ krb5_get_init_creds_ctx *ctx,
+ METHOD_DATA *md)
+{
+ size_t len, length;
+ krb5_error_code ret;
+ PA_PAC_REQUEST req;
+ void *buf;
+
+ switch (ctx->req_pac) {
+ case KRB5_INIT_CREDS_TRISTATE_UNSET:
+ return 0; /* don't bother */
+ case KRB5_INIT_CREDS_TRISTATE_TRUE:
+ req.include_pac = 1;
+ break;
+ case KRB5_INIT_CREDS_TRISTATE_FALSE:
+ req.include_pac = 0;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
+ &req, &len, ret);
+ if (ret)
+ return ret;
+ if(len != length)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
+ if (ret)
+ free(buf);
+
+ return 0;
+}
+
+/*
+ * Assumes caller always will free `out_md', even on error.
+ */
+
+static krb5_error_code
+process_pa_data_to_md(krb5_context context,
+ const krb5_creds *creds,
+ const AS_REQ *a,
+ krb5_get_init_creds_ctx *ctx,
+ METHOD_DATA *in_md,
+ METHOD_DATA **out_md,
+ krb5_prompter_fct prompter,
+ void *prompter_data)
+{
+ krb5_error_code ret;
+
+ ALLOC(*out_md, 1);
+ if (*out_md == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*out_md)->len = 0;
+ (*out_md)->val = NULL;
+
+ /*
+ * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
+ * need to expose our password protecting our PKCS12 key.
+ */
+
+ if (ctx->pk_init_ctx) {
+
+ ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
+ if (ret)
+ return ret;
+
+ } else if (in_md->len != 0) {
+ struct pa_info_data paid, *ppaid;
+
+ memset(&paid, 0, sizeof(paid));
+
+ paid.etype = ENCTYPE_NULL;
+ ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
+
+ pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
+ if (ppaid)
+ free_paid(context, ppaid);
+ }
+
+ pa_data_add_pac_request(context, ctx, *out_md);
+
+ if ((*out_md)->len == 0) {
+ free(*out_md);
+ *out_md = NULL;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+process_pa_data_to_key(krb5_context context,
+ krb5_get_init_creds_ctx *ctx,
+ krb5_creds *creds,
+ AS_REQ *a,
+ krb5_kdc_rep *rep,
+ const krb5_krbhst_info *hi,
+ krb5_keyblock **key)
+{
+ struct pa_info_data paid, *ppaid = NULL;
+ krb5_error_code ret;
+ krb5_enctype etype;
+ PA_DATA *pa;
+
+ memset(&paid, 0, sizeof(paid));
+
+ etype = rep->kdc_rep.enc_part.etype;
+
+ if (rep->kdc_rep.padata) {
+ paid.etype = etype;
+ ppaid = process_pa_info(context, creds->client, a, &paid,
+ rep->kdc_rep.padata);
+ }
+ if (ppaid == NULL) {
+ ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
+ if (ret)
+ return ret;
+ paid.etype = etype;
+ paid.s2kparams = NULL;
+ }
+
+ pa = NULL;
+ if (rep->kdc_rep.padata) {
+ int idx = 0;
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_PK_AS_REP,
+ &idx);
+ if (pa == NULL) {
+ idx = 0;
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_PK_AS_REP_19,
+ &idx);
+ }
+ }
+ if (pa && ctx->pk_init_ctx) {
+#ifdef PKINIT
+ ret = _krb5_pk_rd_pa_reply(context,
+ a->req_body.realm,
+ ctx->pk_init_ctx,
+ etype,
+ hi,
+ ctx->pk_nonce,
+ &ctx->req_buffer,
+ pa,
+ key);
+#else
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "no support for PKINIT compiled in");
+#endif
+ } else if (ctx->password)
+ ret = pa_data_to_key_plain(context, creds->client, ctx,
+ paid.salt, paid.s2kparams, etype, key);
+ else {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "No usable pa data type");
+ }
+
+ free_paid(context, &paid);
+ return ret;
+}
+
+static krb5_error_code
+init_cred_loop(krb5_context context,
+ krb5_get_init_creds_opt *init_cred_opts,
+ const krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_get_init_creds_ctx *ctx,
+ krb5_creds *creds,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_error_code ret;
+ krb5_kdc_rep rep;
+ METHOD_DATA md;
+ krb5_data resp;
+ size_t len;
+ size_t size;
+ krb5_krbhst_info *hi = NULL;
+ krb5_sendto_ctx stctx = NULL;
+
+
+ memset(&md, 0, sizeof(md));
+ memset(&rep, 0, sizeof(rep));
+
+ _krb5_get_init_creds_opt_free_krb5_error(init_cred_opts);
+
+ if (ret_as_reply)
+ memset(ret_as_reply, 0, sizeof(*ret_as_reply));
+
+ ret = init_creds_init_as_req(context, ctx->flags, creds,
+ ctx->addrs, ctx->etypes, &ctx->as_req);
+ if (ret)
+ return ret;
+
+ ret = krb5_sendto_ctx_alloc(context, &stctx);
+ if (ret)
+ goto out;
+ krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
+
+ /* Set a new nonce. */
+ krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
+ ctx->nonce &= 0xffffffff;
+ /* XXX these just needs to be the same when using Windows PK-INIT */
+ ctx->pk_nonce = ctx->nonce;
+
+ /*
+ * Increase counter when we want other pre-auth types then
+ * KRB5_PA_ENC_TIMESTAMP.
+ */
+#define MAX_PA_COUNTER 3
+
+ ctx->pa_counter = 0;
+ while (ctx->pa_counter < MAX_PA_COUNTER) {
+
+ ctx->pa_counter++;
+
+ if (ctx->as_req.padata) {
+ free_METHOD_DATA(ctx->as_req.padata);
+ free(ctx->as_req.padata);
+ ctx->as_req.padata = NULL;
+ }
+
+ /* Set a new nonce. */
+ ctx->as_req.req_body.nonce = ctx->nonce;
+
+ /* fill_in_md_data */
+ ret = process_pa_data_to_md(context, creds, &ctx->as_req, ctx,
+ &md, &ctx->as_req.padata,
+ prompter, prompter_data);
+ if (ret)
+ goto out;
+
+ krb5_data_free(&ctx->req_buffer);
+
+ ASN1_MALLOC_ENCODE(AS_REQ,
+ ctx->req_buffer.data, ctx->req_buffer.length,
+ &ctx->as_req, &len, ret);
+ if (ret)
+ goto out;
+ if(len != ctx->req_buffer.length)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_sendto_context (context, stctx, &ctx->req_buffer,
+ creds->client->realm, &resp);
+ if (ret)
+ goto out;
+
+ memset (&rep, 0, sizeof(rep));
+ ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
+ if (ret == 0) {
+ krb5_data_free(&resp);
+ krb5_clear_error_string(context);
+ break;
+ } else {
+ /* let's try to parse it as a KRB-ERROR */
+ KRB_ERROR error;
+
+ ret = krb5_rd_error(context, &resp, &error);
+ if(ret && resp.data && ((char*)resp.data)[0] == 4)
+ ret = KRB5KRB_AP_ERR_V4_REPLY;
+ krb5_data_free(&resp);
+ if (ret)
+ goto out;
+
+ ret = krb5_error_from_rd_error(context, &error, creds);
+
+ /*
+ * If no preauth was set and KDC requires it, give it one
+ * more try.
+ */
+
+ if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
+ free_METHOD_DATA(&md);
+ memset(&md, 0, sizeof(md));
+
+ if (error.e_data) {
+ ret = decode_METHOD_DATA(error.e_data->data,
+ error.e_data->length,
+ &md,
+ NULL);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ "failed to decode METHOD DATA");
+ } else {
+ /* XXX guess what the server want here add add md */
+ }
+ krb5_free_error_contents(context, &error);
+ if (ret)
+ goto out;
+ } else {
+ _krb5_get_init_creds_opt_set_krb5_error(context,
+ init_cred_opts,
+ &error);
+ if (ret_as_reply)
+ rep.error = error;
+ else
+ krb5_free_error_contents(context, &error);
+ goto out;
+ }
+ }
+ }
+
+ {
+ krb5_keyblock *key = NULL;
+ unsigned flags = EXTRACT_TICKET_AS_REQ;
+
+ if (ctx->flags.request_anonymous)
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+ if (ctx->flags.canonicalize) {
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+ flags |= EXTRACT_TICKET_MATCH_REALM;
+ }
+ if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
+ flags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
+
+ ret = process_pa_data_to_key(context, ctx, creds,
+ &ctx->as_req, &rep, hi, &key);
+ if (ret)
+ goto out;
+
+ ret = _krb5_extract_ticket(context,
+ &rep,
+ creds,
+ key,
+ NULL,
+ KRB5_KU_AS_REP_ENC_PART,
+ NULL,
+ ctx->nonce,
+ flags,
+ NULL,
+ NULL);
+ krb5_free_keyblock(context, key);
+ }
+out:
+ if (stctx)
+ krb5_sendto_ctx_free(context, stctx);
+ krb5_data_free(&ctx->req_buffer);
+ free_METHOD_DATA(&md);
+ memset(&md, 0, sizeof(md));
+
+ if (ret == 0 && ret_as_reply)
+ *ret_as_reply = rep;
+ else
+ krb5_free_kdc_rep (context, &rep);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_prompter_fct prompter,
+ void *data,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ krb5_get_init_creds_ctx ctx;
+ krb5_kdc_rep kdc_reply;
+ krb5_error_code ret;
+ char buf[BUFSIZ];
+ int done;
+
+ memset(&kdc_reply, 0, sizeof(kdc_reply));
+
+ ret = get_init_creds_common(context, client, start_time,
+ in_tkt_service, options, &ctx);
+ if (ret)
+ goto out;
+
+ done = 0;
+ while(!done) {
+ memset(&kdc_reply, 0, sizeof(kdc_reply));
+
+ ret = init_cred_loop(context,
+ options,
+ prompter,
+ data,
+ &ctx,
+ &ctx.cred,
+ &kdc_reply);
+
+ switch (ret) {
+ case 0 :
+ done = 1;
+ break;
+ case KRB5KDC_ERR_KEY_EXPIRED :
+ /* try to avoid recursion */
+
+ /* don't try to change password where then where none */
+ if (prompter == NULL || ctx.password == NULL)
+ goto out;
+
+ krb5_clear_error_string (context);
+
+ if (ctx.in_tkt_service != NULL
+ && strcmp (ctx.in_tkt_service, "kadmin/changepw") == 0)
+ goto out;
+
+ ret = change_password (context,
+ client,
+ ctx.password,
+ buf,
+ sizeof(buf),
+ prompter,
+ data,
+ options);
+ if (ret)
+ goto out;
+ ctx.password = buf;
+ break;
+ default:
+ goto out;
+ }
+ }
+
+ if (prompter)
+ print_expire (context,
+ krb5_principal_get_realm (context, ctx.cred.client),
+ &kdc_reply,
+ prompter,
+ data);
+
+ out:
+ memset (buf, 0, sizeof(buf));
+ free_init_creds_ctx(context, &ctx);
+ krb5_free_kdc_rep (context, &kdc_reply);
+ if (ret == 0)
+ *creds = ctx.cred;
+ else
+ krb5_free_cred_contents (context, &ctx.cred);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_password(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ const char *password,
+ krb5_prompter_fct prompter,
+ void *data,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *in_options)
+{
+ krb5_get_init_creds_opt *options;
+ char buf[BUFSIZ];
+ krb5_error_code ret;
+
+ if (in_options == NULL) {
+ const char *realm = krb5_principal_get_realm(context, client);
+ ret = krb5_get_init_creds_opt_alloc(context, &options);
+ if (ret == 0)
+ krb5_get_init_creds_opt_set_default_flags(context,
+ NULL,
+ realm,
+ options);
+ } else
+ ret = _krb5_get_init_creds_opt_copy(context, in_options, &options);
+ if (ret)
+ return ret;
+
+ if (password == NULL &&
+ options->opt_private->password == NULL &&
+ options->opt_private->pk_init_ctx == NULL)
+ {
+ krb5_prompt prompt;
+ krb5_data password_data;
+ char *p, *q;
+
+ krb5_unparse_name (context, client, &p);
+ asprintf (&q, "%s's Password: ", p);
+ free (p);
+ prompt.prompt = q;
+ password_data.data = buf;
+ password_data.length = sizeof(buf);
+ prompt.hidden = 1;
+ prompt.reply = &password_data;
+ prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
+
+ ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
+ free (q);
+ if (ret) {
+ memset (buf, 0, sizeof(buf));
+ krb5_get_init_creds_opt_free(context, options);
+ ret = KRB5_LIBOS_PWDINTR;
+ krb5_clear_error_string (context);
+ return ret;
+ }
+ password = password_data.data;
+ }
+
+ if (options->opt_private->password == NULL) {
+ ret = krb5_get_init_creds_opt_set_pa_password(context, options,
+ password, NULL);
+ if (ret) {
+ krb5_get_init_creds_opt_free(context, options);
+ memset(buf, 0, sizeof(buf));
+ return ret;
+ }
+ }
+
+ ret = krb5_get_init_creds(context, creds, client, prompter,
+ data, start_time, in_tkt_service, options);
+ krb5_get_init_creds_opt_free(context, options);
+ memset(buf, 0, sizeof(buf));
+ return ret;
+}
+
+static krb5_error_code
+init_creds_keyblock_key_proc (krb5_context context,
+ krb5_enctype type,
+ krb5_salt salt,
+ krb5_const_pointer keyseed,
+ krb5_keyblock **key)
+{
+ return krb5_copy_keyblock (context, keyseed, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_keyblock(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_keyblock *keyblock,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ struct krb5_get_init_creds_ctx ctx;
+ krb5_error_code ret;
+
+ ret = get_init_creds_common(context, client, start_time,
+ in_tkt_service, options, &ctx);
+ if (ret)
+ goto out;
+
+ ret = krb5_get_in_cred (context,
+ KDCOptions2int(ctx.flags),
+ ctx.addrs,
+ ctx.etypes,
+ ctx.pre_auth_types,
+ NULL,
+ init_creds_keyblock_key_proc,
+ keyblock,
+ NULL,
+ NULL,
+ &ctx.cred,
+ NULL);
+
+ if (ret == 0 && creds)
+ *creds = ctx.cred;
+ else
+ krb5_free_cred_contents (context, &ctx.cred);
+
+ out:
+ free_init_creds_ctx(context, &ctx);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/k524_err.et b/source4/heimdal/lib/krb5/k524_err.et
new file mode 100644
index 0000000000..4827b397af
--- /dev/null
+++ b/source4/heimdal/lib/krb5/k524_err.et
@@ -0,0 +1,20 @@
+#
+# Error messages for the k524 functions
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table k524
+
+prefix KRB524
+error_code BADKEY, "wrong keytype in ticket"
+error_code BADADDR, "incorrect network address"
+error_code BADPRINC, "cannot convert V5 principal" #unused
+error_code BADREALM, "V5 realm name longer than V4 maximum" #unused
+error_code V4ERR, "kerberos V4 error server"
+error_code ENCFULL, "encoding too large at server"
+error_code DECEMPTY, "decoding out of data" #unused
+error_code NOTRESP, "service not responding" #unused
+end
+
diff --git a/source4/heimdal/lib/krb5/kcm.c b/source4/heimdal/lib/krb5/kcm.c
new file mode 100644
index 0000000000..d5f38c5aaf
--- /dev/null
+++ b/source4/heimdal/lib/krb5/kcm.c
@@ -0,0 +1,1148 @@
+/*
+ * Copyright (c) 2005, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+#ifdef HAVE_KCM
+/*
+ * Client library for Kerberos Credentials Manager (KCM) daemon
+ */
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include "kcm.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_kcmcache {
+ char *name;
+ struct sockaddr_un path;
+ char *door_path;
+} krb5_kcmcache;
+
+#define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data)
+#define CACHENAME(X) (KCMCACHE(X)->name)
+#define KCMCURSOR(C) (*(uint32_t *)(C))
+
+static krb5_error_code
+try_door(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_data *request_data,
+ krb5_data *response_data)
+{
+#ifdef HAVE_DOOR_CREATE
+ door_arg_t arg;
+ int fd;
+ int ret;
+
+ memset(&arg, 0, sizeof(arg));
+
+ fd = open(k->door_path, O_RDWR);
+ if (fd < 0)
+ return KRB5_CC_IO;
+ rk_cloexec(fd);
+
+ arg.data_ptr = request_data->data;
+ arg.data_size = request_data->length;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = NULL;
+ arg.rsize = 0;
+
+ ret = door_call(fd, &arg);
+ close(fd);
+ if (ret != 0)
+ return KRB5_CC_IO;
+
+ ret = krb5_data_copy(response_data, arg.rbuf, arg.rsize);
+ munmap(arg.rbuf, arg.rsize);
+ if (ret)
+ return ret;
+
+ return 0;
+#else
+ return KRB5_CC_IO;
+#endif
+}
+
+static krb5_error_code
+try_unix_socket(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_data *request_data,
+ krb5_data *response_data)
+{
+ krb5_error_code ret;
+ int fd;
+
+ fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return KRB5_CC_IO;
+ rk_cloexec(fd);
+
+ if (connect(fd, rk_UNCONST(&k->path), sizeof(k->path)) != 0) {
+ close(fd);
+ return KRB5_CC_IO;
+ }
+
+ ret = _krb5_send_and_recv_tcp(fd, context->kdc_timeout,
+ request_data, response_data);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+kcm_send_request(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_storage *request,
+ krb5_data *response_data)
+{
+ krb5_error_code ret;
+ krb5_data request_data;
+ int i;
+
+ response_data->data = NULL;
+ response_data->length = 0;
+
+ ret = krb5_storage_to_data(request, &request_data);
+ if (ret) {
+ krb5_clear_error_string(context);
+ return KRB5_CC_NOMEM;
+ }
+
+ ret = KRB5_CC_NOSUPP;
+
+ for (i = 0; i < context->max_retries; i++) {
+ ret = try_door(context, k, &request_data, response_data);
+ if (ret == 0 && response_data->length != 0)
+ break;
+ ret = try_unix_socket(context, k, &request_data, response_data);
+ if (ret == 0 && response_data->length != 0)
+ break;
+ }
+
+ krb5_data_free(&request_data);
+
+ if (ret) {
+ krb5_clear_error_string(context);
+ ret = KRB5_CC_NOSUPP;
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+kcm_storage_request(krb5_context context,
+ kcm_operation opcode,
+ krb5_storage **storage_p)
+{
+ krb5_storage *sp;
+ krb5_error_code ret;
+
+ *storage_p = NULL;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+
+ /* Send MAJOR | VERSION | OPCODE */
+ ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
+ if (ret)
+ goto fail;
+ ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
+ if (ret)
+ goto fail;
+ ret = krb5_store_int16(sp, opcode);
+ if (ret)
+ goto fail;
+
+ *storage_p = sp;
+ fail:
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to encode request");
+ krb5_storage_free(sp);
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
+{
+ krb5_kcmcache *k;
+ const char *path;
+
+ k = malloc(sizeof(*k));
+ if (k == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+
+ if (name != NULL) {
+ k->name = strdup(name);
+ if (k->name == NULL) {
+ free(k);
+ krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ } else
+ k->name = NULL;
+
+ path = krb5_config_get_string_default(context, NULL,
+ _PATH_KCM_SOCKET,
+ "libdefaults",
+ "kcm_socket",
+ NULL);
+
+ k->path.sun_family = AF_UNIX;
+ strlcpy(k->path.sun_path, path, sizeof(k->path.sun_path));
+
+ path = krb5_config_get_string_default(context, NULL,
+ _PATH_KCM_DOOR,
+ "libdefaults",
+ "kcm_door",
+ NULL);
+ k->door_path = strdup(path);
+
+ (*id)->data.data = k;
+ (*id)->data.length = sizeof(*k);
+
+ return 0;
+}
+
+static krb5_error_code
+kcm_call(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_storage *request,
+ krb5_storage **response_p,
+ krb5_data *response_data_p)
+{
+ krb5_data response_data;
+ krb5_error_code ret;
+ int32_t status;
+ krb5_storage *response;
+
+ if (response_p != NULL)
+ *response_p = NULL;
+
+ ret = kcm_send_request(context, k, request, &response_data);
+ if (ret) {
+ return ret;
+ }
+
+ response = krb5_storage_from_data(&response_data);
+ if (response == NULL) {
+ krb5_data_free(&response_data);
+ return KRB5_CC_IO;
+ }
+
+ ret = krb5_ret_int32(response, &status);
+ if (ret) {
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+ return KRB5_CC_FORMAT;
+ }
+
+ if (status) {
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+ return status;
+ }
+
+ if (response_p != NULL) {
+ *response_data_p = response_data;
+ *response_p = response;
+
+ return 0;
+ }
+
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return 0;
+}
+
+static void
+kcm_free(krb5_context context, krb5_ccache *id)
+{
+ krb5_kcmcache *k = KCMCACHE(*id);
+
+ if (k != NULL) {
+ if (k->name != NULL)
+ free(k->name);
+ if (k->door_path)
+ free(k->door_path);
+ memset(k, 0, sizeof(*k));
+ krb5_data_free(&(*id)->data);
+ }
+
+ *id = NULL;
+}
+
+static const char *
+kcm_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return CACHENAME(id);
+}
+
+static krb5_error_code
+kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ return kcm_alloc(context, res, id);
+}
+
+/*
+ * Request:
+ *
+ * Response:
+ * NameZ
+ */
+static krb5_error_code
+kcm_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_kcmcache *k;
+ krb5_error_code ret;
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_alloc(context, NULL, id);
+ if (ret)
+ return ret;
+
+ k = KCMCACHE(*id);
+
+ ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
+ if (ret) {
+ kcm_free(context, id);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ kcm_free(context, id);
+ return ret;
+ }
+
+ ret = krb5_ret_stringz(response, &k->name);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ if (ret)
+ kcm_free(context, id);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Principal
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_principal(request, primary_principal);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static krb5_error_code
+kcm_close(krb5_context context,
+ krb5_ccache id)
+{
+ kcm_free(context, &id);
+ return 0;
+}
+
+/*
+ * Request:
+ * NameZ
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Creds
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_STORE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_creds(request, creds);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * WhichFields
+ * MatchCreds
+ *
+ * Response:
+ * Creds
+ *
+ */
+static krb5_error_code
+kcm_retrieve(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ const krb5_creds *mcred,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, which);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_creds(response, creds);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ *
+ * Response:
+ * Principal
+ */
+static krb5_error_code
+kcm_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_principal(response, principal);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ *
+ * Response:
+ * Cursor
+ *
+ */
+static krb5_error_code
+kcm_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+ int32_t tmp;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_int32(response, &tmp);
+ if (ret || tmp < 0)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ if (ret)
+ return ret;
+
+ *cursor = malloc(sizeof(tmp));
+ if (*cursor == NULL)
+ return KRB5_CC_NOMEM;
+
+ KCMCURSOR(*cursor) = tmp;
+
+ return 0;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Cursor
+ *
+ * Response:
+ * Creds
+ */
+static krb5_error_code
+kcm_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, KCMCURSOR(*cursor));
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_creds(response, creds);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Cursor
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, KCMCURSOR(*cursor));
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ krb5_storage_free(request);
+
+ KCMCURSOR(*cursor) = 0;
+ free(*cursor);
+ *cursor = NULL;
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * WhichFields
+ * MatchCreds
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, which);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_creds_tag(request, cred);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static krb5_error_code
+kcm_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, flags);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static int
+kcm_get_version(krb5_context context,
+ krb5_ccache id)
+{
+ return 0;
+}
+
+static krb5_error_code
+kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *oldk = KCMCACHE(from);
+ krb5_kcmcache *newk = KCMCACHE(to);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, oldk->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_stringz(request, newk->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+ ret = kcm_call(context, oldk, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static krb5_error_code
+kcm_default_name(krb5_context context, char **str)
+{
+ return _krb5_expand_default_cc_name(context,
+ KRB5_DEFAULT_CCNAME_KCM,
+ str);
+}
+
+/**
+ * Variable containing the KCM based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
+ KRB5_CC_OPS_VERSION,
+ "KCM",
+ kcm_get_name,
+ kcm_resolve,
+ kcm_gen_new,
+ kcm_initialize,
+ kcm_destroy,
+ kcm_close,
+ kcm_store_cred,
+ kcm_retrieve,
+ kcm_get_principal,
+ kcm_get_first,
+ kcm_get_next,
+ kcm_end_get,
+ kcm_remove_cred,
+ kcm_set_flags,
+ kcm_get_version,
+ NULL,
+ NULL,
+ NULL,
+ kcm_move,
+ kcm_default_name
+};
+
+krb5_boolean
+_krb5_kcm_is_running(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_ccache_data ccdata;
+ krb5_ccache id = &ccdata;
+ krb5_boolean running;
+
+ ret = kcm_alloc(context, NULL, &id);
+ if (ret)
+ return 0;
+
+ running = (_krb5_kcm_noop(context, id) == 0);
+
+ kcm_free(context, &id);
+
+ return running;
+}
+
+/*
+ * Request:
+ *
+ * Response:
+ *
+ */
+krb5_error_code
+_krb5_kcm_noop(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
+ if (ret)
+ return ret;
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * Mode
+ *
+ * Response:
+ *
+ */
+krb5_error_code
+_krb5_kcm_chmod(krb5_context context,
+ krb5_ccache id,
+ uint16_t mode)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int16(request, mode);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * UID
+ * GID
+ *
+ * Response:
+ *
+ */
+krb5_error_code
+_krb5_kcm_chown(krb5_context context,
+ krb5_ccache id,
+ uint32_t uid,
+ uint32_t gid)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, uid);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, gid);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * ServerPrincipalPresent
+ * ServerPrincipal OPTIONAL
+ * Key
+ *
+ * Repsonse:
+ *
+ */
+krb5_error_code
+_krb5_kcm_get_initial_ticket(krb5_context context,
+ krb5_ccache id,
+ krb5_principal server,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ if (server != NULL) {
+ ret = krb5_store_principal(request, server);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+ }
+
+ ret = krb5_store_keyblock(request, *key);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * KDCFlags
+ * EncryptionType
+ * ServerPrincipal
+ *
+ * Repsonse:
+ *
+ */
+krb5_error_code
+_krb5_kcm_get_ticket(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_enctype enctype,
+ krb5_principal server)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, flags.i);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, enctype);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_principal(request, server);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+#endif /* HAVE_KCM */
diff --git a/source4/heimdal/lib/krb5/keyblock.c b/source4/heimdal/lib/krb5/keyblock.c
new file mode 100644
index 0000000000..38a856624e
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keyblock.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+void KRB5_LIB_FUNCTION
+krb5_keyblock_zero(krb5_keyblock *keyblock)
+{
+ keyblock->keytype = 0;
+ krb5_data_zero(&keyblock->keyvalue);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_keyblock_contents(krb5_context context,
+ krb5_keyblock *keyblock)
+{
+ if(keyblock) {
+ if (keyblock->keyvalue.data != NULL)
+ memset(keyblock->keyvalue.data, 0, keyblock->keyvalue.length);
+ krb5_data_free (&keyblock->keyvalue);
+ keyblock->keytype = ENCTYPE_NULL;
+ }
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_keyblock(krb5_context context,
+ krb5_keyblock *keyblock)
+{
+ if(keyblock){
+ krb5_free_keyblock_contents(context, keyblock);
+ free(keyblock);
+ }
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_keyblock_contents (krb5_context context,
+ const krb5_keyblock *inblock,
+ krb5_keyblock *to)
+{
+ return copy_EncryptionKey(inblock, to);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_keyblock (krb5_context context,
+ const krb5_keyblock *inblock,
+ krb5_keyblock **to)
+{
+ krb5_keyblock *k;
+
+ k = malloc (sizeof(*k));
+ if (k == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *to = k;
+ return krb5_copy_keyblock_contents (context, inblock, k);
+}
+
+krb5_enctype
+krb5_keyblock_get_enctype(const krb5_keyblock *block)
+{
+ return block->keytype;
+}
+
+/*
+ * Fill in `key' with key data of type `enctype' from `data' of length
+ * `size'. Key should be freed using krb5_free_keyblock_contents.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keyblock_init(krb5_context context,
+ krb5_enctype type,
+ const void *data,
+ size_t size,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ size_t len;
+
+ memset(key, 0, sizeof(*key));
+
+ ret = krb5_enctype_keysize(context, type, &len);
+ if (ret)
+ return ret;
+
+ if (len != size) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "Encryption key %d is %lu bytes "
+ "long, %lu was passed in",
+ type, (unsigned long)len, (unsigned long)size);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ ret = krb5_data_copy(&key->keyvalue, data, len);
+ if(ret) {
+ krb5_set_error_message(context, ret, "malloc failed: %lu",
+ (unsigned long)len);
+ return ret;
+ }
+ key->keytype = type;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/keytab.c b/source4/heimdal/lib/krb5/keytab.c
new file mode 100644
index 0000000000..f3e6b9e8f4
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Register a new keytab in `ops'
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_register(krb5_context context,
+ const krb5_kt_ops *ops)
+{
+ struct krb5_keytab_data *tmp;
+
+ if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) {
+ krb5_set_error_message(context, KRB5_KT_BADNAME,
+ "krb5_kt_register; prefix too long");
+ return KRB5_KT_BADNAME;
+ }
+
+ tmp = realloc(context->kt_types,
+ (context->num_kt_types + 1) * sizeof(*context->kt_types));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(&tmp[context->num_kt_types], ops,
+ sizeof(tmp[context->num_kt_types]));
+ context->kt_types = tmp;
+ context->num_kt_types++;
+ return 0;
+}
+
+/*
+ * Resolve the keytab name (of the form `type:residual') in `name'
+ * into a keytab in `id'.
+ * Return 0 or an error
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_resolve(krb5_context context,
+ const char *name,
+ krb5_keytab *id)
+{
+ krb5_keytab k;
+ int i;
+ const char *type, *residual;
+ size_t type_len;
+ krb5_error_code ret;
+
+ residual = strchr(name, ':');
+ if(residual == NULL) {
+ type = "FILE";
+ type_len = strlen(type);
+ residual = name;
+ } else {
+ type = name;
+ type_len = residual - name;
+ residual++;
+ }
+
+ for(i = 0; i < context->num_kt_types; i++) {
+ if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0)
+ break;
+ }
+ if(i == context->num_kt_types) {
+ krb5_set_error_message(context, KRB5_KT_UNKNOWN_TYPE,
+ "unknown keytab type %.*s",
+ (int)type_len, type);
+ return KRB5_KT_UNKNOWN_TYPE;
+ }
+
+ k = malloc (sizeof(*k));
+ if (k == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(k, &context->kt_types[i], sizeof(*k));
+ k->data = NULL;
+ ret = (*k->resolve)(context, residual, k);
+ if(ret) {
+ free(k);
+ k = NULL;
+ }
+ *id = k;
+ return ret;
+}
+
+/*
+ * copy the name of the default keytab into `name'.
+ * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_default_name(krb5_context context, char *name, size_t namesize)
+{
+ if (strlcpy (name, context->default_keytab, namesize) >= namesize) {
+ krb5_clear_error_string (context);
+ return KRB5_CONFIG_NOTENUFSPACE;
+ }
+ return 0;
+}
+
+/*
+ * copy the name of the default modify keytab into `name'.
+ * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize)
+{
+ const char *kt = NULL;
+ if(context->default_keytab_modify == NULL) {
+ if(strncasecmp(context->default_keytab, "ANY:", 4) != 0)
+ kt = context->default_keytab;
+ else {
+ size_t len = strcspn(context->default_keytab + 4, ",");
+ if(len >= namesize) {
+ krb5_clear_error_string(context);
+ return KRB5_CONFIG_NOTENUFSPACE;
+ }
+ strlcpy(name, context->default_keytab + 4, namesize);
+ name[len] = '\0';
+ return 0;
+ }
+ } else
+ kt = context->default_keytab_modify;
+ if (strlcpy (name, kt, namesize) >= namesize) {
+ krb5_clear_error_string (context);
+ return KRB5_CONFIG_NOTENUFSPACE;
+ }
+ return 0;
+}
+
+/*
+ * Set `id' to the default keytab.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_default(krb5_context context, krb5_keytab *id)
+{
+ return krb5_kt_resolve (context, context->default_keytab, id);
+}
+
+/*
+ * Read the key identified by `(principal, vno, enctype)' from the
+ * keytab in `keyprocarg' (the default if == NULL) into `*key'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_read_service_key(krb5_context context,
+ krb5_pointer keyprocarg,
+ krb5_principal principal,
+ krb5_kvno vno,
+ krb5_enctype enctype,
+ krb5_keyblock **key)
+{
+ krb5_keytab keytab;
+ krb5_keytab_entry entry;
+ krb5_error_code ret;
+
+ if (keyprocarg)
+ ret = krb5_kt_resolve (context, keyprocarg, &keytab);
+ else
+ ret = krb5_kt_default (context, &keytab);
+
+ if (ret)
+ return ret;
+
+ ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
+ krb5_kt_close (context, keytab);
+ if (ret)
+ return ret;
+ ret = krb5_copy_keyblock (context, &entry.keyblock, key);
+ krb5_kt_free_entry(context, &entry);
+ return ret;
+}
+
+/*
+ * Return the type of the `keytab' in the string `prefix of length
+ * `prefixsize'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_type(krb5_context context,
+ krb5_keytab keytab,
+ char *prefix,
+ size_t prefixsize)
+{
+ strlcpy(prefix, keytab->prefix, prefixsize);
+ return 0;
+}
+
+/*
+ * Retrieve the name of the keytab `keytab' into `name', `namesize'
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_name(krb5_context context,
+ krb5_keytab keytab,
+ char *name,
+ size_t namesize)
+{
+ return (*keytab->get_name)(context, keytab, name, namesize);
+}
+
+/*
+ * Retrieve the full name of the keytab `keytab' and store the name in
+ * `str'. `str' needs to be freed by the caller using free(3).
+ * Returns 0 or an error. On error, *str is set to NULL.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_full_name(krb5_context context,
+ krb5_keytab keytab,
+ char **str)
+{
+ char type[KRB5_KT_PREFIX_MAX_LEN];
+ char name[MAXPATHLEN];
+ krb5_error_code ret;
+
+ *str = NULL;
+
+ ret = krb5_kt_get_type(context, keytab, type, sizeof(type));
+ if (ret)
+ return ret;
+
+ ret = krb5_kt_get_name(context, keytab, name, sizeof(name));
+ if (ret)
+ return ret;
+
+ if (asprintf(str, "%s:%s", type, name) == -1) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ *str = NULL;
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Finish using the keytab in `id'. All resources will be released,
+ * even on errors. Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_close(krb5_context context,
+ krb5_keytab id)
+{
+ krb5_error_code ret;
+
+ ret = (*id->close)(context, id);
+ memset(id, 0, sizeof(*id));
+ free(id);
+ return ret;
+}
+
+/*
+ * Compare `entry' against `principal, vno, enctype'.
+ * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
+ * Return TRUE if they compare the same, FALSE otherwise.
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_kt_compare(krb5_context context,
+ krb5_keytab_entry *entry,
+ krb5_const_principal principal,
+ krb5_kvno vno,
+ krb5_enctype enctype)
+{
+ if(principal != NULL &&
+ !krb5_principal_compare(context, entry->principal, principal))
+ return FALSE;
+ if(vno && vno != entry->vno)
+ return FALSE;
+ if(enctype && enctype != entry->keyblock.keytype)
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
+ * from the keytab `id'.
+ * kvno == 0 is a wildcard and gives the keytab with the highest vno.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_const_principal principal,
+ krb5_kvno kvno,
+ krb5_enctype enctype,
+ krb5_keytab_entry *entry)
+{
+ krb5_keytab_entry tmp;
+ krb5_error_code ret;
+ krb5_kt_cursor cursor;
+
+ if(id->get)
+ return (*id->get)(context, id, principal, kvno, enctype, entry);
+
+ ret = krb5_kt_start_seq_get (context, id, &cursor);
+ if (ret) {
+ /* This is needed for krb5_verify_init_creds, but keep error
+ * string from previous error for the human. */
+ context->error_code = KRB5_KT_NOTFOUND;
+ return KRB5_KT_NOTFOUND;
+ }
+
+ entry->vno = 0;
+ while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
+ if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
+ /* the file keytab might only store the lower 8 bits of
+ the kvno, so only compare those bits */
+ if (kvno == tmp.vno
+ || (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
+ krb5_kt_copy_entry_contents (context, &tmp, entry);
+ krb5_kt_free_entry (context, &tmp);
+ krb5_kt_end_seq_get(context, id, &cursor);
+ return 0;
+ } else if (kvno == 0 && tmp.vno > entry->vno) {
+ if (entry->vno)
+ krb5_kt_free_entry (context, entry);
+ krb5_kt_copy_entry_contents (context, &tmp, entry);
+ }
+ }
+ krb5_kt_free_entry(context, &tmp);
+ }
+ krb5_kt_end_seq_get (context, id, &cursor);
+ if (entry->vno) {
+ return 0;
+ } else {
+ char princ[256], kvno_str[25], *kt_name;
+ char *enctype_str = NULL;
+
+ krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
+ krb5_kt_get_full_name (context, id, &kt_name);
+ krb5_enctype_to_string(context, enctype, &enctype_str);
+
+ if (kvno)
+ snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
+ else
+ kvno_str[0] = '\0';
+
+ krb5_set_error_message (context, KRB5_KT_NOTFOUND,
+ "Failed to find %s%s in keytab %s (%s)",
+ princ,
+ kvno_str,
+ kt_name ? kt_name : "unknown keytab",
+ enctype_str ? enctype_str : "unknown enctype");
+ free(kt_name);
+ free(enctype_str);
+ return KRB5_KT_NOTFOUND;
+ }
+}
+
+/*
+ * Copy the contents of `in' into `out'.
+ * Return 0 or an error. */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_copy_entry_contents(krb5_context context,
+ const krb5_keytab_entry *in,
+ krb5_keytab_entry *out)
+{
+ krb5_error_code ret;
+
+ memset(out, 0, sizeof(*out));
+ out->vno = in->vno;
+
+ ret = krb5_copy_principal (context, in->principal, &out->principal);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_keyblock_contents (context,
+ &in->keyblock,
+ &out->keyblock);
+ if (ret)
+ goto fail;
+ out->timestamp = in->timestamp;
+ return 0;
+fail:
+ krb5_kt_free_entry (context, out);
+ return ret;
+}
+
+/*
+ * Free the contents of `entry'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_free_entry(krb5_context context,
+ krb5_keytab_entry *entry)
+{
+ krb5_free_principal (context, entry->principal);
+ krb5_free_keyblock_contents (context, &entry->keyblock);
+ memset(entry, 0, sizeof(*entry));
+ return 0;
+}
+
+/*
+ * Set `cursor' to point at the beginning of `id'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ if(id->start_seq_get == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
+ "start_seq_get is not supported in the %s "
+ " keytab", id->prefix);
+ return HEIM_ERR_OPNOTSUPP;
+ }
+ return (*id->start_seq_get)(context, id, cursor);
+}
+
+/*
+ * Get the next entry from `id' pointed to by `cursor' and advance the
+ * `cursor'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ if(id->next_entry == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
+ "next_entry is not supported in the %s "
+ " keytab", id->prefix);
+ return HEIM_ERR_OPNOTSUPP;
+ }
+ return (*id->next_entry)(context, id, entry, cursor);
+}
+
+/*
+ * Release all resources associated with `cursor'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ if(id->end_seq_get == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
+ "end_seq_get is not supported in the %s "
+ " keytab", id->prefix);
+ return HEIM_ERR_OPNOTSUPP;
+ }
+ return (*id->end_seq_get)(context, id, cursor);
+}
+
+/*
+ * Add the entry in `entry' to the keytab `id'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ if(id->add == NULL) {
+ krb5_set_error_message(context, KRB5_KT_NOWRITE,
+ "Add is not supported in the %s keytab",
+ id->prefix);
+ return KRB5_KT_NOWRITE;
+ }
+ entry->timestamp = time(NULL);
+ return (*id->add)(context, id,entry);
+}
+
+/*
+ * Remove the entry `entry' from the keytab `id'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ if(id->remove == NULL) {
+ krb5_set_error_message(context, KRB5_KT_NOWRITE,
+ "Remove is not supported in the %s keytab",
+ id->prefix);
+ return KRB5_KT_NOWRITE;
+ }
+ return (*id->remove)(context, id, entry);
+}
diff --git a/source4/heimdal/lib/krb5/keytab_any.c b/source4/heimdal/lib/krb5/keytab_any.c
new file mode 100644
index 0000000000..a4b15394a5
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_any.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2001-2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct any_data {
+ krb5_keytab kt;
+ char *name;
+ struct any_data *next;
+};
+
+static void
+free_list (krb5_context context, struct any_data *a)
+{
+ struct any_data *next;
+
+ for (; a != NULL; a = next) {
+ next = a->next;
+ free (a->name);
+ if(a->kt)
+ krb5_kt_close(context, a->kt);
+ free (a);
+ }
+}
+
+static krb5_error_code
+any_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct any_data *a, *a0 = NULL, *prev = NULL;
+ krb5_error_code ret;
+ char buf[256];
+
+ while (strsep_copy(&name, ",", buf, sizeof(buf)) != -1) {
+ a = malloc(sizeof(*a));
+ if (a == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ if (a0 == NULL) {
+ a0 = a;
+ a->name = strdup(buf);
+ if (a->name == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto fail;
+ }
+ } else
+ a->name = NULL;
+ if (prev != NULL)
+ prev->next = a;
+ a->next = NULL;
+ ret = krb5_kt_resolve (context, buf, &a->kt);
+ if (ret)
+ goto fail;
+ prev = a;
+ }
+ if (a0 == NULL) {
+ krb5_set_error_message(context, ENOENT, "empty ANY: keytab");
+ return ENOENT;
+ }
+ id->data = a0;
+ return 0;
+ fail:
+ free_list (context, a0);
+ return ret;
+}
+
+static krb5_error_code
+any_get_name (krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ struct any_data *a = id->data;
+ strlcpy(name, a->name, namesize);
+ return 0;
+}
+
+static krb5_error_code
+any_close (krb5_context context,
+ krb5_keytab id)
+{
+ struct any_data *a = id->data;
+
+ free_list (context, a);
+ return 0;
+}
+
+struct any_cursor_extra_data {
+ struct any_data *a;
+ krb5_kt_cursor cursor;
+};
+
+static krb5_error_code
+any_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ struct any_data *a = id->data;
+ struct any_cursor_extra_data *ed;
+ krb5_error_code ret;
+
+ c->data = malloc (sizeof(struct any_cursor_extra_data));
+ if(c->data == NULL){
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ed = (struct any_cursor_extra_data *)c->data;
+ ed->a = a;
+ ret = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor);
+ if (ret) {
+ free (c->data);
+ c->data = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+any_next_entry (krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ krb5_error_code ret, ret2;
+ struct any_cursor_extra_data *ed;
+
+ ed = (struct any_cursor_extra_data *)cursor->data;
+ do {
+ ret = krb5_kt_next_entry(context, ed->a->kt, entry, &ed->cursor);
+ if (ret == 0)
+ return 0;
+ else if (ret != KRB5_KT_END)
+ return ret;
+
+ ret2 = krb5_kt_end_seq_get (context, ed->a->kt, &ed->cursor);
+ if (ret2)
+ return ret2;
+ while ((ed->a = ed->a->next) != NULL) {
+ ret2 = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor);
+ if (ret2 == 0)
+ break;
+ }
+ if (ed->a == NULL) {
+ krb5_clear_error_string (context);
+ return KRB5_KT_END;
+ }
+ } while (1);
+}
+
+static krb5_error_code
+any_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_error_code ret = 0;
+ struct any_cursor_extra_data *ed;
+
+ ed = (struct any_cursor_extra_data *)cursor->data;
+ if (ed->a != NULL)
+ ret = krb5_kt_end_seq_get(context, ed->a->kt, &ed->cursor);
+ free (ed);
+ cursor->data = NULL;
+ return ret;
+}
+
+static krb5_error_code
+any_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct any_data *a = id->data;
+ krb5_error_code ret;
+ while(a != NULL) {
+ ret = krb5_kt_add_entry(context, a->kt, entry);
+ if(ret != 0 && ret != KRB5_KT_NOWRITE) {
+ krb5_set_error_message(context, ret, "failed to add entry to %s",
+ a->name);
+ return ret;
+ }
+ a = a->next;
+ }
+ return 0;
+}
+
+static krb5_error_code
+any_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct any_data *a = id->data;
+ krb5_error_code ret;
+ int found = 0;
+ while(a != NULL) {
+ ret = krb5_kt_remove_entry(context, a->kt, entry);
+ if(ret == 0)
+ found++;
+ else {
+ if(ret != KRB5_KT_NOWRITE && ret != KRB5_KT_NOTFOUND) {
+ krb5_set_error_message(context, ret,
+ "Failed to remove keytab entry from %s",
+ a->name);
+ return ret;
+ }
+ }
+ a = a->next;
+ }
+ if(!found)
+ return KRB5_KT_NOTFOUND;
+ return 0;
+}
+
+const krb5_kt_ops krb5_any_ops = {
+ "ANY",
+ any_resolve,
+ any_get_name,
+ any_close,
+ NULL, /* get */
+ any_start_seq_get,
+ any_next_entry,
+ any_end_seq_get,
+ any_add_entry,
+ any_remove_entry
+};
diff --git a/source4/heimdal/lib/krb5/keytab_file.c b/source4/heimdal/lib/krb5/keytab_file.c
new file mode 100644
index 0000000000..17f2d57742
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_file.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#define KRB5_KT_VNO_1 1
+#define KRB5_KT_VNO_2 2
+#define KRB5_KT_VNO KRB5_KT_VNO_2
+
+#define KRB5_KT_FL_JAVA 1
+
+
+/* file operations -------------------------------------------- */
+
+struct fkt_data {
+ char *filename;
+ int flags;
+};
+
+static krb5_error_code
+krb5_kt_ret_data(krb5_context context,
+ krb5_storage *sp,
+ krb5_data *data)
+{
+ int ret;
+ int16_t size;
+ ret = krb5_ret_int16(sp, &size);
+ if(ret)
+ return ret;
+ data->length = size;
+ data->data = malloc(size);
+ if (data->data == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = krb5_storage_read(sp, data->data, size);
+ if(ret != size)
+ return (ret < 0)? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_ret_string(krb5_context context,
+ krb5_storage *sp,
+ heim_general_string *data)
+{
+ int ret;
+ int16_t size;
+ ret = krb5_ret_int16(sp, &size);
+ if(ret)
+ return ret;
+ *data = malloc(size + 1);
+ if (*data == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = krb5_storage_read(sp, *data, size);
+ (*data)[size] = '\0';
+ if(ret != size)
+ return (ret < 0)? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_store_data(krb5_context context,
+ krb5_storage *sp,
+ krb5_data data)
+{
+ int ret;
+ ret = krb5_store_int16(sp, data.length);
+ if(ret < 0)
+ return ret;
+ ret = krb5_storage_write(sp, data.data, data.length);
+ if(ret != data.length){
+ if(ret < 0)
+ return errno;
+ return KRB5_KT_END;
+ }
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_store_string(krb5_storage *sp,
+ heim_general_string data)
+{
+ int ret;
+ size_t len = strlen(data);
+ ret = krb5_store_int16(sp, len);
+ if(ret < 0)
+ return ret;
+ ret = krb5_storage_write(sp, data, len);
+ if(ret != len){
+ if(ret < 0)
+ return errno;
+ return KRB5_KT_END;
+ }
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_ret_keyblock(krb5_context context, krb5_storage *sp, krb5_keyblock *p)
+{
+ int ret;
+ int16_t tmp;
+
+ ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
+ if(ret) return ret;
+ p->keytype = tmp;
+ ret = krb5_kt_ret_data(context, sp, &p->keyvalue);
+ return ret;
+}
+
+static krb5_error_code
+krb5_kt_store_keyblock(krb5_context context,
+ krb5_storage *sp,
+ krb5_keyblock *p)
+{
+ int ret;
+
+ ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
+ if(ret) return ret;
+ ret = krb5_kt_store_data(context, sp, p->keyvalue);
+ return ret;
+}
+
+
+static krb5_error_code
+krb5_kt_ret_principal(krb5_context context,
+ krb5_storage *sp,
+ krb5_principal *princ)
+{
+ int i;
+ int ret;
+ krb5_principal p;
+ int16_t len;
+
+ ALLOC(p, 1);
+ if(p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = krb5_ret_int16(sp, &len);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ "Failed decoding length of keytab principal");
+ goto out;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ len--;
+ if (len < 0) {
+ ret = KRB5_KT_END;
+ krb5_set_error_message(context, ret,
+ "Keytab principal contains invalid length");
+ goto out;
+ }
+ ret = krb5_kt_ret_string(context, sp, &p->realm);
+ if(ret)
+ goto out;
+ p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
+ if(p->name.name_string.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ p->name.name_string.len = len;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i);
+ if(ret)
+ goto out;
+ }
+ if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
+ p->name.name_type = KRB5_NT_UNKNOWN;
+ else {
+ int32_t tmp32;
+ ret = krb5_ret_int32(sp, &tmp32);
+ p->name.name_type = tmp32;
+ if (ret)
+ goto out;
+ }
+ *princ = p;
+ return 0;
+out:
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+static krb5_error_code
+krb5_kt_store_principal(krb5_context context,
+ krb5_storage *sp,
+ krb5_principal p)
+{
+ int i;
+ int ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ret = krb5_store_int16(sp, p->name.name_string.len + 1);
+ else
+ ret = krb5_store_int16(sp, p->name.name_string.len);
+ if(ret) return ret;
+ ret = krb5_kt_store_string(sp, p->realm);
+ if(ret) return ret;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
+ if(ret)
+ return ret;
+ }
+ if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
+ ret = krb5_store_int32(sp, p->name.name_type);
+ if(ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct fkt_data *d;
+
+ d = malloc(sizeof(*d));
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ d->filename = strdup(name);
+ if(d->filename == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ d->flags = 0;
+ id->data = d;
+ return 0;
+}
+
+static krb5_error_code
+fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
+{
+ krb5_error_code ret;
+
+ ret = fkt_resolve(context, name, id);
+ if (ret == 0) {
+ struct fkt_data *d = id->data;
+ d->flags |= KRB5_KT_FL_JAVA;
+ }
+ return ret;
+}
+
+static krb5_error_code
+fkt_close(krb5_context context, krb5_keytab id)
+{
+ struct fkt_data *d = id->data;
+ free(d->filename);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+fkt_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ /* This function is XXX */
+ struct fkt_data *d = id->data;
+ strlcpy(name, d->filename, namesize);
+ return 0;
+}
+
+static void
+storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
+{
+ int flags = 0;
+ switch(vno) {
+ case KRB5_KT_VNO_1:
+ flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
+ flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
+ flags |= KRB5_STORAGE_HOST_BYTEORDER;
+ break;
+ case KRB5_KT_VNO_2:
+ break;
+ default:
+ krb5_warnx(context,
+ "storage_set_flags called with bad vno (%d)", vno);
+ }
+ krb5_storage_set_flags(sp, flags);
+}
+
+static krb5_error_code
+fkt_start_seq_get_int(krb5_context context,
+ krb5_keytab id,
+ int flags,
+ int exclusive,
+ krb5_kt_cursor *c)
+{
+ int8_t pvno, tag;
+ krb5_error_code ret;
+ struct fkt_data *d = id->data;
+
+ c->fd = open (d->filename, flags);
+ if (c->fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "keytab %s open failed: %s",
+ d->filename, strerror(ret));
+ return ret;
+ }
+ rk_cloexec(c->fd);
+ ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
+ if (ret) {
+ close(c->fd);
+ return ret;
+ }
+ c->sp = krb5_storage_from_fd(c->fd);
+ if (c->sp == NULL) {
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
+ ret = krb5_ret_int8(c->sp, &pvno);
+ if(ret) {
+ krb5_storage_free(c->sp);
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_clear_error_string(context);
+ return ret;
+ }
+ if(pvno != 5) {
+ krb5_storage_free(c->sp);
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_clear_error_string (context);
+ return KRB5_KEYTAB_BADVNO;
+ }
+ ret = krb5_ret_int8(c->sp, &tag);
+ if (ret) {
+ krb5_storage_free(c->sp);
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_clear_error_string(context);
+ return ret;
+ }
+ id->version = tag;
+ storage_set_flags(context, c->sp, id->version);
+ return 0;
+}
+
+static krb5_error_code
+fkt_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY | O_CLOEXEC, 0, c);
+}
+
+static krb5_error_code
+fkt_next_entry_int(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor,
+ off_t *start,
+ off_t *end)
+{
+ int32_t len;
+ int ret;
+ int8_t tmp8;
+ int32_t tmp32;
+ off_t pos, curpos;
+
+ pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+loop:
+ ret = krb5_ret_int32(cursor->sp, &len);
+ if (ret)
+ return ret;
+ if(len < 0) {
+ pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
+ goto loop;
+ }
+ ret = krb5_kt_ret_principal (context, cursor->sp, &entry->principal);
+ if (ret)
+ goto out;
+ ret = krb5_ret_int32(cursor->sp, &tmp32);
+ entry->timestamp = tmp32;
+ if (ret)
+ goto out;
+ ret = krb5_ret_int8(cursor->sp, &tmp8);
+ if (ret)
+ goto out;
+ entry->vno = tmp8;
+ ret = krb5_kt_ret_keyblock (context, cursor->sp, &entry->keyblock);
+ if (ret)
+ goto out;
+ /* there might be a 32 bit kvno here
+ * if it's zero, assume that the 8bit one was right,
+ * otherwise trust the new value */
+ curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+ if(len + 4 + pos - curpos >= 4) {
+ ret = krb5_ret_int32(cursor->sp, &tmp32);
+ if (ret == 0 && tmp32 != 0) {
+ entry->vno = tmp32;
+ }
+ }
+ if(start) *start = pos;
+ if(end) *end = pos + 4 + len;
+ out:
+ krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
+ return ret;
+}
+
+static krb5_error_code
+fkt_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
+}
+
+static krb5_error_code
+fkt_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_storage_free(cursor->sp);
+ _krb5_xunlock(context, cursor->fd);
+ close(cursor->fd);
+ return 0;
+}
+
+static krb5_error_code
+fkt_setup_keytab(krb5_context context,
+ krb5_keytab id,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+ ret = krb5_store_int8(sp, 5);
+ if(ret)
+ return ret;
+ if(id->version == 0)
+ id->version = KRB5_KT_VNO;
+ return krb5_store_int8 (sp, id->version);
+}
+
+static krb5_error_code
+fkt_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ int ret;
+ int fd;
+ krb5_storage *sp;
+ struct fkt_data *d = id->data;
+ krb5_data keytab;
+ int32_t len;
+
+ fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
+ if (fd < 0) {
+ fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", d->filename,
+ strerror(ret));
+ return ret;
+ }
+ rk_cloexec(fd);
+
+ ret = _krb5_xlock(context, fd, 1, d->filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ sp = krb5_storage_from_fd(fd);
+ krb5_storage_set_eof_code(sp, KRB5_KT_END);
+ ret = fkt_setup_keytab(context, id, sp);
+ if(ret) {
+ goto out;
+ }
+ storage_set_flags(context, sp, id->version);
+ } else {
+ int8_t pvno, tag;
+
+ rk_cloexec(fd);
+
+ ret = _krb5_xlock(context, fd, 1, d->filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ sp = krb5_storage_from_fd(fd);
+ krb5_storage_set_eof_code(sp, KRB5_KT_END);
+ ret = krb5_ret_int8(sp, &pvno);
+ if(ret) {
+ /* we probably have a zero byte file, so try to set it up
+ properly */
+ ret = fkt_setup_keytab(context, id, sp);
+ if(ret) {
+ krb5_set_error_message(context, ret, "%s: keytab is corrupted: %s",
+ d->filename, strerror(ret));
+ goto out;
+ }
+ storage_set_flags(context, sp, id->version);
+ } else {
+ if(pvno != 5) {
+ ret = KRB5_KEYTAB_BADVNO;
+ krb5_set_error_message(context, ret, "%s: %s",
+ d->filename, strerror(ret));
+ goto out;
+ }
+ ret = krb5_ret_int8 (sp, &tag);
+ if (ret) {
+ krb5_set_error_message(context, ret, "%s: reading tag: %s",
+ d->filename, strerror(ret));
+ goto out;
+ }
+ id->version = tag;
+ storage_set_flags(context, sp, id->version);
+ }
+ }
+
+ {
+ krb5_storage *emem;
+ emem = krb5_storage_emem();
+ if(emem == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = krb5_kt_store_principal(context, emem, entry->principal);
+ if(ret) {
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_store_int32 (emem, entry->timestamp);
+ if(ret) {
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_store_int8 (emem, entry->vno % 256);
+ if(ret) {
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_kt_store_keyblock (context, emem, &entry->keyblock);
+ if(ret) {
+ krb5_storage_free(emem);
+ goto out;
+ }
+ if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
+ ret = krb5_store_int32 (emem, entry->vno);
+ if (ret) {
+ krb5_storage_free(emem);
+ goto out;
+ }
+ }
+
+ ret = krb5_storage_to_data(emem, &keytab);
+ krb5_storage_free(emem);
+ if(ret)
+ goto out;
+ }
+
+ while(1) {
+ ret = krb5_ret_int32(sp, &len);
+ if(ret == KRB5_KT_END) {
+ len = keytab.length;
+ break;
+ }
+ if(len < 0) {
+ len = -len;
+ if(len >= keytab.length) {
+ krb5_storage_seek(sp, -4, SEEK_CUR);
+ break;
+ }
+ }
+ krb5_storage_seek(sp, len, SEEK_CUR);
+ }
+ ret = krb5_store_int32(sp, len);
+ if(krb5_storage_write(sp, keytab.data, keytab.length) < 0)
+ ret = errno;
+ memset(keytab.data, 0, keytab.length);
+ krb5_data_free(&keytab);
+ out:
+ krb5_storage_free(sp);
+ _krb5_xunlock(context, fd);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+fkt_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ krb5_keytab_entry e;
+ krb5_kt_cursor cursor;
+ off_t pos_start, pos_end;
+ int found = 0;
+ krb5_error_code ret;
+
+ ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor);
+ if(ret != 0)
+ goto out; /* return other error here? */
+ while(fkt_next_entry_int(context, id, &e, &cursor,
+ &pos_start, &pos_end) == 0) {
+ if(krb5_kt_compare(context, &e, entry->principal,
+ entry->vno, entry->keyblock.keytype)) {
+ int32_t len;
+ unsigned char buf[128];
+ found = 1;
+ krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
+ len = pos_end - pos_start - 4;
+ krb5_store_int32(cursor.sp, -len);
+ memset(buf, 0, sizeof(buf));
+ while(len > 0) {
+ krb5_storage_write(cursor.sp, buf, min(len, sizeof(buf)));
+ len -= min(len, sizeof(buf));
+ }
+ }
+ krb5_kt_free_entry(context, &e);
+ }
+ krb5_kt_end_seq_get(context, id, &cursor);
+ out:
+ if (!found) {
+ krb5_clear_error_string (context);
+ return KRB5_KT_NOTFOUND;
+ }
+ return 0;
+}
+
+const krb5_kt_ops krb5_fkt_ops = {
+ "FILE",
+ fkt_resolve,
+ fkt_get_name,
+ fkt_close,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry
+};
+
+const krb5_kt_ops krb5_wrfkt_ops = {
+ "WRFILE",
+ fkt_resolve,
+ fkt_get_name,
+ fkt_close,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry
+};
+
+const krb5_kt_ops krb5_javakt_ops = {
+ "JAVA14",
+ fkt_resolve_java14,
+ fkt_get_name,
+ fkt_close,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry
+};
diff --git a/source4/heimdal/lib/krb5/keytab_keyfile.c b/source4/heimdal/lib/krb5/keytab_keyfile.c
new file mode 100644
index 0000000000..3339a96319
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_keyfile.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/* afs keyfile operations --------------------------------------- */
+
+/*
+ * Minimum tools to handle the AFS KeyFile.
+ *
+ * Format of the KeyFile is:
+ * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
+ *
+ * It just adds to the end of the keyfile, deleting isn't implemented.
+ * Use your favorite text/hex editor to delete keys.
+ *
+ */
+
+#define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
+#define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
+
+struct akf_data {
+ uint32_t num_entries;
+ char *filename;
+ char *cell;
+ char *realm;
+};
+
+/*
+ * set `d->cell' and `d->realm'
+ */
+
+static int
+get_cell_and_realm (krb5_context context, struct akf_data *d)
+{
+ FILE *f;
+ char buf[BUFSIZ], *cp;
+ int ret;
+
+ f = fopen (AFS_SERVERTHISCELL, "r");
+ if (f == NULL) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "open %s: %s", AFS_SERVERTHISCELL,
+ strerror(ret));
+ return ret;
+ }
+ if (fgets (buf, sizeof(buf), f) == NULL) {
+ fclose (f);
+ krb5_set_error_message (context, EINVAL, "no cell in %s", AFS_SERVERTHISCELL);
+ return EINVAL;
+ }
+ buf[strcspn(buf, "\n")] = '\0';
+ fclose(f);
+
+ d->cell = strdup (buf);
+ if (d->cell == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ f = fopen (AFS_SERVERMAGICKRBCONF, "r");
+ if (f != NULL) {
+ if (fgets (buf, sizeof(buf), f) == NULL) {
+ free (d->cell);
+ d->cell = NULL;
+ fclose (f);
+ krb5_set_error_message (context, EINVAL, "no realm in %s",
+ AFS_SERVERMAGICKRBCONF);
+ return EINVAL;
+ }
+ buf[strcspn(buf, "\n")] = '\0';
+ fclose(f);
+ }
+ /* uppercase */
+ for (cp = buf; *cp != '\0'; cp++)
+ *cp = toupper((unsigned char)*cp);
+
+ d->realm = strdup (buf);
+ if (d->realm == NULL) {
+ free (d->cell);
+ d->cell = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * init and get filename
+ */
+
+static krb5_error_code
+akf_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ int ret;
+ struct akf_data *d = malloc(sizeof (struct akf_data));
+
+ if (d == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ d->num_entries = 0;
+ ret = get_cell_and_realm (context, d);
+ if (ret) {
+ free (d);
+ return ret;
+ }
+ d->filename = strdup (name);
+ if (d->filename == NULL) {
+ free (d->cell);
+ free (d->realm);
+ free (d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ id->data = d;
+
+ return 0;
+}
+
+/*
+ * cleanup
+ */
+
+static krb5_error_code
+akf_close(krb5_context context, krb5_keytab id)
+{
+ struct akf_data *d = id->data;
+
+ free (d->filename);
+ free (d->cell);
+ free (d);
+ return 0;
+}
+
+/*
+ * Return filename
+ */
+
+static krb5_error_code
+akf_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t name_sz)
+{
+ struct akf_data *d = id->data;
+
+ strlcpy (name, d->filename, name_sz);
+ return 0;
+}
+
+/*
+ * Init
+ */
+
+static krb5_error_code
+akf_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ int32_t ret;
+ struct akf_data *d = id->data;
+
+ c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600);
+ if (c->fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "keytab afs keyfil open %s failed: %s",
+ d->filename, strerror(ret));
+ return ret;
+ }
+
+ c->sp = krb5_storage_from_fd(c->fd);
+ ret = krb5_ret_uint32(c->sp, &d->num_entries);
+ if(ret) {
+ krb5_storage_free(c->sp);
+ close(c->fd);
+ krb5_clear_error_string (context);
+ if(ret == KRB5_KT_END)
+ return KRB5_KT_NOTFOUND;
+ return ret;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+akf_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ struct akf_data *d = id->data;
+ int32_t kvno;
+ off_t pos;
+ int ret;
+
+ pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+
+ if ((pos - 4) / (4 + 8) >= d->num_entries)
+ return KRB5_KT_END;
+
+ ret = krb5_make_principal (context, &entry->principal,
+ d->realm, "afs", d->cell, NULL);
+ if (ret)
+ goto out;
+
+ ret = krb5_ret_int32(cursor->sp, &kvno);
+ if (ret) {
+ krb5_free_principal (context, entry->principal);
+ goto out;
+ }
+
+ entry->vno = kvno;
+
+ entry->keyblock.keytype = ETYPE_DES_CBC_MD5;
+ entry->keyblock.keyvalue.length = 8;
+ entry->keyblock.keyvalue.data = malloc (8);
+ if (entry->keyblock.keyvalue.data == NULL) {
+ krb5_free_principal (context, entry->principal);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
+ if(ret != 8)
+ ret = (ret < 0) ? errno : KRB5_KT_END;
+ else
+ ret = 0;
+
+ entry->timestamp = time(NULL);
+
+ out:
+ krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
+ return ret;
+}
+
+static krb5_error_code
+akf_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_storage_free(cursor->sp);
+ close(cursor->fd);
+ return 0;
+}
+
+static krb5_error_code
+akf_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct akf_data *d = id->data;
+ int fd, created = 0;
+ krb5_error_code ret;
+ int32_t len;
+ krb5_storage *sp;
+
+
+ if (entry->keyblock.keyvalue.length != 8)
+ return 0;
+ switch(entry->keyblock.keytype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ break;
+ default:
+ return 0;
+ }
+
+ fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
+ if (fd < 0) {
+ fd = open (d->filename,
+ O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", d->filename,
+ strerror(ret));
+ return ret;
+ }
+ created = 1;
+ }
+
+ sp = krb5_storage_from_fd(fd);
+ if(sp == NULL) {
+ close(fd);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ if (created)
+ len = 0;
+ else {
+ if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
+ ret = errno;
+ krb5_storage_free(sp);
+ close(fd);
+ krb5_set_error_message(context, ret, "seek: %s", strerror(ret));
+ return ret;
+ }
+
+ ret = krb5_ret_int32(sp, &len);
+ if(ret) {
+ krb5_storage_free(sp);
+ close(fd);
+ return ret;
+ }
+ }
+
+ /*
+ * Make sure we don't add the entry twice, assumes the DES
+ * encryption types are all the same key.
+ */
+ if (len > 0) {
+ int32_t kvno;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ ret = krb5_ret_int32(sp, &kvno);
+ if (ret) {
+ krb5_set_error_message (context, ret, "Failed to get kvno ");
+ goto out;
+ }
+ if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "seek: %s", strerror(ret));
+ goto out;
+ }
+ if (kvno == entry->vno) {
+ ret = 0;
+ goto out;
+ }
+ }
+ }
+
+ len++;
+
+ if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "seek: %s", strerror(ret));
+ goto out;
+ }
+
+ ret = krb5_store_int32(sp, len);
+ if(ret) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "keytab keyfile failed new length");
+ return ret;
+ }
+
+ if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "seek to end: %s", strerror(ret));
+ goto out;
+ }
+
+ ret = krb5_store_int32(sp, entry->vno);
+ if(ret) {
+ krb5_set_error_message(context, ret, "keytab keyfile failed store kvno");
+ goto out;
+ }
+ ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
+ entry->keyblock.keyvalue.length);
+ if(ret != entry->keyblock.keyvalue.length) {
+ if (ret < 0)
+ ret = errno;
+ else
+ ret = ENOTTY;
+ krb5_set_error_message(context, ret, "keytab keyfile failed to add key");
+ goto out;
+ }
+ ret = 0;
+out:
+ krb5_storage_free(sp);
+ close (fd);
+ return ret;
+}
+
+const krb5_kt_ops krb5_akf_ops = {
+ "AFSKEYFILE",
+ akf_resolve,
+ akf_get_name,
+ akf_close,
+ NULL, /* get */
+ akf_start_seq_get,
+ akf_next_entry,
+ akf_end_seq_get,
+ akf_add_entry,
+ NULL /* remove */
+};
diff --git a/source4/heimdal/lib/krb5/keytab_memory.c b/source4/heimdal/lib/krb5/keytab_memory.c
new file mode 100644
index 0000000000..5f648d9bce
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_memory.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/* memory operations -------------------------------------------- */
+
+struct mkt_data {
+ krb5_keytab_entry *entries;
+ int num_entries;
+ char *name;
+ int refcount;
+ struct mkt_data *next;
+};
+
+/* this mutex protects mkt_head, ->refcount, and ->next
+ * content is not protected (name is static and need no protection)
+ */
+static HEIMDAL_MUTEX mkt_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct mkt_data *mkt_head;
+
+
+static krb5_error_code
+mkt_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct mkt_data *d;
+
+ HEIMDAL_MUTEX_lock(&mkt_mutex);
+
+ for (d = mkt_head; d != NULL; d = d->next)
+ if (strcmp(d->name, name) == 0)
+ break;
+ if (d) {
+ if (d->refcount < 1)
+ krb5_abortx(context, "Double close on memory keytab, "
+ "refcount < 1 %d", d->refcount);
+ d->refcount++;
+ id->data = d;
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ return 0;
+ }
+
+ d = calloc(1, sizeof(*d));
+ if(d == NULL) {
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ d->name = strdup(name);
+ if (d->name == NULL) {
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ d->entries = NULL;
+ d->num_entries = 0;
+ d->refcount = 1;
+ d->next = mkt_head;
+ mkt_head = d;
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ id->data = d;
+ return 0;
+}
+
+static krb5_error_code
+mkt_close(krb5_context context, krb5_keytab id)
+{
+ struct mkt_data *d = id->data, **dp;
+ int i;
+
+ HEIMDAL_MUTEX_lock(&mkt_mutex);
+ if (d->refcount < 1)
+ krb5_abortx(context,
+ "krb5 internal error, memory keytab refcount < 1 on close");
+
+ if (--d->refcount > 0) {
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ return 0;
+ }
+ for (dp = &mkt_head; *dp != NULL; dp = &(*dp)->next) {
+ if (*dp == d) {
+ *dp = d->next;
+ break;
+ }
+ }
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+
+ free(d->name);
+ for(i = 0; i < d->num_entries; i++)
+ krb5_kt_free_entry(context, &d->entries[i]);
+ free(d->entries);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+mkt_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ struct mkt_data *d = id->data;
+ strlcpy(name, d->name, namesize);
+ return 0;
+}
+
+static krb5_error_code
+mkt_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ /* XXX */
+ c->fd = 0;
+ return 0;
+}
+
+static krb5_error_code
+mkt_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *c)
+{
+ struct mkt_data *d = id->data;
+ if(c->fd >= d->num_entries)
+ return KRB5_KT_END;
+ return krb5_kt_copy_entry_contents(context, &d->entries[c->fd++], entry);
+}
+
+static krb5_error_code
+mkt_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ return 0;
+}
+
+static krb5_error_code
+mkt_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct mkt_data *d = id->data;
+ krb5_keytab_entry *tmp;
+ tmp = realloc(d->entries, (d->num_entries + 1) * sizeof(*d->entries));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ d->entries = tmp;
+ return krb5_kt_copy_entry_contents(context, entry,
+ &d->entries[d->num_entries++]);
+}
+
+static krb5_error_code
+mkt_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct mkt_data *d = id->data;
+ krb5_keytab_entry *e, *end;
+ int found = 0;
+
+ if (d->num_entries == 0) {
+ krb5_clear_error_string(context);
+ return KRB5_KT_NOTFOUND;
+ }
+
+ /* do this backwards to minimize copying */
+ for(end = d->entries + d->num_entries, e = end - 1; e >= d->entries; e--) {
+ if(krb5_kt_compare(context, e, entry->principal,
+ entry->vno, entry->keyblock.keytype)) {
+ krb5_kt_free_entry(context, e);
+ memmove(e, e + 1, (end - e - 1) * sizeof(*e));
+ memset(end - 1, 0, sizeof(*end));
+ d->num_entries--;
+ end--;
+ found = 1;
+ }
+ }
+ if (!found) {
+ krb5_clear_error_string (context);
+ return KRB5_KT_NOTFOUND;
+ }
+ e = realloc(d->entries, d->num_entries * sizeof(*d->entries));
+ if(e != NULL || d->num_entries == 0)
+ d->entries = e;
+ return 0;
+}
+
+const krb5_kt_ops krb5_mkt_ops = {
+ "MEMORY",
+ mkt_resolve,
+ mkt_get_name,
+ mkt_close,
+ NULL, /* get */
+ mkt_start_seq_get,
+ mkt_next_entry,
+ mkt_end_seq_get,
+ mkt_add_entry,
+ mkt_remove_entry
+};
diff --git a/source4/heimdal/lib/krb5/krb5-v4compat.h b/source4/heimdal/lib/krb5/krb5-v4compat.h
new file mode 100644
index 0000000000..9470f10337
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5-v4compat.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_V4COMPAT_H__
+#define __KRB5_V4COMPAT_H__
+
+#include "krb_err.h"
+
+/*
+ * This file must only be included with v4 compat glue stuff in
+ * heimdal sources.
+ *
+ * It MUST NOT be installed.
+ */
+
+#define KRB_PROT_VERSION 4
+
+#define AUTH_MSG_KDC_REQUEST (1<<1)
+#define AUTH_MSG_KDC_REPLY (2<<1)
+#define AUTH_MSG_APPL_REQUEST (3<<1)
+#define AUTH_MSG_APPL_REQUEST_MUTUAL (4<<1)
+#define AUTH_MSG_ERR_REPLY (5<<1)
+#define AUTH_MSG_PRIVATE (6<<1)
+#define AUTH_MSG_SAFE (7<<1)
+#define AUTH_MSG_APPL_ERR (8<<1)
+#define AUTH_MSG_KDC_FORWARD (9<<1)
+#define AUTH_MSG_KDC_RENEW (10<<1)
+#define AUTH_MSG_DIE (63<<1)
+
+/* General definitions */
+#define KSUCCESS 0
+#define KFAILURE 255
+
+/* */
+
+#define MAX_KTXT_LEN 1250
+
+#define ANAME_SZ 40
+#define REALM_SZ 40
+#define SNAME_SZ 40
+#define INST_SZ 40
+
+struct ktext {
+ unsigned int length; /* Length of the text */
+ unsigned char dat[MAX_KTXT_LEN]; /* The data itself */
+ uint32_t mbz; /* zero to catch runaway strings */
+};
+
+struct credentials {
+ char service[ANAME_SZ]; /* Service name */
+ char instance[INST_SZ]; /* Instance */
+ char realm[REALM_SZ]; /* Auth domain */
+ char session[8]; /* Session key */
+ int lifetime; /* Lifetime */
+ int kvno; /* Key version number */
+ struct ktext ticket_st; /* The ticket itself */
+ int32_t issue_date; /* The issue time */
+ char pname[ANAME_SZ]; /* Principal's name */
+ char pinst[INST_SZ]; /* Principal's instance */
+};
+
+#define TKTLIFENUMFIXED 64
+#define TKTLIFEMINFIXED 0x80
+#define TKTLIFEMAXFIXED 0xBF
+#define TKTLIFENOEXPIRE 0xFF
+#define MAXTKTLIFETIME (30*24*3600) /* 30 days */
+#ifndef NEVERDATE
+#define NEVERDATE ((time_t)0x7fffffffL)
+#endif
+
+#define KERB_ERR_NULL_KEY 10
+
+#define CLOCK_SKEW 5*60
+
+#ifndef TKT_ROOT
+#define TKT_ROOT "/tmp/tkt"
+#endif
+
+struct _krb5_krb_auth_data {
+ int8_t k_flags; /* Flags from ticket */
+ char *pname; /* Principal's name */
+ char *pinst; /* His Instance */
+ char *prealm; /* His Realm */
+ uint32_t checksum; /* Data checksum (opt) */
+ krb5_keyblock session; /* Session Key */
+ unsigned char life; /* Life of ticket */
+ uint32_t time_sec; /* Time ticket issued */
+ uint32_t address; /* Address in ticket */
+};
+
+time_t _krb5_krb_life_to_time (int, int);
+int _krb5_krb_time_to_life (time_t, time_t);
+krb5_error_code _krb5_krb_tf_setup (krb5_context, struct credentials *,
+ const char *, int);
+krb5_error_code _krb5_krb_dest_tkt(krb5_context, const char *);
+
+#define krb_time_to_life _krb5_krb_time_to_life
+#define krb_life_to_time _krb5_krb_life_to_time
+
+#endif /* __KRB5_V4COMPAT_H__ */
diff --git a/source4/heimdal/lib/krb5/krb5.h b/source4/heimdal/lib/krb5/krb5.h
new file mode 100644
index 0000000000..aedabcc350
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5.h
@@ -0,0 +1,810 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_H__
+#define __KRB5_H__
+
+#include <time.h>
+#include <krb5-types.h>
+
+#include <asn1_err.h>
+#include <krb5_err.h>
+#include <heim_err.h>
+#include <k524_err.h>
+
+#include <krb5_asn1.h>
+
+/* name confusion with MIT */
+#ifndef KRB5KDC_ERR_KEY_EXP
+#define KRB5KDC_ERR_KEY_EXP KRB5KDC_ERR_KEY_EXPIRED
+#endif
+
+/* simple constants */
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+typedef int krb5_boolean;
+
+typedef int32_t krb5_error_code;
+
+typedef int krb5_kvno;
+
+typedef uint32_t krb5_flags;
+
+typedef void *krb5_pointer;
+typedef const void *krb5_const_pointer;
+
+struct krb5_crypto_data;
+typedef struct krb5_crypto_data *krb5_crypto;
+
+struct krb5_get_creds_opt_data;
+typedef struct krb5_get_creds_opt_data *krb5_get_creds_opt;
+
+struct krb5_digest_data;
+typedef struct krb5_digest_data *krb5_digest;
+struct krb5_ntlm_data;
+typedef struct krb5_ntlm_data *krb5_ntlm;
+
+struct krb5_pac_data;
+typedef struct krb5_pac_data *krb5_pac;
+
+typedef struct krb5_rd_req_in_ctx_data *krb5_rd_req_in_ctx;
+typedef struct krb5_rd_req_out_ctx_data *krb5_rd_req_out_ctx;
+
+typedef CKSUMTYPE krb5_cksumtype;
+
+typedef Checksum krb5_checksum;
+
+typedef ENCTYPE krb5_enctype;
+
+typedef heim_octet_string krb5_data;
+
+/* PKINIT related forward declarations */
+struct ContentInfo;
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+
+/* krb5_enc_data is a mit compat structure */
+typedef struct krb5_enc_data {
+ krb5_enctype enctype;
+ krb5_kvno kvno;
+ krb5_data ciphertext;
+} krb5_enc_data;
+
+/* alternative names */
+enum {
+ ENCTYPE_NULL = ETYPE_NULL,
+ ENCTYPE_DES_CBC_CRC = ETYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD4 = ETYPE_DES_CBC_MD4,
+ ENCTYPE_DES_CBC_MD5 = ETYPE_DES_CBC_MD5,
+ ENCTYPE_DES3_CBC_MD5 = ETYPE_DES3_CBC_MD5,
+ ENCTYPE_OLD_DES3_CBC_SHA1 = ETYPE_OLD_DES3_CBC_SHA1,
+ ENCTYPE_SIGN_DSA_GENERATE = ETYPE_SIGN_DSA_GENERATE,
+ ENCTYPE_ENCRYPT_RSA_PRIV = ETYPE_ENCRYPT_RSA_PRIV,
+ ENCTYPE_ENCRYPT_RSA_PUB = ETYPE_ENCRYPT_RSA_PUB,
+ ENCTYPE_DES3_CBC_SHA1 = ETYPE_DES3_CBC_SHA1,
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96 = ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96 = ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ENCTYPE_ARCFOUR_HMAC = ETYPE_ARCFOUR_HMAC_MD5,
+ ENCTYPE_ARCFOUR_HMAC_MD5 = ETYPE_ARCFOUR_HMAC_MD5,
+ ENCTYPE_ARCFOUR_HMAC_MD5_56 = ETYPE_ARCFOUR_HMAC_MD5_56,
+ ENCTYPE_ENCTYPE_PK_CROSS = ETYPE_ENCTYPE_PK_CROSS,
+ ENCTYPE_DES_CBC_NONE = ETYPE_DES_CBC_NONE,
+ ENCTYPE_DES3_CBC_NONE = ETYPE_DES3_CBC_NONE,
+ ENCTYPE_DES_CFB64_NONE = ETYPE_DES_CFB64_NONE,
+ ENCTYPE_DES_PCBC_NONE = ETYPE_DES_PCBC_NONE
+};
+
+typedef PADATA_TYPE krb5_preauthtype;
+
+typedef enum krb5_key_usage {
+ KRB5_KU_PA_ENC_TIMESTAMP = 1,
+ /* AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the
+ client key (section 5.4.1) */
+ KRB5_KU_TICKET = 2,
+ /* AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
+ application session key), encrypted with the service key
+ (section 5.4.2) */
+ KRB5_KU_AS_REP_ENC_PART = 3,
+ /* AS-REP encrypted part (includes tgs session key or application
+ session key), encrypted with the client key (section 5.4.2) */
+ KRB5_KU_TGS_REQ_AUTH_DAT_SESSION = 4,
+ /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs
+ session key (section 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY = 5,
+ /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs
+ authenticator subkey (section 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH_CKSUM = 6,
+ /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum, keyed
+ with the tgs session key (sections 5.3.2, 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH = 7,
+ /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes tgs
+ authenticator subkey), encrypted with the tgs session key
+ (section 5.3.2) */
+ KRB5_KU_TGS_REP_ENC_PART_SESSION = 8,
+ /* TGS-REP encrypted part (includes application session key),
+ encrypted with the tgs session key (section 5.4.2) */
+ KRB5_KU_TGS_REP_ENC_PART_SUB_KEY = 9,
+ /* TGS-REP encrypted part (includes application session key),
+ encrypted with the tgs authenticator subkey (section 5.4.2) */
+ KRB5_KU_AP_REQ_AUTH_CKSUM = 10,
+ /* AP-REQ Authenticator cksum, keyed with the application session
+ key (section 5.3.2) */
+ KRB5_KU_AP_REQ_AUTH = 11,
+ /* AP-REQ Authenticator (includes application authenticator
+ subkey), encrypted with the application session key (section
+ 5.3.2) */
+ KRB5_KU_AP_REQ_ENC_PART = 12,
+ /* AP-REP encrypted part (includes application session subkey),
+ encrypted with the application session key (section 5.5.2) */
+ KRB5_KU_KRB_PRIV = 13,
+ /* KRB-PRIV encrypted part, encrypted with a key chosen by the
+ application (section 5.7.1) */
+ KRB5_KU_KRB_CRED = 14,
+ /* KRB-CRED encrypted part, encrypted with a key chosen by the
+ application (section 5.8.1) */
+ KRB5_KU_KRB_SAFE_CKSUM = 15,
+ /* KRB-SAFE cksum, keyed with a key chosen by the application
+ (section 5.6.1) */
+ KRB5_KU_OTHER_ENCRYPTED = 16,
+ /* Data which is defined in some specification outside of
+ Kerberos to be encrypted using an RFC1510 encryption type. */
+ KRB5_KU_OTHER_CKSUM = 17,
+ /* Data which is defined in some specification outside of
+ Kerberos to be checksummed using an RFC1510 checksum type. */
+ KRB5_KU_KRB_ERROR = 18,
+ /* Krb-error checksum */
+ KRB5_KU_AD_KDC_ISSUED = 19,
+ /* AD-KDCIssued checksum */
+ KRB5_KU_MANDATORY_TICKET_EXTENSION = 20,
+ /* Checksum for Mandatory Ticket Extensions */
+ KRB5_KU_AUTH_DATA_TICKET_EXTENSION = 21,
+ /* Checksum in Authorization Data in Ticket Extensions */
+ KRB5_KU_USAGE_SEAL = 22,
+ /* seal in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_SIGN = 23,
+ /* sign in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_SEQ = 24,
+ /* SEQ in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_ACCEPTOR_SEAL = 22,
+ /* acceptor sign in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_ACCEPTOR_SIGN = 23,
+ /* acceptor seal in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_INITIATOR_SEAL = 24,
+ /* initiator sign in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_INITIATOR_SIGN = 25,
+ /* initiator seal in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_PA_SERVER_REFERRAL_DATA = 22,
+ /* encrypted server referral data */
+ KRB5_KU_SAM_CHECKSUM = 25,
+ /* Checksum for the SAM-CHECKSUM field */
+ KRB5_KU_SAM_ENC_TRACK_ID = 26,
+ /* Encryption of the SAM-TRACK-ID field */
+ KRB5_KU_PA_SERVER_REFERRAL = 26,
+ /* Keyusage for the server referral in a TGS req */
+ KRB5_KU_SAM_ENC_NONCE_SAD = 27,
+ /* Encryption of the SAM-NONCE-OR-SAD field */
+ KRB5_KU_DIGEST_ENCRYPT = -18,
+ /* Encryption key usage used in the digest encryption field */
+ KRB5_KU_DIGEST_OPAQUE = -19,
+ /* Checksum key usage used in the digest opaque field */
+ KRB5_KU_KRB5SIGNEDPATH = -21,
+ /* Checksum key usage on KRB5SignedPath */
+ KRB5_KU_CANONICALIZED_NAMES = -23
+ /* Checksum key usage on PA-CANONICALIZED */
+} krb5_key_usage;
+
+typedef krb5_key_usage krb5_keyusage;
+
+typedef enum krb5_salttype {
+ KRB5_PW_SALT = KRB5_PADATA_PW_SALT,
+ KRB5_AFS3_SALT = KRB5_PADATA_AFS3_SALT
+}krb5_salttype;
+
+typedef struct krb5_salt {
+ krb5_salttype salttype;
+ krb5_data saltvalue;
+} krb5_salt;
+
+typedef ETYPE_INFO krb5_preauthinfo;
+
+typedef struct {
+ krb5_preauthtype type;
+ krb5_preauthinfo info; /* list of preauthinfo for this type */
+} krb5_preauthdata_entry;
+
+typedef struct krb5_preauthdata {
+ unsigned len;
+ krb5_preauthdata_entry *val;
+}krb5_preauthdata;
+
+typedef enum krb5_address_type {
+ KRB5_ADDRESS_INET = 2,
+ KRB5_ADDRESS_NETBIOS = 20,
+ KRB5_ADDRESS_INET6 = 24,
+ KRB5_ADDRESS_ADDRPORT = 256,
+ KRB5_ADDRESS_IPPORT = 257
+} krb5_address_type;
+
+enum {
+ AP_OPTS_USE_SESSION_KEY = 1,
+ AP_OPTS_MUTUAL_REQUIRED = 2,
+ AP_OPTS_USE_SUBKEY = 4 /* library internal */
+};
+
+typedef HostAddress krb5_address;
+
+typedef HostAddresses krb5_addresses;
+
+typedef enum krb5_keytype {
+ KEYTYPE_NULL = 0,
+ KEYTYPE_DES = 1,
+ KEYTYPE_DES3 = 7,
+ KEYTYPE_AES128 = 17,
+ KEYTYPE_AES256 = 18,
+ KEYTYPE_ARCFOUR = 23,
+ KEYTYPE_ARCFOUR_56 = 24
+} krb5_keytype;
+
+typedef EncryptionKey krb5_keyblock;
+
+typedef AP_REQ krb5_ap_req;
+
+struct krb5_cc_ops;
+
+#define KRB5_DEFAULT_CCFILE_ROOT "/tmp/krb5cc_"
+
+#define KRB5_DEFAULT_CCROOT "FILE:" KRB5_DEFAULT_CCFILE_ROOT
+
+#define KRB5_ACCEPT_NULL_ADDRESSES(C) \
+ krb5_config_get_bool_default((C), NULL, TRUE, \
+ "libdefaults", "accept_null_addresses", \
+ NULL)
+
+typedef void *krb5_cc_cursor;
+
+typedef struct krb5_ccache_data {
+ const struct krb5_cc_ops *ops;
+ krb5_data data;
+}krb5_ccache_data;
+
+typedef struct krb5_ccache_data *krb5_ccache;
+
+typedef struct krb5_context_data *krb5_context;
+
+typedef Realm krb5_realm;
+typedef const char *krb5_const_realm; /* stupid language */
+
+#define krb5_realm_length(r) strlen(r)
+#define krb5_realm_data(r) (r)
+
+typedef Principal krb5_principal_data;
+typedef struct Principal *krb5_principal;
+typedef const struct Principal *krb5_const_principal;
+
+typedef time_t krb5_deltat;
+typedef time_t krb5_timestamp;
+
+typedef struct krb5_times {
+ krb5_timestamp authtime;
+ krb5_timestamp starttime;
+ krb5_timestamp endtime;
+ krb5_timestamp renew_till;
+} krb5_times;
+
+typedef union {
+ TicketFlags b;
+ krb5_flags i;
+} krb5_ticket_flags;
+
+/* options for krb5_get_in_tkt() */
+#define KDC_OPT_FORWARDABLE (1 << 1)
+#define KDC_OPT_FORWARDED (1 << 2)
+#define KDC_OPT_PROXIABLE (1 << 3)
+#define KDC_OPT_PROXY (1 << 4)
+#define KDC_OPT_ALLOW_POSTDATE (1 << 5)
+#define KDC_OPT_POSTDATED (1 << 6)
+#define KDC_OPT_RENEWABLE (1 << 8)
+#define KDC_OPT_REQUEST_ANONYMOUS (1 << 14)
+#define KDC_OPT_DISABLE_TRANSITED_CHECK (1 << 26)
+#define KDC_OPT_RENEWABLE_OK (1 << 27)
+#define KDC_OPT_ENC_TKT_IN_SKEY (1 << 28)
+#define KDC_OPT_RENEW (1 << 30)
+#define KDC_OPT_VALIDATE (1 << 31)
+
+typedef union {
+ KDCOptions b;
+ krb5_flags i;
+} krb5_kdc_flags;
+
+/* flags for krb5_verify_ap_req */
+
+#define KRB5_VERIFY_AP_REQ_IGNORE_INVALID (1 << 0)
+
+#define KRB5_GC_CACHED (1U << 0)
+#define KRB5_GC_USER_USER (1U << 1)
+#define KRB5_GC_EXPIRED_OK (1U << 2)
+#define KRB5_GC_NO_STORE (1U << 3)
+#define KRB5_GC_FORWARDABLE (1U << 4)
+#define KRB5_GC_NO_TRANSIT_CHECK (1U << 5)
+#define KRB5_GC_CONSTRAINED_DELEGATION (1U << 6)
+#define KRB5_GC_CANONICALIZE (1U << 7)
+
+/* constants for compare_creds (and cc_retrieve_cred) */
+#define KRB5_TC_DONT_MATCH_REALM (1U << 31)
+#define KRB5_TC_MATCH_KEYTYPE (1U << 30)
+#define KRB5_TC_MATCH_KTYPE KRB5_TC_MATCH_KEYTYPE /* MIT name */
+#define KRB5_TC_MATCH_SRV_NAMEONLY (1 << 29)
+#define KRB5_TC_MATCH_FLAGS_EXACT (1 << 28)
+#define KRB5_TC_MATCH_FLAGS (1 << 27)
+#define KRB5_TC_MATCH_TIMES_EXACT (1 << 26)
+#define KRB5_TC_MATCH_TIMES (1 << 25)
+#define KRB5_TC_MATCH_AUTHDATA (1 << 24)
+#define KRB5_TC_MATCH_2ND_TKT (1 << 23)
+#define KRB5_TC_MATCH_IS_SKEY (1 << 22)
+
+typedef AuthorizationData krb5_authdata;
+
+typedef KRB_ERROR krb5_error;
+
+typedef struct krb5_creds {
+ krb5_principal client;
+ krb5_principal server;
+ krb5_keyblock session;
+ krb5_times times;
+ krb5_data ticket;
+ krb5_data second_ticket;
+ krb5_authdata authdata;
+ krb5_addresses addresses;
+ krb5_ticket_flags flags;
+} krb5_creds;
+
+typedef struct krb5_cc_cache_cursor_data *krb5_cc_cache_cursor;
+
+#define KRB5_CC_OPS_VERSION 1
+
+typedef struct krb5_cc_ops {
+ int version;
+ const char *prefix;
+ const char* (*get_name)(krb5_context, krb5_ccache);
+ krb5_error_code (*resolve)(krb5_context, krb5_ccache *, const char *);
+ krb5_error_code (*gen_new)(krb5_context, krb5_ccache *);
+ krb5_error_code (*init)(krb5_context, krb5_ccache, krb5_principal);
+ krb5_error_code (*destroy)(krb5_context, krb5_ccache);
+ krb5_error_code (*close)(krb5_context, krb5_ccache);
+ krb5_error_code (*store)(krb5_context, krb5_ccache, krb5_creds*);
+ krb5_error_code (*retrieve)(krb5_context, krb5_ccache,
+ krb5_flags, const krb5_creds*, krb5_creds *);
+ krb5_error_code (*get_princ)(krb5_context, krb5_ccache, krb5_principal*);
+ krb5_error_code (*get_first)(krb5_context, krb5_ccache, krb5_cc_cursor *);
+ krb5_error_code (*get_next)(krb5_context, krb5_ccache,
+ krb5_cc_cursor*, krb5_creds*);
+ krb5_error_code (*end_get)(krb5_context, krb5_ccache, krb5_cc_cursor*);
+ krb5_error_code (*remove_cred)(krb5_context, krb5_ccache,
+ krb5_flags, krb5_creds*);
+ krb5_error_code (*set_flags)(krb5_context, krb5_ccache, krb5_flags);
+ int (*get_version)(krb5_context, krb5_ccache);
+ krb5_error_code (*get_cache_first)(krb5_context, krb5_cc_cursor *);
+ krb5_error_code (*get_cache_next)(krb5_context, krb5_cc_cursor, krb5_ccache *);
+ krb5_error_code (*end_cache_get)(krb5_context, krb5_cc_cursor);
+ krb5_error_code (*move)(krb5_context, krb5_ccache, krb5_ccache);
+ krb5_error_code (*get_default_name)(krb5_context, char **);
+ krb5_error_code (*set_default)(krb5_context, krb5_ccache);
+} krb5_cc_ops;
+
+struct krb5_log_facility;
+
+struct krb5_config_binding {
+ enum { krb5_config_string, krb5_config_list } type;
+ char *name;
+ struct krb5_config_binding *next;
+ union {
+ char *string;
+ struct krb5_config_binding *list;
+ void *generic;
+ } u;
+};
+
+typedef struct krb5_config_binding krb5_config_binding;
+
+typedef krb5_config_binding krb5_config_section;
+
+typedef struct krb5_ticket {
+ EncTicketPart ticket;
+ krb5_principal client;
+ krb5_principal server;
+} krb5_ticket;
+
+typedef Authenticator krb5_authenticator_data;
+
+typedef krb5_authenticator_data *krb5_authenticator;
+
+struct krb5_rcache_data;
+typedef struct krb5_rcache_data *krb5_rcache;
+typedef Authenticator krb5_donot_replay;
+
+#define KRB5_STORAGE_HOST_BYTEORDER 0x01 /* old */
+#define KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS 0x02
+#define KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE 0x04
+#define KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE 0x08
+#define KRB5_STORAGE_BYTEORDER_MASK 0x60
+#define KRB5_STORAGE_BYTEORDER_BE 0x00 /* default */
+#define KRB5_STORAGE_BYTEORDER_LE 0x20
+#define KRB5_STORAGE_BYTEORDER_HOST 0x40
+#define KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER 0x80
+
+struct krb5_storage_data;
+typedef struct krb5_storage_data krb5_storage;
+
+typedef struct krb5_keytab_entry {
+ krb5_principal principal;
+ krb5_kvno vno;
+ krb5_keyblock keyblock;
+ uint32_t timestamp;
+} krb5_keytab_entry;
+
+typedef struct krb5_kt_cursor {
+ int fd;
+ krb5_storage *sp;
+ void *data;
+} krb5_kt_cursor;
+
+struct krb5_keytab_data;
+
+typedef struct krb5_keytab_data *krb5_keytab;
+
+#define KRB5_KT_PREFIX_MAX_LEN 30
+
+struct krb5_keytab_data {
+ const char *prefix;
+ krb5_error_code (*resolve)(krb5_context, const char*, krb5_keytab);
+ krb5_error_code (*get_name)(krb5_context, krb5_keytab, char*, size_t);
+ krb5_error_code (*close)(krb5_context, krb5_keytab);
+ krb5_error_code (*get)(krb5_context, krb5_keytab, krb5_const_principal,
+ krb5_kvno, krb5_enctype, krb5_keytab_entry*);
+ krb5_error_code (*start_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*);
+ krb5_error_code (*next_entry)(krb5_context, krb5_keytab,
+ krb5_keytab_entry*, krb5_kt_cursor*);
+ krb5_error_code (*end_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*);
+ krb5_error_code (*add)(krb5_context, krb5_keytab, krb5_keytab_entry*);
+ krb5_error_code (*remove)(krb5_context, krb5_keytab, krb5_keytab_entry*);
+ void *data;
+ int32_t version;
+};
+
+typedef struct krb5_keytab_data krb5_kt_ops;
+
+struct krb5_keytab_key_proc_args {
+ krb5_keytab keytab;
+ krb5_principal principal;
+};
+
+typedef struct krb5_keytab_key_proc_args krb5_keytab_key_proc_args;
+
+typedef struct krb5_replay_data {
+ krb5_timestamp timestamp;
+ int32_t usec;
+ uint32_t seq;
+} krb5_replay_data;
+
+/* flags for krb5_auth_con_setflags */
+enum {
+ KRB5_AUTH_CONTEXT_DO_TIME = 1,
+ KRB5_AUTH_CONTEXT_RET_TIME = 2,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE = 4,
+ KRB5_AUTH_CONTEXT_RET_SEQUENCE = 8,
+ KRB5_AUTH_CONTEXT_PERMIT_ALL = 16,
+ KRB5_AUTH_CONTEXT_USE_SUBKEY = 32,
+ KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED = 64
+};
+
+/* flags for krb5_auth_con_genaddrs */
+enum {
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR = 1,
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR = 3,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR = 4,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR = 12
+};
+
+typedef struct krb5_auth_context_data {
+ unsigned int flags;
+
+ krb5_address *local_address;
+ krb5_address *remote_address;
+ int16_t local_port;
+ int16_t remote_port;
+ krb5_keyblock *keyblock;
+ krb5_keyblock *local_subkey;
+ krb5_keyblock *remote_subkey;
+
+ uint32_t local_seqnumber;
+ uint32_t remote_seqnumber;
+
+ krb5_authenticator authenticator;
+
+ krb5_pointer i_vector;
+
+ krb5_rcache rcache;
+
+ krb5_keytype keytype; /* ¿requested key type ? */
+ krb5_cksumtype cksumtype; /* ¡requested checksum type! */
+
+}krb5_auth_context_data, *krb5_auth_context;
+
+typedef struct {
+ KDC_REP kdc_rep;
+ EncKDCRepPart enc_part;
+ KRB_ERROR error;
+} krb5_kdc_rep;
+
+extern const char *heimdal_version, *heimdal_long_version;
+
+typedef void (*krb5_log_log_func_t)(const char*, const char*, void*);
+typedef void (*krb5_log_close_func_t)(void*);
+
+typedef struct krb5_log_facility {
+ char *program;
+ int len;
+ struct facility *val;
+} krb5_log_facility;
+
+typedef EncAPRepPart krb5_ap_rep_enc_part;
+
+#define KRB5_RECVAUTH_IGNORE_VERSION 1
+
+#define KRB5_SENDAUTH_VERSION "KRB5_SENDAUTH_V1.0"
+
+#define KRB5_TGS_NAME_SIZE (6)
+#define KRB5_TGS_NAME ("krbtgt")
+
+#define KRB5_DIGEST_NAME ("digest")
+
+typedef enum {
+ KRB5_PROMPT_TYPE_PASSWORD = 0x1,
+ KRB5_PROMPT_TYPE_NEW_PASSWORD = 0x2,
+ KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN = 0x3,
+ KRB5_PROMPT_TYPE_PREAUTH = 0x4,
+ KRB5_PROMPT_TYPE_INFO = 0x5
+} krb5_prompt_type;
+
+typedef struct _krb5_prompt {
+ const char *prompt;
+ int hidden;
+ krb5_data *reply;
+ krb5_prompt_type type;
+} krb5_prompt;
+
+typedef int (*krb5_prompter_fct)(krb5_context /*context*/,
+ void * /*data*/,
+ const char * /*name*/,
+ const char * /*banner*/,
+ int /*num_prompts*/,
+ krb5_prompt /*prompts*/[]);
+typedef krb5_error_code (*krb5_key_proc)(krb5_context /*context*/,
+ krb5_enctype /*type*/,
+ krb5_salt /*salt*/,
+ krb5_const_pointer /*keyseed*/,
+ krb5_keyblock ** /*key*/);
+typedef krb5_error_code (*krb5_decrypt_proc)(krb5_context /*context*/,
+ krb5_keyblock * /*key*/,
+ krb5_key_usage /*usage*/,
+ krb5_const_pointer /*decrypt_arg*/,
+ krb5_kdc_rep * /*dec_rep*/);
+typedef krb5_error_code (*krb5_s2k_proc)(krb5_context /*context*/,
+ krb5_enctype /*type*/,
+ krb5_const_pointer /*keyseed*/,
+ krb5_salt /*salt*/,
+ krb5_data * /*s2kparms*/,
+ krb5_keyblock ** /*key*/);
+
+struct _krb5_get_init_creds_opt_private;
+
+typedef struct _krb5_get_init_creds_opt {
+ krb5_flags flags;
+ krb5_deltat tkt_life;
+ krb5_deltat renew_life;
+ int forwardable;
+ int proxiable;
+ int anonymous;
+ krb5_enctype *etype_list;
+ int etype_list_length;
+ krb5_addresses *address_list;
+ /* XXX the next three should not be used, as they may be
+ removed later */
+ krb5_preauthtype *preauth_list;
+ int preauth_list_length;
+ krb5_data *salt;
+ struct _krb5_get_init_creds_opt_private *opt_private;
+} krb5_get_init_creds_opt;
+
+#define KRB5_GET_INIT_CREDS_OPT_TKT_LIFE 0x0001
+#define KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE 0x0002
+#define KRB5_GET_INIT_CREDS_OPT_FORWARDABLE 0x0004
+#define KRB5_GET_INIT_CREDS_OPT_PROXIABLE 0x0008
+#define KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST 0x0010
+#define KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST 0x0020
+#define KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST 0x0040
+#define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080
+#define KRB5_GET_INIT_CREDS_OPT_ANONYMOUS 0x0100
+#define KRB5_GET_INIT_CREDS_OPT_DISABLE_TRANSITED_CHECK 0x0200
+
+typedef struct _krb5_verify_init_creds_opt {
+ krb5_flags flags;
+ int ap_req_nofail;
+} krb5_verify_init_creds_opt;
+
+#define KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL 0x0001
+
+typedef struct krb5_verify_opt {
+ unsigned int flags;
+ krb5_ccache ccache;
+ krb5_keytab keytab;
+ krb5_boolean secure;
+ const char *service;
+} krb5_verify_opt;
+
+#define KRB5_VERIFY_LREALMS 1
+#define KRB5_VERIFY_NO_ADDRESSES 2
+
+#define KRB5_KPASSWD_VERS_CHANGEPW 1
+#define KRB5_KPASSWD_VERS_SETPW 0xff80
+
+#define KRB5_KPASSWD_SUCCESS 0
+#define KRB5_KPASSWD_MALFORMED 1
+#define KRB5_KPASSWD_HARDERROR 2
+#define KRB5_KPASSWD_AUTHERROR 3
+#define KRB5_KPASSWD_SOFTERROR 4
+#define KRB5_KPASSWD_ACCESSDENIED 5
+#define KRB5_KPASSWD_BAD_VERSION 6
+#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7
+
+#define KPASSWD_PORT 464
+
+/* types for the new krbhst interface */
+struct krb5_krbhst_data;
+typedef struct krb5_krbhst_data *krb5_krbhst_handle;
+
+#define KRB5_KRBHST_KDC 1
+#define KRB5_KRBHST_ADMIN 2
+#define KRB5_KRBHST_CHANGEPW 3
+#define KRB5_KRBHST_KRB524 4
+#define KRB5_KRBHST_KCA 5
+
+typedef struct krb5_krbhst_info {
+ enum { KRB5_KRBHST_UDP,
+ KRB5_KRBHST_TCP,
+ KRB5_KRBHST_HTTP } proto;
+ unsigned short port;
+ unsigned short def_port;
+ struct addrinfo *ai;
+ struct krb5_krbhst_info *next;
+ char hostname[1]; /* has to come last */
+} krb5_krbhst_info;
+
+/* flags for krb5_krbhst_init_flags (and krb5_send_to_kdc_flags) */
+enum {
+ KRB5_KRBHST_FLAGS_MASTER = 1,
+ KRB5_KRBHST_FLAGS_LARGE_MSG = 2
+};
+
+typedef krb5_error_code (*krb5_send_to_kdc_func)(krb5_context,
+ void *,
+ krb5_krbhst_info *,
+ time_t timeout,
+ const krb5_data *,
+ krb5_data *);
+
+/* flags for krb5_parse_name_flags */
+enum {
+ KRB5_PRINCIPAL_PARSE_NO_REALM = 1,
+ KRB5_PRINCIPAL_PARSE_MUST_REALM = 2,
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE = 4
+};
+
+/* flags for krb5_unparse_name_flags */
+enum {
+ KRB5_PRINCIPAL_UNPARSE_SHORT = 1,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM = 2,
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY = 4
+};
+
+typedef struct krb5_sendto_ctx_data *krb5_sendto_ctx;
+
+#define KRB5_SENDTO_DONE 0
+#define KRB5_SENDTO_RESTART 1
+#define KRB5_SENDTO_CONTINUE 2
+
+typedef krb5_error_code (*krb5_sendto_ctx_func)(krb5_context, krb5_sendto_ctx, void *, const krb5_data *, int *);
+
+struct krb5_plugin;
+enum krb5_plugin_type {
+ PLUGIN_TYPE_DATA = 1,
+ PLUGIN_TYPE_FUNC
+};
+
+struct credentials; /* this is to keep the compiler happy */
+struct getargs;
+struct sockaddr;
+
+/**
+ * Semi private, not stable yet
+ */
+
+typedef struct krb5_crypto_iov {
+ unsigned int flags;
+ /* ignored */
+#define KRB5_CRYPTO_TYPE_EMPTY 0
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_HEADER) */
+#define KRB5_CRYPTO_TYPE_HEADER 1
+ /* IN and OUT */
+#define KRB5_CRYPTO_TYPE_DATA 2
+ /* IN */
+#define KRB5_CRYPTO_TYPE_SIGN_ONLY 3
+ /* (only for encryption) OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */
+#define KRB5_CRYPTO_TYPE_PADDING 4
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */
+#define KRB5_CRYPTO_TYPE_TRAILER 5
+ krb5_data data;
+} krb5_crypto_iov;
+
+
+#include <krb5-protos.h>
+
+/* variables */
+
+extern KRB5_LIB_VARIABLE const char *krb5_config_file;
+extern KRB5_LIB_VARIABLE const char *krb5_defkeyname;
+
+
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops;
+
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_fkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_wrfkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_javakt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_mkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_akf_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb4_fkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_srvtab_fkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_any_ops;
+
+#endif /* __KRB5_H__ */
+
diff --git a/source4/heimdal/lib/krb5/krb5_ccapi.h b/source4/heimdal/lib/krb5/krb5_ccapi.h
new file mode 100644
index 0000000000..7a8ac584a1
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5_ccapi.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef KRB5_CCAPI_H
+#define KRB5_CCAPI_H 1
+
+#include <krb5-types.h>
+
+enum {
+ cc_credentials_v5 = 2
+};
+
+enum {
+ ccapi_version_3 = 3,
+ ccapi_version_4 = 4
+};
+
+enum {
+ ccNoError = 0,
+
+ ccIteratorEnd = 201,
+ ccErrBadParam,
+ ccErrNoMem,
+ ccErrInvalidContext,
+ ccErrInvalidCCache,
+
+ ccErrInvalidString, /* 206 */
+ ccErrInvalidCredentials,
+ ccErrInvalidCCacheIterator,
+ ccErrInvalidCredentialsIterator,
+ ccErrInvalidLock,
+
+ ccErrBadName, /* 211 */
+ ccErrBadCredentialsVersion,
+ ccErrBadAPIVersion,
+ ccErrContextLocked,
+ ccErrContextUnlocked,
+
+ ccErrCCacheLocked, /* 216 */
+ ccErrCCacheUnlocked,
+ ccErrBadLockType,
+ ccErrNeverDefault,
+ ccErrCredentialsNotFound,
+
+ ccErrCCacheNotFound, /* 221 */
+ ccErrContextNotFound,
+ ccErrServerUnavailable,
+ ccErrServerInsecure,
+ ccErrServerCantBecomeUID,
+
+ ccErrTimeOffsetNotSet /* 226 */
+};
+
+typedef int32_t cc_int32;
+typedef uint32_t cc_uint32;
+typedef struct cc_context_t *cc_context_t;
+typedef struct cc_ccache_t *cc_ccache_t;
+typedef struct cc_ccache_iterator_t *cc_ccache_iterator_t;
+typedef struct cc_credentials_v5_t cc_credentials_v5_t;
+typedef struct cc_credentials_t *cc_credentials_t;
+typedef struct cc_credentials_iterator_t *cc_credentials_iterator_t;
+typedef struct cc_string_t *cc_string_t;
+typedef time_t cc_time_t;
+
+typedef struct cc_data {
+ cc_uint32 type;
+ cc_uint32 length;
+ void *data;
+} cc_data;
+
+struct cc_credentials_v5_t {
+ char *client;
+ char *server;
+ cc_data keyblock;
+ cc_time_t authtime;
+ cc_time_t starttime;
+ cc_time_t endtime;
+ cc_time_t renew_till;
+ cc_uint32 is_skey;
+ cc_uint32 ticket_flags;
+#define KRB5_CCAPI_TKT_FLG_FORWARDABLE 0x40000000
+#define KRB5_CCAPI_TKT_FLG_FORWARDED 0x20000000
+#define KRB5_CCAPI_TKT_FLG_PROXIABLE 0x10000000
+#define KRB5_CCAPI_TKT_FLG_PROXY 0x08000000
+#define KRB5_CCAPI_TKT_FLG_MAY_POSTDATE 0x04000000
+#define KRB5_CCAPI_TKT_FLG_POSTDATED 0x02000000
+#define KRB5_CCAPI_TKT_FLG_INVALID 0x01000000
+#define KRB5_CCAPI_TKT_FLG_RENEWABLE 0x00800000
+#define KRB5_CCAPI_TKT_FLG_INITIAL 0x00400000
+#define KRB5_CCAPI_TKT_FLG_PRE_AUTH 0x00200000
+#define KRB5_CCAPI_TKT_FLG_HW_AUTH 0x00100000
+#define KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000
+#define KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE 0x00040000
+#define KRB5_CCAPI_TKT_FLG_ANONYMOUS 0x00020000
+ cc_data **addresses;
+ cc_data ticket;
+ cc_data second_ticket;
+ cc_data **authdata;
+};
+
+
+typedef struct cc_string_functions {
+ cc_int32 (*release)(cc_string_t);
+} cc_string_functions;
+
+struct cc_string_t {
+ const char *data;
+ const cc_string_functions *func;
+};
+
+typedef struct cc_credentials_union {
+ cc_int32 version;
+ union {
+ cc_credentials_v5_t* credentials_v5;
+ } credentials;
+} cc_credentials_union;
+
+struct cc_credentials_functions {
+ cc_int32 (*release)(cc_credentials_t);
+ cc_int32 (*compare)(cc_credentials_t, cc_credentials_t, cc_uint32*);
+};
+
+struct cc_credentials_t {
+ const cc_credentials_union* data;
+ const struct cc_credentials_functions* func;
+};
+
+struct cc_credentials_iterator_functions {
+ cc_int32 (*release)(cc_credentials_iterator_t);
+ cc_int32 (*next)(cc_credentials_iterator_t, cc_credentials_t*);
+};
+
+struct cc_credentials_iterator_t {
+ const struct cc_credentials_iterator_functions *func;
+};
+
+struct cc_ccache_iterator_functions {
+ cc_int32 (*release) (cc_ccache_iterator_t);
+ cc_int32 (*next)(cc_ccache_iterator_t, cc_ccache_t*);
+};
+
+struct cc_ccache_iterator_t {
+ const struct cc_ccache_iterator_functions* func;
+};
+
+typedef struct cc_ccache_functions {
+ cc_int32 (*release)(cc_ccache_t);
+ cc_int32 (*destroy)(cc_ccache_t);
+ cc_int32 (*set_default)(cc_ccache_t);
+ cc_int32 (*get_credentials_version)(cc_ccache_t, cc_uint32*);
+ cc_int32 (*get_name)(cc_ccache_t, cc_string_t*);
+ cc_int32 (*get_principal)(cc_ccache_t, cc_uint32, cc_string_t*);
+ cc_int32 (*set_principal)(cc_ccache_t, cc_uint32, const char*);
+ cc_int32 (*store_credentials)(cc_ccache_t, const cc_credentials_union*);
+ cc_int32 (*remove_credentials)(cc_ccache_t, cc_credentials_t);
+ cc_int32 (*new_credentials_iterator)(cc_ccache_t,
+ cc_credentials_iterator_t*);
+ cc_int32 (*move)(cc_ccache_t, cc_ccache_t);
+ cc_int32 (*lock)(cc_ccache_t, cc_uint32, cc_uint32);
+ cc_int32 (*unlock)(cc_ccache_t);
+ cc_int32 (*get_last_default_time)(cc_ccache_t, cc_time_t*);
+ cc_int32 (*get_change_time)(cc_ccache_t, cc_time_t*);
+ cc_int32 (*compare)(cc_ccache_t, cc_ccache_t, cc_uint32*);
+ cc_int32 (*get_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t *);
+ cc_int32 (*set_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t);
+ cc_int32 (*clear_kdc_time_offset)(cc_ccache_t, cc_int32);
+} cc_ccache_functions;
+
+struct cc_ccache_t {
+ const cc_ccache_functions *func;
+};
+
+struct cc_context_functions {
+ cc_int32 (*release)(cc_context_t);
+ cc_int32 (*get_change_time)(cc_context_t, cc_time_t *);
+ cc_int32 (*get_default_ccache_name)(cc_context_t, cc_string_t*);
+ cc_int32 (*open_ccache)(cc_context_t, const char*, cc_ccache_t *);
+ cc_int32 (*open_default_ccache)(cc_context_t, cc_ccache_t*);
+ cc_int32 (*create_ccache)(cc_context_t,const char*, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*create_default_ccache)(cc_context_t, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*create_new_ccache)(cc_context_t, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*new_ccache_iterator)(cc_context_t, cc_ccache_iterator_t*);
+ cc_int32 (*lock)(cc_context_t, cc_uint32, cc_uint32);
+ cc_int32 (*unlock)(cc_context_t);
+ cc_int32 (*compare)(cc_context_t, cc_context_t, cc_uint32*);
+};
+
+struct cc_context_t {
+ const struct cc_context_functions* func;
+};
+
+typedef cc_int32
+(*cc_initialize_func)(cc_context_t*, cc_int32, cc_int32 *, char const **);
+
+#endif /* KRB5_CCAPI_H */
diff --git a/source4/heimdal/lib/krb5/krb5_err.et b/source4/heimdal/lib/krb5/krb5_err.et
new file mode 100644
index 0000000000..c076992d0b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5_err.et
@@ -0,0 +1,268 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table krb5
+
+prefix KRB5KDC_ERR
+error_code NONE, "No error"
+error_code NAME_EXP, "Client's entry in database has expired"
+error_code SERVICE_EXP, "Server's entry in database has expired"
+error_code BAD_PVNO, "Requested protocol version not supported"
+error_code C_OLD_MAST_KVNO, "Client's key is encrypted in an old master key"
+error_code S_OLD_MAST_KVNO, "Server's key is encrypted in an old master key"
+error_code C_PRINCIPAL_UNKNOWN, "Client not found in Kerberos database"
+error_code S_PRINCIPAL_UNKNOWN, "Server not found in Kerberos database"
+error_code PRINCIPAL_NOT_UNIQUE,"Principal has multiple entries in Kerberos database"
+error_code NULL_KEY, "Client or server has a null key"
+error_code CANNOT_POSTDATE, "Ticket is ineligible for postdating"
+error_code NEVER_VALID, "Requested effective lifetime is negative or too short"
+error_code POLICY, "KDC policy rejects request"
+error_code BADOPTION, "KDC can't fulfill requested option"
+error_code ETYPE_NOSUPP, "KDC has no support for encryption type"
+error_code SUMTYPE_NOSUPP, "KDC has no support for checksum type"
+error_code PADATA_TYPE_NOSUPP, "KDC has no support for padata type"
+error_code TRTYPE_NOSUPP, "KDC has no support for transited type"
+error_code CLIENT_REVOKED, "Clients credentials have been revoked"
+error_code SERVICE_REVOKED, "Credentials for server have been revoked"
+error_code TGT_REVOKED, "TGT has been revoked"
+error_code CLIENT_NOTYET, "Client not yet valid - try again later"
+error_code SERVICE_NOTYET, "Server not yet valid - try again later"
+error_code KEY_EXPIRED, "Password has expired"
+error_code PREAUTH_FAILED, "Preauthentication failed"
+error_code PREAUTH_REQUIRED, "Additional pre-authentication required"
+error_code SERVER_NOMATCH, "Requested server and ticket don't match"
+error_code KDC_ERR_MUST_USE_USER2USER, "Server principal valid for user2user only"
+error_code PATH_NOT_ACCEPTED, "KDC Policy rejects transited path"
+error_code SVC_UNAVAILABLE, "A service is not available"
+
+index 31
+prefix KRB5KRB_AP
+error_code ERR_BAD_INTEGRITY, "Decrypt integrity check failed"
+error_code ERR_TKT_EXPIRED, "Ticket expired"
+error_code ERR_TKT_NYV, "Ticket not yet valid"
+error_code ERR_REPEAT, "Request is a replay"
+error_code ERR_NOT_US, "The ticket isn't for us"
+error_code ERR_BADMATCH, "Ticket/authenticator don't match"
+error_code ERR_SKEW, "Clock skew too great"
+error_code ERR_BADADDR, "Incorrect net address"
+error_code ERR_BADVERSION, "Protocol version mismatch"
+error_code ERR_MSG_TYPE, "Invalid message type"
+error_code ERR_MODIFIED, "Message stream modified"
+error_code ERR_BADORDER, "Message out of order"
+error_code ERR_ILL_CR_TKT, "Invalid cross-realm ticket"
+error_code ERR_BADKEYVER, "Key version is not available"
+error_code ERR_NOKEY, "Service key not available"
+error_code ERR_MUT_FAIL, "Mutual authentication failed"
+error_code ERR_BADDIRECTION, "Incorrect message direction"
+error_code ERR_METHOD, "Alternative authentication method required"
+error_code ERR_BADSEQ, "Incorrect sequence number in message"
+error_code ERR_INAPP_CKSUM, "Inappropriate type of checksum in message"
+error_code PATH_NOT_ACCEPTED, "Policy rejects transited path"
+
+prefix KRB5KRB_ERR
+error_code RESPONSE_TOO_BIG, "Response too big for UDP, retry with TCP"
+# 53-59 are reserved
+index 60
+error_code GENERIC, "Generic error (see e-text)"
+error_code FIELD_TOOLONG, "Field is too long for this implementation"
+
+# pkinit
+index 62
+prefix KRB5_KDC_ERR
+error_code CLIENT_NOT_TRUSTED, "Client not trusted"
+error_code KDC_NOT_TRUSTED, "KDC not trusted"
+error_code INVALID_SIG, "Invalid signature"
+error_code DH_KEY_PARAMETERS_NOT_ACCEPTED, "DH parameters not accepted"
+
+index 68
+prefix KRB5_KDC_ERR
+error_code WRONG_REALM, "Wrong realm"
+
+index 69
+prefix KRB5_AP_ERR
+error_code USER_TO_USER_REQUIRED, "User to user required"
+
+index 70
+prefix KRB5_KDC_ERR
+error_code CANT_VERIFY_CERTIFICATE, "Cannot verify certificate"
+error_code INVALID_CERTIFICATE, "Certificate invalid"
+error_code REVOKED_CERTIFICATE, "Certificate revoked"
+error_code REVOCATION_STATUS_UNKNOWN, "Revocation status unknown"
+error_code REVOCATION_STATUS_UNAVAILABLE, "Revocation status unavaible"
+error_code CLIENT_NAME_MISMATCH, "Client name mismatch in certificate"
+error_code INCONSISTENT_KEY_PURPOSE, "Inconsistent key purpose"
+error_code DIGEST_IN_CERT_NOT_ACCEPTED, "Digest in certificate not accepted"
+error_code PA_CHECKSUM_MUST_BE_INCLUDED, "paChecksum must be included"
+error_code DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signedData not accepted"
+error_code PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
+
+## these are never used
+#index 80
+#prefix KRB5_IAKERB
+#error_code ERR_KDC_NOT_FOUND, "IAKERB proxy could not find a KDC"
+#error_code ERR_KDC_NO_RESPONSE, "IAKERB proxy never reeived a response from a KDC"
+
+# 82-127 are reserved
+
+index 128
+prefix
+error_code KRB5_ERR_RCSID, "$Id$"
+
+error_code KRB5_LIBOS_BADLOCKFLAG, "Invalid flag for file lock mode"
+error_code KRB5_LIBOS_CANTREADPWD, "Cannot read password"
+error_code KRB5_LIBOS_BADPWDMATCH, "Password mismatch"
+error_code KRB5_LIBOS_PWDINTR, "Password read interrupted"
+
+error_code KRB5_PARSE_ILLCHAR, "Invalid character in component name"
+error_code KRB5_PARSE_MALFORMED, "Malformed representation of principal"
+
+error_code KRB5_CONFIG_CANTOPEN, "Can't open/find configuration file"
+error_code KRB5_CONFIG_BADFORMAT, "Improper format of configuration file"
+error_code KRB5_CONFIG_NOTENUFSPACE, "Insufficient space to return complete information"
+
+error_code KRB5_BADMSGTYPE, "Invalid message type specified for encoding"
+
+error_code KRB5_CC_BADNAME, "Credential cache name malformed"
+error_code KRB5_CC_UNKNOWN_TYPE, "Unknown credential cache type"
+error_code KRB5_CC_NOTFOUND, "Matching credential not found"
+error_code KRB5_CC_END, "End of credential cache reached"
+
+error_code KRB5_NO_TKT_SUPPLIED, "Request did not supply a ticket"
+
+error_code KRB5KRB_AP_WRONG_PRINC, "Wrong principal in request"
+error_code KRB5KRB_AP_ERR_TKT_INVALID, "Ticket has invalid flag set"
+
+error_code KRB5_PRINC_NOMATCH, "Requested principal and ticket don't match"
+error_code KRB5_KDCREP_MODIFIED, "KDC reply did not match expectations"
+error_code KRB5_KDCREP_SKEW, "Clock skew too great in KDC reply"
+error_code KRB5_IN_TKT_REALM_MISMATCH, "Client/server realm mismatch in initial ticket request"
+
+error_code KRB5_PROG_ETYPE_NOSUPP, "Program lacks support for encryption type"
+error_code KRB5_PROG_KEYTYPE_NOSUPP, "Program lacks support for key type"
+error_code KRB5_WRONG_ETYPE, "Requested encryption type not used in message"
+error_code KRB5_PROG_SUMTYPE_NOSUPP, "Program lacks support for checksum type"
+
+error_code KRB5_REALM_UNKNOWN, "Cannot find KDC for requested realm"
+error_code KRB5_SERVICE_UNKNOWN, "Kerberos service unknown"
+error_code KRB5_KDC_UNREACH, "Cannot contact any KDC for requested realm"
+error_code KRB5_NO_LOCALNAME, "No local name found for principal name"
+
+error_code KRB5_MUTUAL_FAILED, "Mutual authentication failed"
+
+# some of these should be combined/supplanted by system codes
+
+error_code KRB5_RC_TYPE_EXISTS, "Replay cache type is already registered"
+error_code KRB5_RC_MALLOC, "No more memory to allocate (in replay cache code)"
+error_code KRB5_RC_TYPE_NOTFOUND, "Replay cache type is unknown"
+error_code KRB5_RC_UNKNOWN, "Generic unknown RC error"
+error_code KRB5_RC_REPLAY, "Message is a replay"
+error_code KRB5_RC_IO, "Replay I/O operation failed XXX"
+error_code KRB5_RC_NOIO, "Replay cache type does not support non-volatile storage"
+error_code KRB5_RC_PARSE, "Replay cache name parse/format error"
+
+error_code KRB5_RC_IO_EOF, "End-of-file on replay cache I/O"
+error_code KRB5_RC_IO_MALLOC, "No more memory to allocate (in replay cache I/O code)"
+error_code KRB5_RC_IO_PERM, "Permission denied in replay cache code"
+error_code KRB5_RC_IO_IO, "I/O error in replay cache i/o code"
+error_code KRB5_RC_IO_UNKNOWN, "Generic unknown RC/IO error"
+error_code KRB5_RC_IO_SPACE, "Insufficient system space to store replay information"
+
+error_code KRB5_TRANS_CANTOPEN, "Can't open/find realm translation file"
+error_code KRB5_TRANS_BADFORMAT, "Improper format of realm translation file"
+
+error_code KRB5_LNAME_CANTOPEN, "Can't open/find lname translation database"
+error_code KRB5_LNAME_NOTRANS, "No translation available for requested principal"
+error_code KRB5_LNAME_BADFORMAT, "Improper format of translation database entry"
+
+error_code KRB5_CRYPTO_INTERNAL, "Cryptosystem internal error"
+
+error_code KRB5_KT_BADNAME, "Key table name malformed"
+error_code KRB5_KT_UNKNOWN_TYPE, "Unknown Key table type"
+error_code KRB5_KT_NOTFOUND, "Key table entry not found"
+error_code KRB5_KT_END, "End of key table reached"
+error_code KRB5_KT_NOWRITE, "Cannot write to specified key table"
+error_code KRB5_KT_IOERR, "Error writing to key table"
+
+error_code KRB5_NO_TKT_IN_RLM, "Cannot find ticket for requested realm"
+error_code KRB5DES_BAD_KEYPAR, "DES key has bad parity"
+error_code KRB5DES_WEAK_KEY, "DES key is a weak key"
+
+error_code KRB5_BAD_ENCTYPE, "Bad encryption type"
+error_code KRB5_BAD_KEYSIZE, "Key size is incompatible with encryption type"
+error_code KRB5_BAD_MSIZE, "Message size is incompatible with encryption type"
+
+error_code KRB5_CC_TYPE_EXISTS, "Credentials cache type is already registered."
+error_code KRB5_KT_TYPE_EXISTS, "Key table type is already registered."
+
+error_code KRB5_CC_IO, "Credentials cache I/O operation failed XXX"
+error_code KRB5_FCC_PERM, "Credentials cache file permissions incorrect"
+error_code KRB5_FCC_NOFILE, "No credentials cache file found"
+error_code KRB5_FCC_INTERNAL, "Internal file credentials cache error"
+error_code KRB5_CC_WRITE, "Error writing to credentials cache file"
+error_code KRB5_CC_NOMEM, "No more memory to allocate (in credentials cache code)"
+error_code KRB5_CC_FORMAT, "Bad format in credentials cache"
+error_code KRB5_CC_NOT_KTYPE, "No credentials found with supported encryption types"
+
+# errors for dual tgt library calls
+error_code KRB5_INVALID_FLAGS, "Invalid KDC option combination (library internal error)"
+error_code KRB5_NO_2ND_TKT, "Request missing second ticket"
+
+error_code KRB5_NOCREDS_SUPPLIED, "No credentials supplied to library routine"
+
+# errors for sendauth (and recvauth)
+
+error_code KRB5_SENDAUTH_BADAUTHVERS, "Bad sendauth version was sent"
+error_code KRB5_SENDAUTH_BADAPPLVERS, "Bad application version was sent (via sendauth)"
+error_code KRB5_SENDAUTH_BADRESPONSE, "Bad response (during sendauth exchange)"
+error_code KRB5_SENDAUTH_REJECTED, "Server rejected authentication (during sendauth exchange)"
+
+# errors for preauthentication
+
+error_code KRB5_PREAUTH_BAD_TYPE, "Unsupported preauthentication type"
+error_code KRB5_PREAUTH_NO_KEY, "Required preauthentication key not supplied"
+error_code KRB5_PREAUTH_FAILED, "Generic preauthentication failure"
+
+# version number errors
+
+error_code KRB5_RCACHE_BADVNO, "Unsupported replay cache format version number"
+error_code KRB5_CCACHE_BADVNO, "Unsupported credentials cache format version number"
+error_code KRB5_KEYTAB_BADVNO, "Unsupported key table format version number"
+
+#
+#
+
+error_code KRB5_PROG_ATYPE_NOSUPP, "Program lacks support for address type"
+error_code KRB5_RC_REQUIRED, "Message replay detection requires rcache parameter"
+error_code KRB5_ERR_BAD_HOSTNAME, "Hostname cannot be canonicalized"
+error_code KRB5_ERR_HOST_REALM_UNKNOWN, "Cannot determine realm for host"
+error_code KRB5_SNAME_UNSUPP_NAMETYPE, "Conversion to service principal undefined for name type"
+
+error_code KRB5KRB_AP_ERR_V4_REPLY, "Initial Ticket response appears to be Version 4"
+error_code KRB5_REALM_CANT_RESOLVE, "Cannot resolve KDC for requested realm"
+error_code KRB5_TKT_NOT_FORWARDABLE, "Requesting ticket can't get forwardable tickets"
+error_code KRB5_FWD_BAD_PRINCIPAL, "Bad principal name while trying to forward credentials"
+
+error_code KRB5_GET_IN_TKT_LOOP, "Looping detected inside krb5_get_in_tkt"
+error_code KRB5_CONFIG_NODEFREALM, "Configuration file does not specify default realm"
+
+error_code KRB5_SAM_UNSUPPORTED, "Bad SAM flags in obtain_sam_padata"
+error_code KRB5_SAM_INVALID_ETYPE, "Invalid encryption type in SAM challenge"
+error_code KRB5_SAM_NO_CHECKSUM, "Missing checksum in SAM challenge"
+error_code KRB5_SAM_BAD_CHECKSUM, "Bad checksum in SAM challenge"
+
+index 238
+error_code KRB5_OBSOLETE_FN, "Program called an obsolete, deleted function"
+
+index 245
+error_code KRB5_ERR_BAD_S2K_PARAMS, "Invalid key generation parameters from KDC"
+error_code KRB5_ERR_NO_SERVICE, "Service not available"
+error_code KRB5_CC_NOSUPP, "Credential cache function not supported"
+error_code KRB5_DELTAT_BADFORMAT, "Invalid format of Kerberos lifetime or clock skew string"
+error_code KRB5_PLUGIN_NO_HANDLE, "Supplied data not handled by this plugin"
+error_code KRB5_PLUGIN_OP_NOTSUPP, "Plugin does not support the operaton"
+
+end
diff --git a/source4/heimdal/lib/krb5/krb5_locl.h b/source4/heimdal/lib/krb5/krb5_locl.h
new file mode 100644
index 0000000000..73075bf56c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5_locl.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_LOCL_H__
+#define __KRB5_LOCL_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_PWD_H
+#undef _POSIX_PTHREAD_SEMANTICS
+/* This gets us the 5-arg getpwnam_r on Solaris 9. */
+#define _POSIX_PTHREAD_SEMANTICS
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <time.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef _AIX
+struct ether_addr;
+struct mbuf;
+struct sockaddr_dl;
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#ifdef HAVE_CRYPT_H
+#undef des_encrypt
+#define des_encrypt wingless_pigs_mostly_fail_to_fly
+#include <crypt.h>
+#undef des_encrypt
+#endif
+
+#ifdef HAVE_DOOR_CREATE
+#include <door.h>
+#endif
+
+#include <roken.h>
+#include <parse_time.h>
+#include <base64.h>
+
+#include <wind.h>
+
+#define HC_DEPRECATED_CRYPTO
+#include "crypto-headers.h"
+
+
+#include <krb5_asn1.h>
+
+struct send_to_kdc;
+
+/* XXX glue for pkinit */
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+struct ContentInfo;
+struct AlgorithmIdentifier;
+typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx;
+struct krb5_dh_moduli;
+
+/* v4 glue */
+struct _krb5_krb_auth_data;
+
+#include <der.h>
+
+#include <krb5.h>
+#include <krb5_err.h>
+#include <asn1_err.h>
+#ifdef PKINIT
+#include <hx509.h>
+#endif
+#include <krb5-private.h>
+
+#include "heim_threads.h"
+
+#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
+
+/* should this be public? */
+#define KEYTAB_DEFAULT "FILE:" SYSCONFDIR "/krb5.keytab"
+#define KEYTAB_DEFAULT_MODIFY "FILE:" SYSCONFDIR "/krb5.keytab"
+
+#define MODULI_FILE SYSCONFDIR "/krb5.moduli"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+
+#define KRB5_BUFSIZ 1024
+
+typedef enum {
+ KRB5_INIT_CREDS_TRISTATE_UNSET = 0,
+ KRB5_INIT_CREDS_TRISTATE_TRUE,
+ KRB5_INIT_CREDS_TRISTATE_FALSE
+} krb5_get_init_creds_tristate;
+
+struct _krb5_get_init_creds_opt_private {
+ int refcount;
+ /* ENC_TIMESTAMP */
+ const char *password;
+ krb5_s2k_proc key_proc;
+ /* PA_PAC_REQUEST */
+ krb5_get_init_creds_tristate req_pac;
+ /* PKINIT */
+ krb5_pk_init_ctx pk_init_ctx;
+ KRB_ERROR *error;
+ krb5_get_init_creds_tristate addressless;
+ int flags;
+#define KRB5_INIT_CREDS_CANONICALIZE 1
+#define KRB5_INIT_CREDS_NO_C_CANON_CHECK 2
+};
+
+typedef struct krb5_context_data {
+ krb5_enctype *etypes;
+ krb5_enctype *etypes_des;
+ char **default_realms;
+ time_t max_skew;
+ time_t kdc_timeout;
+ unsigned max_retries;
+ int32_t kdc_sec_offset;
+ int32_t kdc_usec_offset;
+ krb5_config_section *cf;
+ struct et_list *et_list;
+ struct krb5_log_facility *warn_dest;
+ krb5_cc_ops *cc_ops;
+ int num_cc_ops;
+ const char *http_proxy;
+ const char *time_fmt;
+ krb5_boolean log_utc;
+ const char *default_keytab;
+ const char *default_keytab_modify;
+ krb5_boolean use_admin_kdc;
+ krb5_addresses *extra_addresses;
+ krb5_boolean scan_interfaces; /* `ifconfig -a' */
+ krb5_boolean srv_lookup; /* do SRV lookups */
+ krb5_boolean srv_try_txt; /* try TXT records also */
+ int32_t fcache_vno; /* create cache files w/ this
+ version */
+ int num_kt_types; /* # of registered keytab types */
+ struct krb5_keytab_data *kt_types; /* registered keytab types */
+ const char *date_fmt;
+ char *error_string;
+ krb5_error_code error_code;
+ krb5_addresses *ignore_addresses;
+ char *default_cc_name;
+ char *default_cc_name_env;
+ int default_cc_name_set;
+ void *mutex; /* protects error_string/error_buf */
+ int large_msg_size;
+ int flags;
+#define KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME 1
+#define KRB5_CTX_F_CHECK_PAC 2
+ struct send_to_kdc *send_to_kdc;
+} krb5_context_data;
+
+#define KRB5_DEFAULT_CCNAME_FILE "FILE:/tmp/krb5cc_%{uid}"
+#define KRB5_DEFAULT_CCNAME_API "API:"
+#define KRB5_DEFAULT_CCNAME_KCM "KCM:%{uid}"
+
+#define EXTRACT_TICKET_ALLOW_CNAME_MISMATCH 1
+#define EXTRACT_TICKET_ALLOW_SERVER_MISMATCH 2
+#define EXTRACT_TICKET_MATCH_REALM 4
+#define EXTRACT_TICKET_AS_REQ 8
+
+/*
+ * Configurable options
+ */
+
+#ifndef KRB5_DEFAULT_CCTYPE
+#ifdef __APPLE__
+#define KRB5_DEFAULT_CCTYPE (&krb5_acc_ops)
+#else
+#define KRB5_DEFAULT_CCTYPE (&krb5_fcc_ops)
+#endif
+#endif
+
+#ifndef KRB5_ADDRESSLESS_DEFAULT
+#define KRB5_ADDRESSLESS_DEFAULT TRUE
+#endif
+
+#ifdef PKINIT
+
+struct krb5_pk_identity {
+ hx509_context hx509ctx;
+ hx509_verify_ctx verify_ctx;
+ hx509_certs certs;
+ hx509_certs anchors;
+ hx509_certs certpool;
+ hx509_revoke_ctx revokectx;
+};
+
+enum krb5_pk_type {
+ PKINIT_WIN2K = 1,
+ PKINIT_27 = 2
+};
+
+#endif /* PKINIT */
+
+#endif /* __KRB5_LOCL_H__ */
diff --git a/source4/heimdal/lib/krb5/krb_err.et b/source4/heimdal/lib/krb5/krb_err.et
new file mode 100644
index 0000000000..f7dbb6ce7a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb_err.et
@@ -0,0 +1,63 @@
+#
+# Error messages for the krb4 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id: krb_err.et,v 1.7 1998/03/29 14:19:52 bg Exp $"
+
+error_table krb
+
+prefix KRB4ET
+ec KSUCCESS, "Kerberos 4 successful"
+ec KDC_NAME_EXP, "Kerberos 4 principal expired"
+ec KDC_SERVICE_EXP, "Kerberos 4 service expired"
+ec KDC_AUTH_EXP, "Kerberos 4 auth expired"
+ec KDC_PKT_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_P_MKEY_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_S_MKEY_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_BYTE_ORDER, "Kerberos 4 byte order unknown"
+ec KDC_PR_UNKNOWN, "Kerberos 4 principal unknown"
+ec KDC_PR_N_UNIQUE, "Kerberos 4 principal not unique"
+ec KDC_NULL_KEY, "Kerberos 4 principal has null key"
+index 20
+ec KDC_GEN_ERR, "Generic error from KDC (Kerberos 4)"
+ec GC_TKFIL, "Can't read Kerberos 4 ticket file"
+ec GC_NOTKT, "Can't find Kerberos 4 ticket or TGT"
+index 26
+ec MK_AP_TGTEXP, "Kerberos 4 TGT Expired"
+index 31
+ec RD_AP_UNDEC, "Kerberos 4: Can't decode authenticator"
+ec RD_AP_EXP, "Kerberos 4 ticket expired"
+ec RD_AP_NYV, "Kerberos 4 ticket not yet valid"
+ec RD_AP_REPEAT, "Kerberos 4: Repeated request"
+ec RD_AP_NOT_US, "The Kerberos 4 ticket isn't for us"
+ec RD_AP_INCON, "Kerberos 4 request inconsistent"
+ec RD_AP_TIME, "Kerberos 4: delta_t too big"
+ec RD_AP_BADD, "Kerberos 4: incorrect net address"
+ec RD_AP_VERSION, "Kerberos protocol not version 4"
+ec RD_AP_MSG_TYPE, "Kerberos 4: invalid msg type"
+ec RD_AP_MODIFIED, "Kerberos 4: message stream modified"
+ec RD_AP_ORDER, "Kerberos 4: message out of order"
+ec RD_AP_UNAUTHOR, "Kerberos 4: unauthorized request"
+index 51
+ec GT_PW_NULL, "Kerberos 4: current PW is null"
+ec GT_PW_BADPW, "Kerberos 4: Incorrect current password"
+ec GT_PW_PROT, "Kerberos 4 protocol error"
+ec GT_PW_KDCERR, "Error returned by KDC (Kerberos 4)"
+ec GT_PW_NULLTKT, "Null Kerberos 4 ticket returned by KDC"
+ec SKDC_RETRY, "Kerberos 4: Retry count exceeded"
+ec SKDC_CANT, "Kerberos 4: Can't send request"
+index 61
+ec INTK_W_NOTALL, "Kerberos 4: not all tickets returned"
+ec INTK_BADPW, "Kerberos 4: incorrect password"
+ec INTK_PROT, "Kerberos 4: Protocol Error"
+index 70
+ec INTK_ERR, "Other error in Kerberos 4"
+ec AD_NOTGT, "Don't have Kerberos 4 ticket-granting ticket"
+index 76
+ec NO_TKT_FIL, "No Kerberos 4 ticket file found"
+ec TKT_FIL_ACC, "Couldn't access Kerberos 4 ticket file"
+ec TKT_FIL_LCK, "Couldn't lock Kerberos 4 ticket file"
+ec TKT_FIL_FMT, "Bad Kerberos 4 ticket file format"
+ec TKT_FIL_INI, "Kerberos 4: tf_init not called first"
+ec KNAME_FMT, "Bad Kerberos 4 name format"
diff --git a/source4/heimdal/lib/krb5/krbhst.c b/source4/heimdal/lib/krb5/krbhst.c
new file mode 100644
index 0000000000..8e49818c50
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krbhst.c
@@ -0,0 +1,1014 @@
+/*
+ * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <resolve.h>
+#include "locate_plugin.h"
+
+RCSID("$Id$");
+
+static int
+string_to_proto(const char *string)
+{
+ if(strcasecmp(string, "udp") == 0)
+ return KRB5_KRBHST_UDP;
+ else if(strcasecmp(string, "tcp") == 0)
+ return KRB5_KRBHST_TCP;
+ else if(strcasecmp(string, "http") == 0)
+ return KRB5_KRBHST_HTTP;
+ return -1;
+}
+
+/*
+ * set `res' and `count' to the result of looking up SRV RR in DNS for
+ * `proto', `proto', `realm' using `dns_type'.
+ * if `port' != 0, force that port number
+ */
+
+static krb5_error_code
+srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
+ const char *realm, const char *dns_type,
+ const char *proto, const char *service, int port)
+{
+ char domain[1024];
+ struct dns_reply *r;
+ struct resource_record *rr;
+ int num_srv;
+ int proto_num;
+ int def_port;
+
+ *res = NULL;
+ *count = 0;
+
+ proto_num = string_to_proto(proto);
+ if(proto_num < 0) {
+ krb5_set_error_message(context, EINVAL,
+ "unknown protocol `%s'", proto);
+ return EINVAL;
+ }
+
+ if(proto_num == KRB5_KRBHST_HTTP)
+ def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
+ else if(port == 0)
+ def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
+ else
+ def_port = port;
+
+ snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
+
+ r = dns_lookup(domain, dns_type);
+ if(r == NULL)
+ return KRB5_KDC_UNREACH;
+
+ for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+ if(rr->type == T_SRV)
+ num_srv++;
+
+ *res = malloc(num_srv * sizeof(**res));
+ if(*res == NULL) {
+ dns_free_data(r);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ dns_srv_order(r);
+
+ for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+ if(rr->type == T_SRV) {
+ krb5_krbhst_info *hi;
+ size_t len = strlen(rr->u.srv->target);
+
+ hi = calloc(1, sizeof(*hi) + len);
+ if(hi == NULL) {
+ dns_free_data(r);
+ while(--num_srv >= 0)
+ free((*res)[num_srv]);
+ free(*res);
+ *res = NULL;
+ return ENOMEM;
+ }
+ (*res)[num_srv++] = hi;
+
+ hi->proto = proto_num;
+
+ hi->def_port = def_port;
+ if (port != 0)
+ hi->port = port;
+ else
+ hi->port = rr->u.srv->port;
+
+ strlcpy(hi->hostname, rr->u.srv->target, len + 1);
+ }
+
+ *count = num_srv;
+
+ dns_free_data(r);
+ return 0;
+}
+
+
+struct krb5_krbhst_data {
+ char *realm;
+ unsigned int flags;
+ int def_port;
+ int port; /* hardwired port number if != 0 */
+#define KD_CONFIG 1
+#define KD_SRV_UDP 2
+#define KD_SRV_TCP 4
+#define KD_SRV_HTTP 8
+#define KD_FALLBACK 16
+#define KD_CONFIG_EXISTS 32
+#define KD_LARGE_MSG 64
+#define KD_PLUGIN 128
+ krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
+ krb5_krbhst_info**);
+
+ unsigned int fallback_count;
+
+ struct krb5_krbhst_info *hosts, **index, **end;
+};
+
+static krb5_boolean
+krbhst_empty(const struct krb5_krbhst_data *kd)
+{
+ return kd->index == &kd->hosts;
+}
+
+/*
+ * Return the default protocol for the `kd' (either TCP or UDP)
+ */
+
+static int
+krbhst_get_default_proto(struct krb5_krbhst_data *kd)
+{
+ if (kd->flags & KD_LARGE_MSG)
+ return KRB5_KRBHST_TCP;
+ return KRB5_KRBHST_UDP;
+}
+
+
+/*
+ * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
+ * and forcing it to `port' if port != 0
+ */
+
+static struct krb5_krbhst_info*
+parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *spec, int def_port, int port)
+{
+ const char *p = spec;
+ struct krb5_krbhst_info *hi;
+
+ hi = calloc(1, sizeof(*hi) + strlen(spec));
+ if(hi == NULL)
+ return NULL;
+
+ hi->proto = krbhst_get_default_proto(kd);
+
+ if(strncmp(p, "http://", 7) == 0){
+ hi->proto = KRB5_KRBHST_HTTP;
+ p += 7;
+ } else if(strncmp(p, "http/", 5) == 0) {
+ hi->proto = KRB5_KRBHST_HTTP;
+ p += 5;
+ def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
+ }else if(strncmp(p, "tcp/", 4) == 0){
+ hi->proto = KRB5_KRBHST_TCP;
+ p += 4;
+ } else if(strncmp(p, "udp/", 4) == 0) {
+ p += 4;
+ }
+
+ if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
+ free(hi);
+ return NULL;
+ }
+ /* get rid of trailing /, and convert to lower case */
+ hi->hostname[strcspn(hi->hostname, "/")] = '\0';
+ strlwr(hi->hostname);
+
+ hi->port = hi->def_port = def_port;
+ if(p != NULL) {
+ char *end;
+ hi->port = strtol(p, &end, 0);
+ if(end == p) {
+ free(hi);
+ return NULL;
+ }
+ }
+ if (port)
+ hi->port = port;
+ return hi;
+}
+
+void
+_krb5_free_krbhst_info(krb5_krbhst_info *hi)
+{
+ if (hi->ai != NULL)
+ freeaddrinfo(hi->ai);
+ free(hi);
+}
+
+krb5_error_code
+_krb5_krbhost_info_move(krb5_context context,
+ krb5_krbhst_info *from,
+ krb5_krbhst_info **to)
+{
+ size_t hostnamelen = strlen(from->hostname);
+ /* trailing NUL is included in structure */
+ *to = calloc(1, sizeof(**to) + hostnamelen);
+ if(*to == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*to)->proto = from->proto;
+ (*to)->port = from->port;
+ (*to)->def_port = from->def_port;
+ (*to)->ai = from->ai;
+ from->ai = NULL;
+ (*to)->next = NULL;
+ memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
+ return 0;
+}
+
+
+static void
+append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
+{
+ struct krb5_krbhst_info *h;
+
+ for(h = kd->hosts; h; h = h->next)
+ if(h->proto == host->proto &&
+ h->port == host->port &&
+ strcmp(h->hostname, host->hostname) == 0) {
+ _krb5_free_krbhst_info(host);
+ return;
+ }
+ *kd->end = host;
+ kd->end = &host->next;
+}
+
+static krb5_error_code
+append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *host, int def_port, int port)
+{
+ struct krb5_krbhst_info *hi;
+
+ hi = parse_hostspec(context, kd, host, def_port, port);
+ if(hi == NULL)
+ return ENOMEM;
+
+ append_host_hostinfo(kd, hi);
+ return 0;
+}
+
+/*
+ * return a readable representation of `host' in `hostname, hostlen'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
+ char *hostname, size_t hostlen)
+{
+ const char *proto = "";
+ char portstr[7] = "";
+ if(host->proto == KRB5_KRBHST_TCP)
+ proto = "tcp/";
+ else if(host->proto == KRB5_KRBHST_HTTP)
+ proto = "http://";
+ if(host->port != host->def_port)
+ snprintf(portstr, sizeof(portstr), ":%d", host->port);
+ snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr);
+ return 0;
+}
+
+/*
+ * create a getaddrinfo `hints' based on `proto'
+ */
+
+static void
+make_hints(struct addrinfo *hints, int proto)
+{
+ memset(hints, 0, sizeof(*hints));
+ hints->ai_family = AF_UNSPEC;
+ switch(proto) {
+ case KRB5_KRBHST_UDP :
+ hints->ai_socktype = SOCK_DGRAM;
+ break;
+ case KRB5_KRBHST_HTTP :
+ case KRB5_KRBHST_TCP :
+ hints->ai_socktype = SOCK_STREAM;
+ break;
+ }
+}
+
+/*
+ * return an `struct addrinfo *' in `ai' corresponding to the information
+ * in `host'. free:ing is handled by krb5_krbhst_free.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
+ struct addrinfo **ai)
+{
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+ int ret;
+
+ if (host->ai == NULL) {
+ make_hints(&hints, host->proto);
+ snprintf (portstr, sizeof(portstr), "%d", host->port);
+ ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
+ if (ret)
+ return krb5_eai_to_heim_errno(ret, errno);
+ }
+ *ai = host->ai;
+ return 0;
+}
+
+static krb5_boolean
+get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
+{
+ struct krb5_krbhst_info *hi = *kd->index;
+ if(hi != NULL) {
+ *host = hi;
+ kd->index = &(*kd->index)->next;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *proto, const char *service)
+{
+ krb5_krbhst_info **res;
+ int count, i;
+
+ if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
+ kd->port))
+ return;
+ for(i = 0; i < count; i++)
+ append_host_hostinfo(kd, res[i]);
+ free(res);
+}
+
+/*
+ * read the configuration for `conf_string', defaulting to kd->def_port and
+ * forcing it to `kd->port' if kd->port != 0
+ */
+
+static void
+config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *conf_string)
+{
+ int i;
+
+ char **hostlist;
+ hostlist = krb5_config_get_strings(context, NULL,
+ "realms", kd->realm, conf_string, NULL);
+
+ if(hostlist == NULL)
+ return;
+ kd->flags |= KD_CONFIG_EXISTS;
+ for(i = 0; hostlist && hostlist[i] != NULL; i++)
+ append_host_string(context, kd, hostlist[i], kd->def_port, kd->port);
+
+ krb5_config_free_strings(hostlist);
+}
+
+/*
+ * as a fallback, look for `serv_string.kd->realm' (typically
+ * kerberos.REALM, kerberos-1.REALM, ...
+ * `port' is the default port for the service, and `proto' the
+ * protocol
+ */
+
+static krb5_error_code
+fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *serv_string, int port, int proto)
+{
+ char *host;
+ int ret;
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+
+ /*
+ * Don't try forever in case the DNS server keep returning us
+ * entries (like wildcard entries or the .nu TLD)
+ */
+ if(kd->fallback_count >= 5) {
+ kd->flags |= KD_FALLBACK;
+ return 0;
+ }
+
+ if(kd->fallback_count == 0)
+ asprintf(&host, "%s.%s.", serv_string, kd->realm);
+ else
+ asprintf(&host, "%s-%d.%s.",
+ serv_string, kd->fallback_count, kd->realm);
+
+ if (host == NULL)
+ return ENOMEM;
+
+ make_hints(&hints, proto);
+ snprintf(portstr, sizeof(portstr), "%d", port);
+ ret = getaddrinfo(host, portstr, &hints, &ai);
+ if (ret) {
+ /* no more hosts, so we're done here */
+ free(host);
+ kd->flags |= KD_FALLBACK;
+ } else {
+ struct krb5_krbhst_info *hi;
+ size_t hostlen = strlen(host);
+
+ hi = calloc(1, sizeof(*hi) + hostlen);
+ if(hi == NULL) {
+ free(host);
+ return ENOMEM;
+ }
+
+ hi->proto = proto;
+ hi->port = hi->def_port = port;
+ hi->ai = ai;
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
+ free(host);
+ append_host_hostinfo(kd, hi);
+ kd->fallback_count++;
+ }
+ return 0;
+}
+
+/*
+ * Fetch hosts from plugin
+ */
+
+static krb5_error_code
+add_locate(void *ctx, int type, struct sockaddr *addr)
+{
+ struct krb5_krbhst_info *hi;
+ struct krb5_krbhst_data *kd = ctx;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ struct addrinfo hints, *ai;
+ socklen_t socklen;
+ size_t hostlen;
+ int ret;
+
+ socklen = socket_sockaddr_size(addr);
+
+ ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ if (ret != 0)
+ return 0;
+
+ make_hints(&hints, krbhst_get_default_proto(kd));
+ ret = getaddrinfo(host, port, &hints, &ai);
+ if (ret)
+ return 0;
+
+ hostlen = strlen(host);
+
+ hi = calloc(1, sizeof(*hi) + hostlen);
+ if(hi == NULL)
+ return ENOMEM;
+
+ hi->proto = krbhst_get_default_proto(kd);
+ hi->port = hi->def_port = socket_get_port(addr);
+ hi->ai = ai;
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
+ append_host_hostinfo(kd, hi);
+
+ return 0;
+}
+
+static void
+plugin_get_hosts(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ enum locate_service_type type)
+{
+ struct krb5_plugin *list = NULL, *e;
+ krb5_error_code ret;
+
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_LOCATE, &list);
+ if(ret != 0 || list == NULL)
+ return;
+
+ kd->flags |= KD_CONFIG_EXISTS;
+
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+ krb5plugin_service_locate_ftable *service;
+ void *ctx;
+
+ service = _krb5_plugin_get_symbol(e);
+ if (service->minor_version != 0)
+ continue;
+
+ (*service->init)(context, &ctx);
+ ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
+ (*service->fini)(ctx);
+ if (ret && ret != KRB5_PLUGIN_NO_HANDLE) {
+ krb5_set_error_message(context, ret,
+ "Locate plugin failed to lookup: %d", ret);
+ break;
+ }
+ }
+ _krb5_plugin_free(list);
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+kdc_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kdc);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "kdc");
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
+ srv_get_hosts(context, kd, "udp", "kerberos");
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "kerberos");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ if((kd->flags & KD_SRV_HTTP) == 0) {
+ srv_get_hosts(context, kd, "http", "kerberos");
+ kd->flags |= KD_SRV_HTTP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ while((kd->flags & KD_FALLBACK) == 0) {
+ ret = fallback_get_hosts(context, kd, "kerberos",
+ kd->def_port,
+ krbhst_get_default_proto(kd));
+ if(ret)
+ return ret;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+admin_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kadmin);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "admin_server");
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "kerberos-adm");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ if (krbhst_empty(kd)
+ && (kd->flags & KD_FALLBACK) == 0) {
+ ret = fallback_get_hosts(context, kd, "kerberos",
+ kd->def_port,
+ krbhst_get_default_proto(kd));
+ if(ret)
+ return ret;
+ kd->flags |= KD_FALLBACK;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+kpasswd_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kpasswd);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "kpasswd_server");
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_UDP) == 0) {
+ srv_get_hosts(context, kd, "udp", "kpasswd");
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "kpasswd");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ /* no matches -> try admin */
+
+ if (krbhst_empty(kd)) {
+ kd->flags = 0;
+ kd->port = kd->def_port;
+ kd->get_next = admin_get_next;
+ ret = (*kd->get_next)(context, kd, host);
+ if (ret == 0)
+ (*host)->proto = krbhst_get_default_proto(kd);
+ return ret;
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+krb524_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_krb524);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "krb524_server");
+ if(get_next(kd, host))
+ return 0;
+ kd->flags |= KD_CONFIG;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_UDP) == 0) {
+ srv_get_hosts(context, kd, "udp", "krb524");
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "krb524");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ /* no matches -> try kdc */
+
+ if (krbhst_empty(kd)) {
+ kd->flags = 0;
+ kd->port = kd->def_port;
+ kd->get_next = kdc_get_next;
+ return (*kd->get_next)(context, kd, host);
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static struct krb5_krbhst_data*
+common_init(krb5_context context,
+ const char *realm,
+ int flags)
+{
+ struct krb5_krbhst_data *kd;
+
+ if((kd = calloc(1, sizeof(*kd))) == NULL)
+ return NULL;
+
+ if((kd->realm = strdup(realm)) == NULL) {
+ free(kd);
+ return NULL;
+ }
+
+ /* For 'realms' without a . do not even think of going to DNS */
+ if (!strchr(realm, '.'))
+ kd->flags |= KD_CONFIG_EXISTS;
+
+ if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG)
+ kd->flags |= KD_LARGE_MSG;
+ kd->end = kd->index = &kd->hosts;
+ return kd;
+}
+
+/*
+ * initialize `handle' to look for hosts of type `type' in realm `realm'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_init(krb5_context context,
+ const char *realm,
+ unsigned int type,
+ krb5_krbhst_handle *handle)
+{
+ return krb5_krbhst_init_flags(context, realm, type, 0, handle);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_init_flags(krb5_context context,
+ const char *realm,
+ unsigned int type,
+ int flags,
+ krb5_krbhst_handle *handle)
+{
+ struct krb5_krbhst_data *kd;
+ krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
+ krb5_krbhst_info **);
+ int def_port;
+
+ switch(type) {
+ case KRB5_KRBHST_KDC:
+ next = kdc_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
+ break;
+ case KRB5_KRBHST_ADMIN:
+ next = admin_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
+ "tcp", 749));
+ break;
+ case KRB5_KRBHST_CHANGEPW:
+ next = kpasswd_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
+ KPASSWD_PORT));
+ break;
+ case KRB5_KRBHST_KRB524:
+ next = krb524_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
+ break;
+ default:
+ krb5_set_error_message(context, ENOTTY, "unknown krbhst type (%u)", type);
+ return ENOTTY;
+ }
+ if((kd = common_init(context, realm, flags)) == NULL)
+ return ENOMEM;
+ kd->get_next = next;
+ kd->def_port = def_port;
+ *handle = kd;
+ return 0;
+}
+
+/*
+ * return the next host information from `handle' in `host'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_next(krb5_context context,
+ krb5_krbhst_handle handle,
+ krb5_krbhst_info **host)
+{
+ if(get_next(handle, host))
+ return 0;
+
+ return (*handle->get_next)(context, handle, host);
+}
+
+/*
+ * return the next host information from `handle' as a host name
+ * in `hostname' (or length `hostlen)
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_next_as_string(krb5_context context,
+ krb5_krbhst_handle handle,
+ char *hostname,
+ size_t hostlen)
+{
+ krb5_error_code ret;
+ krb5_krbhst_info *host;
+ ret = krb5_krbhst_next(context, handle, &host);
+ if(ret)
+ return ret;
+ return krb5_krbhst_format_string(context, host, hostname, hostlen);
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
+{
+ handle->index = &handle->hosts;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
+{
+ krb5_krbhst_info *h, *next;
+
+ if (handle == NULL)
+ return;
+
+ for (h = handle->hosts; h != NULL; h = next) {
+ next = h->next;
+ _krb5_free_krbhst_info(h);
+ }
+
+ free(handle->realm);
+ free(handle);
+}
+
+/* backwards compatibility ahead */
+
+static krb5_error_code
+gethostlist(krb5_context context, const char *realm,
+ unsigned int type, char ***hostlist)
+{
+ krb5_error_code ret;
+ int nhost = 0;
+ krb5_krbhst_handle handle;
+ char host[MAXHOSTNAMELEN];
+ krb5_krbhst_info *hostinfo;
+
+ ret = krb5_krbhst_init(context, realm, type, &handle);
+ if (ret)
+ return ret;
+
+ while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
+ nhost++;
+ if(nhost == 0) {
+ krb5_set_error_message(context, KRB5_KDC_UNREACH,
+ "No KDC found for realm %s", realm);
+ return KRB5_KDC_UNREACH;
+ }
+ *hostlist = calloc(nhost + 1, sizeof(**hostlist));
+ if(*hostlist == NULL) {
+ krb5_krbhst_free(context, handle);
+ return ENOMEM;
+ }
+
+ krb5_krbhst_reset(context, handle);
+ nhost = 0;
+ while(krb5_krbhst_next_as_string(context, handle,
+ host, sizeof(host)) == 0) {
+ if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
+ krb5_free_krbhst(context, *hostlist);
+ krb5_krbhst_free(context, handle);
+ return ENOMEM;
+ }
+ }
+ (*hostlist)[nhost++] = NULL;
+ krb5_krbhst_free(context, handle);
+ return 0;
+}
+
+/*
+ * return an malloced list of kadmin-hosts for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krb_admin_hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
+}
+
+/*
+ * return an malloced list of changepw-hosts for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krb_changepw_hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist);
+}
+
+/*
+ * return an malloced list of 524-hosts for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krb524hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
+}
+
+
+/*
+ * return an malloced list of KDC's for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krbhst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist);
+}
+
+/*
+ * free all the memory allocated in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_krbhst (krb5_context context,
+ char **hostlist)
+{
+ char **p;
+
+ for (p = hostlist; *p; ++p)
+ free (*p);
+ free (hostlist);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/locate_plugin.h b/source4/heimdal/lib/krb5/locate_plugin.h
new file mode 100644
index 0000000000..baca037ebc
--- /dev/null
+++ b/source4/heimdal/lib/krb5/locate_plugin.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_LOCATE_PLUGIN_H
+#define HEIMDAL_KRB5_LOCATE_PLUGIN_H 1
+
+#include <krb5.h>
+
+#define KRB5_PLUGIN_LOCATE "resolve"
+
+enum locate_service_type {
+ locate_service_kdc = 1,
+ locate_service_master_kdc,
+ locate_service_kadmin,
+ locate_service_krb524,
+ locate_service_kpasswd
+};
+
+typedef krb5_error_code
+(*krb5plugin_service_locate_lookup) (void *, enum locate_service_type,
+ const char *, int, int,
+ int (*)(void *,int,struct sockaddr *),
+ void *);
+
+
+typedef struct krb5plugin_service_locate_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_service_locate_lookup lookup;
+} krb5plugin_service_locate_ftable;
+
+#endif /* HEIMDAL_KRB5_LOCATE_PLUGIN_H */
+
diff --git a/source4/heimdal/lib/krb5/log.c b/source4/heimdal/lib/krb5/log.c
new file mode 100644
index 0000000000..2ed061c80b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/log.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct facility {
+ int min;
+ int max;
+ krb5_log_log_func_t log_func;
+ krb5_log_close_func_t close_func;
+ void *data;
+};
+
+static struct facility*
+log_realloc(krb5_log_facility *f)
+{
+ struct facility *fp;
+ fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
+ if(fp == NULL)
+ return NULL;
+ f->len++;
+ f->val = fp;
+ fp += f->len - 1;
+ return fp;
+}
+
+struct s2i {
+ const char *s;
+ int val;
+};
+
+#define L(X) { #X, LOG_ ## X }
+
+static struct s2i syslogvals[] = {
+ L(EMERG),
+ L(ALERT),
+ L(CRIT),
+ L(ERR),
+ L(WARNING),
+ L(NOTICE),
+ L(INFO),
+ L(DEBUG),
+
+ L(AUTH),
+#ifdef LOG_AUTHPRIV
+ L(AUTHPRIV),
+#endif
+#ifdef LOG_CRON
+ L(CRON),
+#endif
+ L(DAEMON),
+#ifdef LOG_FTP
+ L(FTP),
+#endif
+ L(KERN),
+ L(LPR),
+ L(MAIL),
+#ifdef LOG_NEWS
+ L(NEWS),
+#endif
+ L(SYSLOG),
+ L(USER),
+#ifdef LOG_UUCP
+ L(UUCP),
+#endif
+ L(LOCAL0),
+ L(LOCAL1),
+ L(LOCAL2),
+ L(LOCAL3),
+ L(LOCAL4),
+ L(LOCAL5),
+ L(LOCAL6),
+ L(LOCAL7),
+ { NULL, -1 }
+};
+
+static int
+find_value(const char *s, struct s2i *table)
+{
+ while(table->s && strcasecmp(table->s, s))
+ table++;
+ return table->val;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_initlog(krb5_context context,
+ const char *program,
+ krb5_log_facility **fac)
+{
+ krb5_log_facility *f = calloc(1, sizeof(*f));
+ if(f == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ f->program = strdup(program);
+ if(f->program == NULL){
+ free(f);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *fac = f;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_addlog_func(krb5_context context,
+ krb5_log_facility *fac,
+ int min,
+ int max,
+ krb5_log_log_func_t log_func,
+ krb5_log_close_func_t close_func,
+ void *data)
+{
+ struct facility *fp = log_realloc(fac);
+ if(fp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ fp->min = min;
+ fp->max = max;
+ fp->log_func = log_func;
+ fp->close_func = close_func;
+ fp->data = data;
+ return 0;
+}
+
+
+struct _heimdal_syslog_data{
+ int priority;
+};
+
+static void
+log_syslog(const char *timestr,
+ const char *msg,
+ void *data)
+
+{
+ struct _heimdal_syslog_data *s = data;
+ syslog(s->priority, "%s", msg);
+}
+
+static void
+close_syslog(void *data)
+{
+ free(data);
+ closelog();
+}
+
+static krb5_error_code
+open_syslog(krb5_context context,
+ krb5_log_facility *facility, int min, int max,
+ const char *sev, const char *fac)
+{
+ struct _heimdal_syslog_data *sd = malloc(sizeof(*sd));
+ int i;
+
+ if(sd == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ i = find_value(sev, syslogvals);
+ if(i == -1)
+ i = LOG_ERR;
+ sd->priority = i;
+ i = find_value(fac, syslogvals);
+ if(i == -1)
+ i = LOG_AUTH;
+ sd->priority |= i;
+ roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
+ return krb5_addlog_func(context, facility, min, max,
+ log_syslog, close_syslog, sd);
+}
+
+struct file_data{
+ const char *filename;
+ const char *mode;
+ FILE *fd;
+ int keep_open;
+};
+
+static void
+log_file(const char *timestr,
+ const char *msg,
+ void *data)
+{
+ struct file_data *f = data;
+ if(f->keep_open == 0)
+ f->fd = fopen(f->filename, f->mode);
+ if(f->fd == NULL)
+ return;
+ fprintf(f->fd, "%s %s\n", timestr, msg);
+ if(f->keep_open == 0) {
+ fclose(f->fd);
+ f->fd = NULL;
+ }
+}
+
+static void
+close_file(void *data)
+{
+ struct file_data *f = data;
+ if(f->keep_open && f->filename)
+ fclose(f->fd);
+ free(data);
+}
+
+static krb5_error_code
+open_file(krb5_context context, krb5_log_facility *fac, int min, int max,
+ const char *filename, const char *mode, FILE *f, int keep_open)
+{
+ struct file_data *fd = malloc(sizeof(*fd));
+ if(fd == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ fd->filename = filename;
+ fd->mode = mode;
+ fd->fd = f;
+ fd->keep_open = keep_open;
+
+ return krb5_addlog_func(context, fac, min, max, log_file, close_file, fd);
+}
+
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
+{
+ krb5_error_code ret = 0;
+ int min = 0, max = -1, n;
+ char c;
+ const char *p = orig;
+
+ n = sscanf(p, "%d%c%d/", &min, &c, &max);
+ if(n == 2){
+ if(c == '/') {
+ if(min < 0){
+ max = -min;
+ min = 0;
+ }else{
+ max = min;
+ }
+ }
+ }
+ if(n){
+ p = strchr(p, '/');
+ if(p == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_LOG_PARSE,
+ "failed to parse \"%s\"", orig);
+ return HEIM_ERR_LOG_PARSE;
+ }
+ p++;
+ }
+ if(strcmp(p, "STDERR") == 0){
+ ret = open_file(context, f, min, max, NULL, NULL, stderr, 1);
+ }else if(strcmp(p, "CONSOLE") == 0){
+ ret = open_file(context, f, min, max, "/dev/console", "w", NULL, 0);
+ }else if(strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')){
+ char *fn;
+ FILE *file = NULL;
+ int keep_open = 0;
+ fn = strdup(p + 5);
+ if(fn == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ if(p[4] == '='){
+ int i = open(fn, O_WRONLY | O_CREAT |
+ O_TRUNC | O_APPEND, 0666);
+ if(i < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", fn,
+ strerror(ret));
+ free(fn);
+ return ret;
+ }
+ rk_cloexec(i);
+ file = fdopen(i, "a");
+ if(file == NULL){
+ ret = errno;
+ close(i);
+ krb5_set_error_message(context, ret, "fdopen(%s): %s", fn,
+ strerror(ret));
+ free(fn);
+ return ret;
+ }
+ keep_open = 1;
+ }
+ ret = open_file(context, f, min, max, fn, "a", file, keep_open);
+ }else if(strncmp(p, "DEVICE", 6) == 0 && (p[6] == ':' || p[6] == '=')){
+ ret = open_file(context, f, min, max, strdup(p + 7), "w", NULL, 0);
+ }else if(strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')){
+ char severity[128] = "";
+ char facility[128] = "";
+ p += 6;
+ if(*p != '\0')
+ p++;
+ if(strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
+ strsep_copy(&p, ":", facility, sizeof(facility));
+ if(*severity == '\0')
+ strlcpy(severity, "ERR", sizeof(severity));
+ if(*facility == '\0')
+ strlcpy(facility, "AUTH", sizeof(facility));
+ ret = open_syslog(context, f, min, max, severity, facility);
+ }else{
+ ret = HEIM_ERR_LOG_PARSE; /* XXX */
+ krb5_set_error_message (context, ret, "unknown log type: %s", p);
+ }
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_openlog(krb5_context context,
+ const char *program,
+ krb5_log_facility **fac)
+{
+ krb5_error_code ret;
+ char **p, **q;
+
+ ret = krb5_initlog(context, program, fac);
+ if(ret)
+ return ret;
+
+ p = krb5_config_get_strings(context, NULL, "logging", program, NULL);
+ if(p == NULL)
+ p = krb5_config_get_strings(context, NULL, "logging", "default", NULL);
+ if(p){
+ for(q = p; *q && ret == 0; q++)
+ ret = krb5_addlog_dest(context, *fac, *q);
+ krb5_config_free_strings(p);
+ }else
+ ret = krb5_addlog_dest(context, *fac, "SYSLOG");
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_closelog(krb5_context context,
+ krb5_log_facility *fac)
+{
+ int i;
+ for(i = 0; i < fac->len; i++)
+ (*fac->val[i].close_func)(fac->val[i].data);
+ free(fac->val);
+ free(fac->program);
+ fac->val = NULL;
+ fac->len = 0;
+ fac->program = NULL;
+ free(fac);
+ return 0;
+}
+
+#undef __attribute__
+#define __attribute__(X)
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vlog_msg(krb5_context context,
+ krb5_log_facility *fac,
+ char **reply,
+ int level,
+ const char *fmt,
+ va_list ap)
+ __attribute__((format (printf, 5, 0)))
+{
+
+ char *msg = NULL;
+ const char *actual = NULL;
+ char buf[64];
+ time_t t = 0;
+ int i;
+
+ for(i = 0; fac && i < fac->len; i++)
+ if(fac->val[i].min <= level &&
+ (fac->val[i].max < 0 || fac->val[i].max >= level)) {
+ if(t == 0) {
+ t = time(NULL);
+ krb5_format_time(context, t, buf, sizeof(buf), TRUE);
+ }
+ if(actual == NULL) {
+ vasprintf(&msg, fmt, ap);
+ if(msg == NULL)
+ actual = fmt;
+ else
+ actual = msg;
+ }
+ (*fac->val[i].log_func)(buf, actual, fac->val[i].data);
+ }
+ if(reply == NULL)
+ free(msg);
+ else
+ *reply = msg;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vlog(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ const char *fmt,
+ va_list ap)
+ __attribute__((format (printf, 4, 0)))
+{
+ return krb5_vlog_msg(context, fac, NULL, level, fmt, ap);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_log_msg(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ char **reply,
+ const char *fmt,
+ ...)
+ __attribute__((format (printf, 5, 6)))
+{
+ va_list ap;
+ krb5_error_code ret;
+
+ va_start(ap, fmt);
+ ret = krb5_vlog_msg(context, fac, reply, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_log(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ const char *fmt,
+ ...)
+ __attribute__((format (printf, 4, 5)))
+{
+ va_list ap;
+ krb5_error_code ret;
+
+ va_start(ap, fmt);
+ ret = krb5_vlog(context, fac, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
diff --git a/source4/heimdal/lib/krb5/mcache.c b/source4/heimdal/lib/krb5/mcache.c
new file mode 100644
index 0000000000..3f26b27a46
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mcache.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_mcache {
+ char *name;
+ unsigned int refcnt;
+ int dead;
+ krb5_principal primary_principal;
+ struct link {
+ krb5_creds cred;
+ struct link *next;
+ } *creds;
+ struct krb5_mcache *next;
+} krb5_mcache;
+
+static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct krb5_mcache *mcc_head;
+
+#define MCACHE(X) ((krb5_mcache *)(X)->data.data)
+
+#define MISDEAD(X) ((X)->dead)
+
+static const char*
+mcc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return MCACHE(id)->name;
+}
+
+static krb5_mcache *
+mcc_alloc(const char *name)
+{
+ krb5_mcache *m, *m_c;
+
+ ALLOC(m, 1);
+ if(m == NULL)
+ return NULL;
+ if(name == NULL)
+ asprintf(&m->name, "%p", m);
+ else
+ m->name = strdup(name);
+ if(m->name == NULL) {
+ free(m);
+ return NULL;
+ }
+ /* check for dups first */
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
+ if (strcmp(m->name, m_c->name) == 0)
+ break;
+ if (m_c) {
+ free(m->name);
+ free(m);
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ return NULL;
+ }
+
+ m->dead = 0;
+ m->refcnt = 1;
+ m->primary_principal = NULL;
+ m->creds = NULL;
+ m->next = mcc_head;
+ mcc_head = m;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ return m;
+}
+
+static krb5_error_code
+mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ krb5_mcache *m;
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for (m = mcc_head; m != NULL; m = m->next)
+ if (strcmp(m->name, res) == 0)
+ break;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ if (m != NULL) {
+ m->refcnt++;
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+ return 0;
+ }
+
+ m = mcc_alloc(res);
+ if (m == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+
+static krb5_error_code
+mcc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_mcache *m;
+
+ m = mcc_alloc(NULL);
+
+ if (m == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+static krb5_error_code
+mcc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_mcache *m = MCACHE(id);
+ m->dead = 0;
+ return krb5_copy_principal (context,
+ primary_principal,
+ &m->primary_principal);
+}
+
+static int
+mcc_close_internal(krb5_mcache *m)
+{
+ if (--m->refcnt != 0)
+ return 0;
+
+ if (MISDEAD(m)) {
+ free (m->name);
+ return 1;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_close(krb5_context context,
+ krb5_ccache id)
+{
+ if (mcc_close_internal(MCACHE(id)))
+ krb5_data_free(&id->data);
+ return 0;
+}
+
+static krb5_error_code
+mcc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_mcache **n, *m = MCACHE(id);
+ struct link *l;
+
+ if (m->refcnt == 0)
+ krb5_abortx(context, "mcc_destroy: refcnt already 0");
+
+ if (!MISDEAD(m)) {
+ /* if this is an active mcache, remove it from the linked
+ list, and free all data */
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for(n = &mcc_head; n && *n; n = &(*n)->next) {
+ if(m == *n) {
+ *n = m->next;
+ break;
+ }
+ }
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ if (m->primary_principal != NULL) {
+ krb5_free_principal (context, m->primary_principal);
+ m->primary_principal = NULL;
+ }
+ m->dead = 1;
+
+ l = m->creds;
+ while (l != NULL) {
+ struct link *old;
+
+ krb5_free_cred_contents (context, &l->cred);
+ old = l;
+ l = l->next;
+ free (old);
+ }
+ m->creds = NULL;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_mcache *m = MCACHE(id);
+ krb5_error_code ret;
+ struct link *l;
+
+ if (MISDEAD(m))
+ return ENOENT;
+
+ l = malloc (sizeof(*l));
+ if (l == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
+ return KRB5_CC_NOMEM;
+ }
+ l->next = m->creds;
+ m->creds = l;
+ memset (&l->cred, 0, sizeof(l->cred));
+ ret = krb5_copy_creds_contents (context, creds, &l->cred);
+ if (ret) {
+ m->creds = l->next;
+ free (l);
+ return ret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_mcache *m = MCACHE(id);
+
+ if (MISDEAD(m) || m->primary_principal == NULL)
+ return ENOENT;
+ return krb5_copy_principal (context,
+ m->primary_principal,
+ principal);
+}
+
+static krb5_error_code
+mcc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_mcache *m = MCACHE(id);
+
+ if (MISDEAD(m))
+ return ENOENT;
+
+ *cursor = m->creds;
+ return 0;
+}
+
+static krb5_error_code
+mcc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_mcache *m = MCACHE(id);
+ struct link *l;
+
+ if (MISDEAD(m))
+ return ENOENT;
+
+ l = *cursor;
+ if (l != NULL) {
+ *cursor = l->next;
+ return krb5_copy_creds_contents (context,
+ &l->cred,
+ creds);
+ } else
+ return KRB5_CC_END;
+}
+
+static krb5_error_code
+mcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ return 0;
+}
+
+static krb5_error_code
+mcc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *mcreds)
+{
+ krb5_mcache *m = MCACHE(id);
+ struct link **q, *p;
+ for(q = &m->creds, p = *q; p; p = *q) {
+ if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
+ *q = p->next;
+ krb5_free_cred_contents(context, &p->cred);
+ free(p);
+ } else
+ q = &p->next;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0; /* XXX */
+}
+
+struct mcache_iter {
+ krb5_mcache *cache;
+};
+
+static krb5_error_code
+mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct mcache_iter *iter;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ iter->cache = mcc_head;
+ if (iter->cache)
+ iter->cache->refcnt++;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code
+mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct mcache_iter *iter = cursor;
+ krb5_error_code ret;
+ krb5_mcache *m;
+
+ if (iter->cache == NULL)
+ return KRB5_CC_END;
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ m = iter->cache;
+ if (m->next)
+ m->next->refcnt++;
+ iter->cache = m->next;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
+ if (ret)
+ return ret;
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+static krb5_error_code
+mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct mcache_iter *iter = cursor;
+
+ if (iter->cache)
+ mcc_close_internal(iter->cache);
+ iter->cache = NULL;
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code
+mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
+ struct link *creds;
+ krb5_principal principal;
+ krb5_mcache **n;
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+
+ /* drop the from cache from the linked list to avoid lookups */
+ for(n = &mcc_head; n && *n; n = &(*n)->next) {
+ if(mfrom == *n) {
+ *n = mfrom->next;
+ break;
+ }
+ }
+
+ /* swap creds */
+ creds = mto->creds;
+ mto->creds = mfrom->creds;
+ mfrom->creds = creds;
+ /* swap principal */
+ principal = mto->primary_principal;
+ mto->primary_principal = mfrom->primary_principal;
+ mfrom->primary_principal = principal;
+
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ mcc_destroy(context, from);
+
+ return 0;
+}
+
+static krb5_error_code
+mcc_default_name(krb5_context context, char **str)
+{
+ *str = strdup("MEMORY:");
+ if (*str == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+/**
+ * Variable containing the MEMORY based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "MEMORY",
+ mcc_get_name,
+ mcc_resolve,
+ mcc_gen_new,
+ mcc_initialize,
+ mcc_destroy,
+ mcc_close,
+ mcc_store_cred,
+ NULL, /* mcc_retrieve */
+ mcc_get_principal,
+ mcc_get_first,
+ mcc_get_next,
+ mcc_end_get,
+ mcc_remove_cred,
+ mcc_set_flags,
+ NULL,
+ mcc_get_cache_first,
+ mcc_get_cache_next,
+ mcc_end_cache_get,
+ mcc_move,
+ mcc_default_name
+};
diff --git a/source4/heimdal/lib/krb5/misc.c b/source4/heimdal/lib/krb5/misc.c
new file mode 100644
index 0000000000..1ed4f08d77
--- /dev/null
+++ b/source4/heimdal/lib/krb5/misc.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_s4u2self_to_checksumdata(krb5_context context,
+ const PA_S4U2Self *self,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t ssize;
+ krb5_storage *sp;
+ size_t size;
+ int i;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+ ret = krb5_store_int32(sp, self->name.name_type);
+ if (ret)
+ goto out;
+ for (i = 0; i < self->name.name_string.len; i++) {
+ size = strlen(self->name.name_string.val[i]);
+ ssize = krb5_storage_write(sp, self->name.name_string.val[i], size);
+ if (ssize != size) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+ size = strlen(self->realm);
+ ssize = krb5_storage_write(sp, self->realm, size);
+ if (ssize != size) {
+ ret = ENOMEM;
+ goto out;
+ }
+ size = strlen(self->auth);
+ ssize = krb5_storage_write(sp, self->auth, size);
+ if (ssize != size) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, data);
+ krb5_storage_free(sp);
+ return ret;
+
+out:
+ krb5_clear_error_string(context);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mit_glue.c b/source4/heimdal/lib/krb5/mit_glue.c
new file mode 100644
index 0000000000..c157c5d365
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mit_glue.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+/*
+ * Glue for MIT API
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_make_checksum(krb5_context context,
+ krb5_cksumtype cksumtype,
+ const krb5_keyblock *key,
+ krb5_keyusage usage,
+ const krb5_data *input,
+ krb5_checksum *cksum)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_create_checksum(context, crypto, usage, cksumtype,
+ input->data, input->length, cksum);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_verify_checksum(krb5_context context, const krb5_keyblock *key,
+ krb5_keyusage usage, const krb5_data *data,
+ const krb5_checksum *cksum, krb5_boolean *valid)
+{
+ krb5_error_code ret;
+ krb5_checksum data_cksum;
+
+ *valid = 0;
+
+ ret = krb5_c_make_checksum(context, cksum->cksumtype,
+ key, usage, data, &data_cksum);
+ if (ret)
+ return ret;
+
+ if (data_cksum.cksumtype == cksum->cksumtype
+ && data_cksum.checksum.length == cksum->checksum.length
+ && memcmp(data_cksum.checksum.data, cksum->checksum.data, cksum->checksum.length) == 0)
+ *valid = 1;
+
+ krb5_free_checksum_contents(context, &data_cksum);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_get_checksum(krb5_context context, const krb5_checksum *cksum,
+ krb5_cksumtype *type, krb5_data **data)
+{
+ krb5_error_code ret;
+
+ if (type)
+ *type = cksum->cksumtype;
+ if (data) {
+ *data = malloc(sizeof(**data));
+ if (*data == NULL)
+ return ENOMEM;
+
+ ret = der_copy_octet_string(&cksum->checksum, *data);
+ if (ret) {
+ free(*data);
+ *data = NULL;
+ return ret;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_set_checksum(krb5_context context, krb5_checksum *cksum,
+ krb5_cksumtype type, const krb5_data *data)
+{
+ cksum->cksumtype = type;
+ return der_copy_octet_string(data, &cksum->checksum);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_checksum (krb5_context context, krb5_checksum *cksum)
+{
+ krb5_checksum_free(context, cksum);
+ free(cksum);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_checksum_contents(krb5_context context, krb5_checksum *cksum)
+{
+ krb5_checksum_free(context, cksum);
+ memset(cksum, 0, sizeof(*cksum));
+}
+
+void KRB5_LIB_FUNCTION
+krb5_checksum_free(krb5_context context, krb5_checksum *cksum)
+{
+ free_Checksum(cksum);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_valid_enctype (krb5_enctype etype)
+{
+ return krb5_enctype_valid(NULL, etype);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_valid_cksumtype(krb5_cksumtype ctype)
+{
+ return krb5_cksumtype_valid(NULL, ctype);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_is_coll_proof_cksum(krb5_cksumtype ctype)
+{
+ return krb5_checksum_is_collision_proof(NULL, ctype);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_is_keyed_cksum(krb5_cksumtype ctype)
+{
+ return krb5_checksum_is_keyed(NULL, ctype);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_checksum (krb5_context context,
+ const krb5_checksum *old,
+ krb5_checksum **new)
+{
+ *new = malloc(sizeof(**new));
+ if (*new == NULL)
+ return ENOMEM;
+ return copy_Checksum(old, *new);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_checksum_length (krb5_context context, krb5_cksumtype cksumtype,
+ size_t *length)
+{
+ return krb5_checksumsize(context, cksumtype, length);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_block_size(krb5_context context,
+ krb5_enctype enctype,
+ size_t *blocksize)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_keyblock key;
+
+ ret = krb5_generate_random_keyblock(context, enctype, &key);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret)
+ return ret;
+ ret = krb5_crypto_getblocksize(context, crypto, blocksize);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_decrypt(krb5_context context,
+ const krb5_keyblock key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ krb5_enc_data *input,
+ krb5_data *output)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, &key, input->enctype, &crypto);
+ if (ret)
+ return ret;
+
+ if (ivec) {
+ size_t blocksize;
+
+ ret = krb5_crypto_getblocksize(context, crypto, &blocksize);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+ }
+
+ if (blocksize > ivec->length) {
+ krb5_crypto_destroy(context, crypto);
+ return KRB5_BAD_MSIZE;
+ }
+ }
+
+ ret = krb5_decrypt_ivec(context, crypto, usage,
+ input->ciphertext.data, input->ciphertext.length,
+ output,
+ ivec ? ivec->data : NULL);
+
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_encrypt(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ const krb5_data *input,
+ krb5_enc_data *output)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ if (ivec) {
+ size_t blocksize;
+
+ ret = krb5_crypto_getblocksize(context, crypto, &blocksize);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+ }
+
+ if (blocksize > ivec->length) {
+ krb5_crypto_destroy(context, crypto);
+ return KRB5_BAD_MSIZE;
+ }
+ }
+
+ ret = krb5_encrypt_ivec(context, crypto, usage,
+ input->data, input->length,
+ &output->ciphertext,
+ ivec ? ivec->data : NULL);
+ output->kvno = 0;
+ krb5_crypto_getenctype(context, crypto, &output->enctype);
+
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_encrypt_length(krb5_context context,
+ krb5_enctype enctype,
+ size_t inputlen,
+ size_t *length)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_keyblock key;
+
+ ret = krb5_generate_random_keyblock(context, enctype, &key);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret)
+ return ret;
+
+ *length = krb5_get_wrapped_length(context, crypto, inputlen);
+ krb5_crypto_destroy(context, crypto);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_enctype_compare(krb5_context context,
+ krb5_enctype e1,
+ krb5_enctype e2,
+ krb5_boolean *similar)
+{
+ *similar = krb5_enctypes_compatible_keys(context, e1, e2);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_make_random_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_keyblock *random_key)
+{
+ return krb5_generate_random_keyblock(context, enctype, random_key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_keylengths(krb5_context context,
+ krb5_enctype enctype,
+ size_t *ilen,
+ size_t *keylen)
+{
+ krb5_error_code ret;
+
+ ret = krb5_enctype_keybits(context, enctype, ilen);
+ if (ret)
+ return ret;
+ *ilen = (*ilen + 7) / 8;
+ return krb5_enctype_keysize(context, enctype, keylen);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_prf_length(krb5_context context,
+ krb5_enctype type,
+ size_t *length)
+{
+ return krb5_crypto_prf_length(context, type, length);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_prf(krb5_context context,
+ const krb5_keyblock *key,
+ const krb5_data *input,
+ krb5_data *output)
+{
+ krb5_crypto crypto;
+ krb5_error_code ret;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_prf(context, crypto, input, output);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mk_error.c b/source4/heimdal/lib/krb5/mk_error.c
new file mode 100644
index 0000000000..d4c3867edd
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_error.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_error(krb5_context context,
+ krb5_error_code error_code,
+ const char *e_text,
+ const krb5_data *e_data,
+ const krb5_principal client,
+ const krb5_principal server,
+ time_t *client_time,
+ int *client_usec,
+ krb5_data *reply)
+{
+ KRB_ERROR msg;
+ krb5_timestamp sec;
+ int32_t usec;
+ size_t len;
+ krb5_error_code ret = 0;
+
+ krb5_us_timeofday (context, &sec, &usec);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.pvno = 5;
+ msg.msg_type = krb_error;
+ msg.stime = sec;
+ msg.susec = usec;
+ msg.ctime = client_time;
+ msg.cusec = client_usec;
+ /* Make sure we only send `protocol' error codes */
+ if(error_code < KRB5KDC_ERR_NONE || error_code >= KRB5_ERR_RCSID) {
+ if(e_text == NULL)
+ e_text = krb5_get_err_text(context, error_code);
+ error_code = KRB5KRB_ERR_GENERIC;
+ }
+ msg.error_code = error_code - KRB5KDC_ERR_NONE;
+ if (e_text)
+ msg.e_text = rk_UNCONST(&e_text);
+ if (e_data)
+ msg.e_data = rk_UNCONST(e_data);
+ if(server){
+ msg.realm = server->realm;
+ msg.sname = server->name;
+ }else{
+ msg.realm = "<unspecified realm>";
+ }
+ if(client){
+ msg.crealm = &client->realm;
+ msg.cname = &client->name;
+ }
+
+ ASN1_MALLOC_ENCODE(KRB_ERROR, reply->data, reply->length, &msg, &len, ret);
+ if (ret)
+ return ret;
+ if(reply->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/mk_priv.c b/source4/heimdal/lib/krb5/mk_priv.c
new file mode 100644
index 0000000000..a1a9ea4dff
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_priv.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_priv(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *userdata,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_PRIV s;
+ EncKrbPrivPart part;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len;
+ krb5_crypto crypto;
+ krb5_keyblock *key;
+ krb5_replay_data rdata;
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
+ outdata == NULL)
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+
+ if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else
+ key = auth_context->keyblock;
+
+ memset(&rdata, 0, sizeof(rdata));
+
+ part.user_data = *userdata;
+
+ krb5_us_timeofday (context, &rdata.timestamp, &rdata.usec);
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ part.timestamp = &rdata.timestamp;
+ part.usec = &rdata.usec;
+ } else {
+ part.timestamp = NULL;
+ part.usec = NULL;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_TIME) {
+ outdata->timestamp = rdata.timestamp;
+ outdata->usec = rdata.usec;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ rdata.seq = auth_context->local_seqnumber;
+ part.seq_number = &rdata.seq;
+ } else
+ part.seq_number = NULL;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
+ outdata->seq = auth_context->local_seqnumber;
+
+ part.s_address = auth_context->local_address;
+ part.r_address = auth_context->remote_address;
+
+ krb5_data_zero (&s.enc_part.cipher);
+
+ ASN1_MALLOC_ENCODE(EncKrbPrivPart, buf, buf_size, &part, &len, ret);
+ if (ret)
+ goto fail;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ s.pvno = 5;
+ s.msg_type = krb_priv;
+ s.enc_part.etype = key->keytype;
+ s.enc_part.kvno = NULL;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free (buf);
+ return ret;
+ }
+ ret = krb5_encrypt (context,
+ crypto,
+ KRB5_KU_KRB_PRIV,
+ buf + buf_size - len,
+ len,
+ &s.enc_part.cipher);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ free(buf);
+
+
+ ASN1_MALLOC_ENCODE(KRB_PRIV, buf, buf_size, &s, &len, ret);
+ if (ret)
+ goto fail;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ krb5_data_free (&s.enc_part.cipher);
+
+ ret = krb5_data_copy(outbuf, buf + buf_size - len, len);
+ if (ret) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ free(buf);
+ return ENOMEM;
+ }
+ free (buf);
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
+ auth_context->local_seqnumber =
+ (auth_context->local_seqnumber + 1) & 0xFFFFFFFF;
+ return 0;
+
+ fail:
+ free (buf);
+ krb5_data_free (&s.enc_part.cipher);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mk_rep.c b/source4/heimdal/lib/krb5/mk_rep.c
new file mode 100644
index 0000000000..65c97b5803
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_rep.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_rep(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ AP_REP ap;
+ EncAPRepPart body;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len;
+ krb5_crypto crypto;
+
+ ap.pvno = 5;
+ ap.msg_type = krb_ap_rep;
+
+ memset (&body, 0, sizeof(body));
+
+ body.ctime = auth_context->authenticator->ctime;
+ body.cusec = auth_context->authenticator->cusec;
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) {
+ if (auth_context->local_subkey == NULL) {
+ ret = krb5_auth_con_generatelocalsubkey(context,
+ auth_context,
+ auth_context->keyblock);
+ if(ret) {
+ free_EncAPRepPart(&body);
+ krb5_set_error_message(context, ret,
+ "krb5_mk_rep: generating subkey");
+ return ret;
+ }
+ }
+ ret = krb5_copy_keyblock(context, auth_context->local_subkey,
+ &body.subkey);
+ if (ret) {
+ free_EncAPRepPart(&body);
+ krb5_set_error_message(context, ENOMEM,
+ "krb5_copy_keyblock: out of memory");
+ return ENOMEM;
+ }
+ } else
+ body.subkey = NULL;
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if(auth_context->local_seqnumber == 0)
+ krb5_generate_seq_number (context,
+ auth_context->keyblock,
+ &auth_context->local_seqnumber);
+ ALLOC(body.seq_number, 1);
+ if (body.seq_number == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ free_EncAPRepPart(&body);
+ return ENOMEM;
+ }
+ *(body.seq_number) = auth_context->local_seqnumber;
+ } else
+ body.seq_number = NULL;
+
+ ap.enc_part.etype = auth_context->keyblock->keytype;
+ ap.enc_part.kvno = NULL;
+
+ ASN1_MALLOC_ENCODE(EncAPRepPart, buf, buf_size, &body, &len, ret);
+ free_EncAPRepPart (&body);
+ if(ret)
+ return ret;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ ret = krb5_crypto_init(context, auth_context->keyblock,
+ 0 /* ap.enc_part.etype */, &crypto);
+ if (ret) {
+ free (buf);
+ return ret;
+ }
+ ret = krb5_encrypt (context,
+ crypto,
+ KRB5_KU_AP_REQ_ENC_PART,
+ buf + buf_size - len,
+ len,
+ &ap.enc_part.cipher);
+ krb5_crypto_destroy(context, crypto);
+ free(buf);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(AP_REP, outbuf->data, outbuf->length, &ap, &len, ret);
+ if (ret == 0 && outbuf->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_AP_REP (&ap);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mk_req.c b/source4/heimdal/lib/krb5/mk_req.c
new file mode 100644
index 0000000000..1068aaa668
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_req.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_req_exact(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const krb5_principal server,
+ krb5_data *in_data,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ krb5_creds this_cred, *cred;
+
+ memset(&this_cred, 0, sizeof(this_cred));
+
+ ret = krb5_cc_get_principal(context, ccache, &this_cred.client);
+
+ if(ret)
+ return ret;
+
+ ret = krb5_copy_principal (context, server, &this_cred.server);
+ if (ret) {
+ krb5_free_cred_contents (context, &this_cred);
+ return ret;
+ }
+
+ this_cred.times.endtime = 0;
+ if (auth_context && *auth_context && (*auth_context)->keytype)
+ this_cred.session.keytype = (*auth_context)->keytype;
+
+ ret = krb5_get_credentials (context, 0, ccache, &this_cred, &cred);
+ krb5_free_cred_contents(context, &this_cred);
+ if (ret)
+ return ret;
+
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ ap_req_options,
+ in_data,
+ cred,
+ outbuf);
+ krb5_free_creds(context, cred);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *service,
+ const char *hostname,
+ krb5_data *in_data,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ char **realms;
+ char *real_hostname;
+ krb5_principal server;
+
+ ret = krb5_expand_hostname_realms (context, hostname,
+ &real_hostname, &realms);
+ if (ret)
+ return ret;
+
+ ret = krb5_build_principal (context, &server,
+ strlen(*realms),
+ *realms,
+ service,
+ real_hostname,
+ NULL);
+ free (real_hostname);
+ krb5_free_host_realm (context, realms);
+ if (ret)
+ return ret;
+ ret = krb5_mk_req_exact (context, auth_context, ap_req_options,
+ server, in_data, ccache, outbuf);
+ krb5_free_principal (context, server);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mk_req_ext.c b/source4/heimdal/lib/krb5/mk_req_ext.c
new file mode 100644
index 0000000000..645dadee22
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_req_ext.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code
+_krb5_mk_req_internal(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ krb5_data *in_data,
+ krb5_creds *in_creds,
+ krb5_data *outbuf,
+ krb5_key_usage checksum_usage,
+ krb5_key_usage encrypt_usage)
+{
+ krb5_error_code ret;
+ krb5_data authenticator;
+ Checksum c;
+ Checksum *c_opt;
+ krb5_auth_context ac;
+
+ if(auth_context) {
+ if(*auth_context == NULL)
+ ret = krb5_auth_con_init(context, auth_context);
+ else
+ ret = 0;
+ ac = *auth_context;
+ } else
+ ret = krb5_auth_con_init(context, &ac);
+ if(ret)
+ return ret;
+
+ if(ac->local_subkey == NULL && (ap_req_options & AP_OPTS_USE_SUBKEY)) {
+ ret = krb5_auth_con_generatelocalsubkey(context,
+ ac,
+ &in_creds->session);
+ if(ret)
+ goto out;
+ }
+
+ krb5_free_keyblock(context, ac->keyblock);
+ ret = krb5_copy_keyblock(context, &in_creds->session, &ac->keyblock);
+ if (ret)
+ goto out;
+
+ /* it's unclear what type of checksum we can use. try the best one, except:
+ * a) if it's configured differently for the current realm, or
+ * b) if the session key is des-cbc-crc
+ */
+
+ if (in_data) {
+ if(ac->keyblock->keytype == ETYPE_DES_CBC_CRC) {
+ /* this is to make DCE secd (and older MIT kdcs?) happy */
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_RSA_MD4,
+ in_data->data,
+ in_data->length,
+ &c);
+ } else if(ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5 ||
+ ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5_56 ||
+ ac->keyblock->keytype == ETYPE_DES_CBC_MD4 ||
+ ac->keyblock->keytype == ETYPE_DES_CBC_MD5) {
+ /* this is to make MS kdc happy */
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_RSA_MD5,
+ in_data->data,
+ in_data->length,
+ &c);
+ } else {
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, ac->keyblock, 0, &crypto);
+ if (ret)
+ goto out;
+ ret = krb5_create_checksum(context,
+ crypto,
+ checksum_usage,
+ 0,
+ in_data->data,
+ in_data->length,
+ &c);
+ krb5_crypto_destroy(context, crypto);
+ }
+ c_opt = &c;
+ } else {
+ c_opt = NULL;
+ }
+
+ if (ret)
+ goto out;
+
+ ret = krb5_build_authenticator (context,
+ ac,
+ ac->keyblock->keytype,
+ in_creds,
+ c_opt,
+ NULL,
+ &authenticator,
+ encrypt_usage);
+ if (c_opt)
+ free_Checksum (c_opt);
+ if (ret)
+ goto out;
+
+ ret = krb5_build_ap_req (context, ac->keyblock->keytype,
+ in_creds, ap_req_options, authenticator, outbuf);
+out:
+ if(auth_context == NULL)
+ krb5_auth_con_free(context, ac);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_req_extended(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ krb5_data *in_data,
+ krb5_creds *in_creds,
+ krb5_data *outbuf)
+{
+ return _krb5_mk_req_internal (context,
+ auth_context,
+ ap_req_options,
+ in_data,
+ in_creds,
+ outbuf,
+ KRB5_KU_AP_REQ_AUTH_CKSUM,
+ KRB5_KU_AP_REQ_AUTH);
+}
diff --git a/source4/heimdal/lib/krb5/n-fold.c b/source4/heimdal/lib/krb5/n-fold.c
new file mode 100644
index 0000000000..147f6aeac7
--- /dev/null
+++ b/source4/heimdal/lib/krb5/n-fold.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+static krb5_error_code
+rr13(unsigned char *buf, size_t len)
+{
+ unsigned char *tmp;
+ int bytes = (len + 7) / 8;
+ int i;
+ if(len == 0)
+ return 0;
+ {
+ const int bits = 13 % len;
+ const int lbit = len % 8;
+
+ tmp = malloc(bytes);
+ if (tmp == NULL)
+ return ENOMEM;
+ memcpy(tmp, buf, bytes);
+ if(lbit) {
+ /* pad final byte with inital bits */
+ tmp[bytes - 1] &= 0xff << (8 - lbit);
+ for(i = lbit; i < 8; i += len)
+ tmp[bytes - 1] |= buf[0] >> i;
+ }
+ for(i = 0; i < bytes; i++) {
+ int bb;
+ int b1, s1, b2, s2;
+ /* calculate first bit position of this byte */
+ bb = 8 * i - bits;
+ while(bb < 0)
+ bb += len;
+ /* byte offset and shift count */
+ b1 = bb / 8;
+ s1 = bb % 8;
+
+ if(bb + 8 > bytes * 8)
+ /* watch for wraparound */
+ s2 = (len + 8 - s1) % 8;
+ else
+ s2 = 8 - s1;
+ b2 = (b1 + 1) % bytes;
+ buf[i] = (tmp[b1] << s1) | (tmp[b2] >> s2);
+ }
+ free(tmp);
+ }
+ return 0;
+}
+
+/* Add `b' to `a', both being one's complement numbers. */
+static void
+add1(unsigned char *a, unsigned char *b, size_t len)
+{
+ int i;
+ int carry = 0;
+ for(i = len - 1; i >= 0; i--){
+ int x = a[i] + b[i] + carry;
+ carry = x > 0xff;
+ a[i] = x & 0xff;
+ }
+ for(i = len - 1; carry && i >= 0; i--){
+ int x = a[i] + carry;
+ carry = x > 0xff;
+ a[i] = x & 0xff;
+ }
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_n_fold(const void *str, size_t len, void *key, size_t size)
+{
+ /* if len < size we need at most N * len bytes, ie < 2 * size;
+ if len > size we need at most 2 * len */
+ krb5_error_code ret = 0;
+ size_t maxlen = 2 * max(size, len);
+ size_t l = 0;
+ unsigned char *tmp = malloc(maxlen);
+ unsigned char *buf = malloc(len);
+
+ if (tmp == NULL || buf == NULL)
+ return ENOMEM;
+
+ memcpy(buf, str, len);
+ memset(key, 0, size);
+ do {
+ memcpy(tmp + l, buf, len);
+ l += len;
+ ret = rr13(buf, len * 8);
+ if (ret)
+ goto out;
+ while(l >= size) {
+ add1(key, tmp, size);
+ l -= size;
+ if(l == 0)
+ break;
+ memmove(tmp, tmp + size, l);
+ }
+ } while(l != 0);
+out:
+ memset(buf, 0, len);
+ free(buf);
+ memset(tmp, 0, maxlen);
+ free(tmp);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c
new file mode 100644
index 0000000000..9a145c48e6
--- /dev/null
+++ b/source4/heimdal/lib/krb5/pac.c
@@ -0,0 +1,1026 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <wind.h>
+
+RCSID("$Id$");
+
+struct PAC_INFO_BUFFER {
+ uint32_t type;
+ uint32_t buffersize;
+ uint32_t offset_hi;
+ uint32_t offset_lo;
+};
+
+struct PACTYPE {
+ uint32_t numbuffers;
+ uint32_t version;
+ struct PAC_INFO_BUFFER buffers[1];
+};
+
+struct krb5_pac_data {
+ struct PACTYPE *pac;
+ krb5_data data;
+ struct PAC_INFO_BUFFER *server_checksum;
+ struct PAC_INFO_BUFFER *privsvr_checksum;
+ struct PAC_INFO_BUFFER *logon_name;
+};
+
+#define PAC_ALIGNMENT 8
+
+#define PACTYPE_SIZE 8
+#define PAC_INFO_BUFFER_SIZE 16
+
+#define PAC_SERVER_CHECKSUM 6
+#define PAC_PRIVSVR_CHECKSUM 7
+#define PAC_LOGON_NAME 10
+#define PAC_CONSTRAINED_DELEGATION 11
+
+#define CHECK(r,f,l) \
+ do { \
+ if (((r) = f ) != 0) { \
+ krb5_clear_error_string(context); \
+ goto l; \
+ } \
+ } while(0)
+
+static const char zeros[PAC_ALIGNMENT] = { 0 };
+
+/*
+ *
+ */
+
+krb5_error_code
+krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
+ krb5_pac *pac)
+{
+ krb5_error_code ret;
+ krb5_pac p;
+ krb5_storage *sp = NULL;
+ uint32_t i, tmp, tmp2, header_end;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ sp = krb5_storage_from_readonly_mem(ptr, len);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &tmp), out);
+ CHECK(ret, krb5_ret_uint32(sp, &tmp2), out);
+ if (tmp < 1) {
+ ret = EINVAL; /* Too few buffers */
+ krb5_set_error_message(context, ret, "PAC have too few buffer");
+ goto out;
+ }
+ if (tmp2 != 0) {
+ ret = EINVAL; /* Wrong version */
+ krb5_set_error_message(context, ret, "PAC have wrong version");
+ goto out;
+ }
+
+ p->pac = calloc(1,
+ sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1)));
+ if (p->pac == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ p->pac->numbuffers = tmp;
+ p->pac->version = tmp2;
+
+ header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
+ if (header_end > len) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out);
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out);
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out);
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out);
+
+ /* consistency checks */
+ if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC out of allignment");
+ goto out;
+ }
+ if (p->pac->buffers[i].offset_hi) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC high offset set");
+ goto out;
+ }
+ if (p->pac->buffers[i].offset_lo > len) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC offset off end");
+ goto out;
+ }
+ if (p->pac->buffers[i].offset_lo < header_end) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC offset inside header: %lu %lu",
+ (unsigned long)p->pac->buffers[i].offset_lo,
+ (unsigned long)header_end);
+ goto out;
+ }
+ if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC length off end");
+ goto out;
+ }
+
+ /* let save pointer to data we need later */
+ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
+ if (p->server_checksum) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC have two server checksums");
+ goto out;
+ }
+ p->server_checksum = &p->pac->buffers[i];
+ } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
+ if (p->privsvr_checksum) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC have two KDC checksums");
+ goto out;
+ }
+ p->privsvr_checksum = &p->pac->buffers[i];
+ } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
+ if (p->logon_name) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC have two logon names");
+ goto out;
+ }
+ p->logon_name = &p->pac->buffers[i];
+ }
+ }
+
+ ret = krb5_data_copy(&p->data, ptr, len);
+ if (ret)
+ goto out;
+
+ krb5_storage_free(sp);
+
+ *pac = p;
+ return 0;
+
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ if (p) {
+ if (p->pac)
+ free(p->pac);
+ free(p);
+ }
+ *pac = NULL;
+
+ return ret;
+}
+
+krb5_error_code
+krb5_pac_init(krb5_context context, krb5_pac *pac)
+{
+ krb5_error_code ret;
+ krb5_pac p;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ p->pac = calloc(1, sizeof(*p->pac));
+ if (p->pac == NULL) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
+ if (ret) {
+ free (p->pac);
+ free(p);
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ return ret;
+ }
+
+
+ *pac = p;
+ return 0;
+}
+
+krb5_error_code
+krb5_pac_add_buffer(krb5_context context, krb5_pac p,
+ uint32_t type, const krb5_data *data)
+{
+ krb5_error_code ret;
+ void *ptr;
+ size_t len, offset, header_end, old_end;
+ uint32_t i;
+
+ len = p->pac->numbuffers;
+
+ ptr = realloc(p->pac,
+ sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len));
+ if (ptr == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ p->pac = ptr;
+
+ for (i = 0; i < len; i++)
+ p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE;
+
+ offset = p->data.length + PAC_INFO_BUFFER_SIZE;
+
+ p->pac->buffers[len].type = type;
+ p->pac->buffers[len].buffersize = data->length;
+ p->pac->buffers[len].offset_lo = offset;
+ p->pac->buffers[len].offset_hi = 0;
+
+ old_end = p->data.length;
+ len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE;
+ if (len < p->data.length) {
+ krb5_set_error_message(context, EINVAL, "integer overrun");
+ return EINVAL;
+ }
+
+ /* align to PAC_ALIGNMENT */
+ len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
+
+ ret = krb5_data_realloc(&p->data, len);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ return ret;
+ }
+
+ /*
+ * make place for new PAC INFO BUFFER header
+ */
+ header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
+ memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE,
+ (unsigned char *)p->data.data + header_end ,
+ old_end - header_end);
+ memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE);
+
+ /*
+ * copy in new data part
+ */
+
+ memcpy((unsigned char *)p->data.data + offset,
+ data->data, data->length);
+ memset((unsigned char *)p->data.data + offset + data->length,
+ 0, p->data.length - offset - data->length);
+
+ p->pac->numbuffers += 1;
+
+ return 0;
+}
+
+krb5_error_code
+krb5_pac_get_buffer(krb5_context context, krb5_pac p,
+ uint32_t type, krb5_data *data)
+{
+ krb5_error_code ret;
+ uint32_t i;
+
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ size_t len = p->pac->buffers[i].buffersize;
+ size_t offset = p->pac->buffers[i].offset_lo;
+
+ if (p->pac->buffers[i].type != type)
+ continue;
+
+ ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ return ret;
+ }
+ return 0;
+ }
+ krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
+ (unsigned long)type);
+ return ENOENT;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+krb5_pac_get_types(krb5_context context,
+ krb5_pac p,
+ size_t *len,
+ uint32_t **types)
+{
+ size_t i;
+
+ *types = calloc(p->pac->numbuffers, sizeof(*types));
+ if (*types == NULL) {
+ *len = 0;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ for (i = 0; i < p->pac->numbuffers; i++)
+ (*types)[i] = p->pac->buffers[i].type;
+ *len = p->pac->numbuffers;
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+void
+krb5_pac_free(krb5_context context, krb5_pac pac)
+{
+ krb5_data_free(&pac->data);
+ free(pac->pac);
+ free(pac);
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+verify_checksum(krb5_context context,
+ const struct PAC_INFO_BUFFER *sig,
+ const krb5_data *data,
+ void *ptr, size_t len,
+ const krb5_keyblock *key)
+{
+ krb5_crypto crypto = NULL;
+ krb5_storage *sp = NULL;
+ uint32_t type;
+ krb5_error_code ret;
+ Checksum cksum;
+
+ memset(&cksum, 0, sizeof(cksum));
+
+ sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo,
+ sig->buffersize);
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &type), out);
+ cksum.cksumtype = type;
+ cksum.checksum.length =
+ sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR);
+ cksum.checksum.data = malloc(cksum.checksum.length);
+ if (cksum.checksum.data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
+ if (ret != cksum.checksum.length) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC checksum missing checksum");
+ goto out;
+ }
+
+ if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "Checksum type %d not keyed",
+ cksum.cksumtype);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
+ ptr, len, &cksum);
+ free(cksum.checksum.data);
+ krb5_crypto_destroy(context, crypto);
+ krb5_storage_free(sp);
+
+ return ret;
+
+out:
+ if (cksum.checksum.data)
+ free(cksum.checksum.data);
+ if (sp)
+ krb5_storage_free(sp);
+ if (crypto)
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+}
+
+static krb5_error_code
+create_checksum(krb5_context context,
+ const krb5_keyblock *key,
+ void *data, size_t datalen,
+ void *sig, size_t siglen)
+{
+ krb5_crypto crypto = NULL;
+ krb5_error_code ret;
+ Checksum cksum;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
+ data, datalen, &cksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ if (cksum.checksum.length != siglen) {
+ krb5_set_error_message(context, EINVAL, "pac checksum wrong length");
+ free_Checksum(&cksum);
+ return EINVAL;
+ }
+
+ memcpy(sig, cksum.checksum.data, siglen);
+ free_Checksum(&cksum);
+
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+#define NTTIME_EPOCH 0x019DB1DED53E8000LL
+
+static uint64_t
+unix2nttime(time_t unix_time)
+{
+ long long wt;
+ wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
+ return wt;
+}
+
+static krb5_error_code
+verify_logonname(krb5_context context,
+ const struct PAC_INFO_BUFFER *logon_name,
+ const krb5_data *data,
+ time_t authtime,
+ krb5_const_principal principal)
+{
+ krb5_error_code ret;
+ krb5_principal p2;
+ uint32_t time1, time2;
+ krb5_storage *sp;
+ uint16_t len;
+ char *s;
+
+ sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
+ logon_name->buffersize);
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &time1), out);
+ CHECK(ret, krb5_ret_uint32(sp, &time2), out);
+
+ {
+ uint64_t t1, t2;
+ t1 = unix2nttime(authtime);
+ t2 = ((uint64_t)time2 << 32) | time1;
+ if (t1 != t2) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
+ return EINVAL;
+ }
+ }
+ CHECK(ret, krb5_ret_uint16(sp, &len), out);
+ if (len == 0) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
+ return EINVAL;
+ }
+
+ s = malloc(len);
+ if (s == NULL) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = krb5_storage_read(sp, s, len);
+ if (ret != len) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
+ return EINVAL;
+ }
+ krb5_storage_free(sp);
+ {
+ size_t ucs2len = len / 2;
+ uint16_t *ucs2;
+ size_t u8len;
+ unsigned int flags = WIND_RW_LE;
+
+ ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
+ if (ucs2 == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
+ free(s);
+ if (ret) {
+ free(ucs2);
+ krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
+ return ret;
+ }
+ ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
+ if (ret) {
+ free(ucs2);
+ krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
+ return ret;
+ }
+ u8len += 1; /* Add space for NUL */
+ s = malloc(u8len);
+ if (s == NULL) {
+ free(ucs2);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len);
+ free(ucs2);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
+ return ret;
+ }
+ }
+ ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
+ free(s);
+ if (ret)
+ return ret;
+
+ if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC logon name mismatch");
+ }
+ krb5_free_principal(context, p2);
+ return ret;
+out:
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+build_logon_name(krb5_context context,
+ time_t authtime,
+ krb5_const_principal principal,
+ krb5_data *logon)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ uint64_t t;
+ char *s, *s2;
+ size_t i, len;
+
+ t = unix2nttime(authtime);
+
+ krb5_data_zero(logon);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
+ CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
+
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s);
+ if (ret)
+ goto out;
+
+ len = strlen(s);
+
+ CHECK(ret, krb5_store_uint16(sp, len * 2), out);
+
+#if 1 /* cheat for now */
+ s2 = malloc(len * 2);
+ if (s2 == NULL) {
+ ret = ENOMEM;
+ free(s);
+ goto out;
+ }
+ for (i = 0; i < len; i++) {
+ s2[i * 2] = s[i];
+ s2[i * 2 + 1] = 0;
+ }
+ free(s);
+#else
+ /* write libwind code here */
+#endif
+
+ ret = krb5_storage_write(sp, s2, len * 2);
+ free(s2);
+ if (ret != len * 2) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = krb5_storage_to_data(sp, logon);
+ if (ret)
+ goto out;
+ krb5_storage_free(sp);
+
+ return 0;
+out:
+ krb5_storage_free(sp);
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+krb5_error_code
+krb5_pac_verify(krb5_context context,
+ const krb5_pac pac,
+ time_t authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+
+ if (pac->server_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
+ return EINVAL;
+ }
+ if (pac->privsvr_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
+ return EINVAL;
+ }
+ if (pac->logon_name == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing logon name");
+ return EINVAL;
+ }
+
+ ret = verify_logonname(context,
+ pac->logon_name,
+ &pac->data,
+ authtime,
+ principal);
+ if (ret)
+ return ret;
+
+ /*
+ * in the service case, clean out data option of the privsvr and
+ * server checksum before checking the checksum.
+ */
+ {
+ krb5_data *copy;
+
+ ret = krb5_copy_data(context, &pac->data, &copy);
+ if (ret)
+ return ret;
+
+ if (pac->server_checksum->buffersize < 4)
+ return EINVAL;
+ if (pac->privsvr_checksum->buffersize < 4)
+ return EINVAL;
+
+ memset((char *)copy->data + pac->server_checksum->offset_lo + 4,
+ 0,
+ pac->server_checksum->buffersize - 4);
+
+ memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4,
+ 0,
+ pac->privsvr_checksum->buffersize - 4);
+
+ ret = verify_checksum(context,
+ pac->server_checksum,
+ &pac->data,
+ copy->data,
+ copy->length,
+ server);
+ krb5_free_data(context, copy);
+ if (ret)
+ return ret;
+ }
+ if (privsvr) {
+ ret = verify_checksum(context,
+ pac->privsvr_checksum,
+ &pac->data,
+ (char *)pac->data.data
+ + pac->server_checksum->offset_lo + 4,
+ pac->server_checksum->buffersize - 4,
+ privsvr);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
+{
+ ssize_t sret;
+ size_t l;
+
+ while (len) {
+ l = len;
+ if (l > sizeof(zeros))
+ l = sizeof(zeros);
+ sret = krb5_storage_write(sp, zeros, l);
+ if (sret <= 0) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ len -= sret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+pac_checksum(krb5_context context,
+ const krb5_keyblock *key,
+ uint32_t *cksumtype,
+ size_t *cksumsize)
+{
+ krb5_cksumtype cktype;
+ krb5_error_code ret;
+ krb5_crypto crypto = NULL;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
+ krb5_set_error_message(context, EINVAL, "PAC checksum type is not keyed");
+ return EINVAL;
+ }
+
+ ret = krb5_checksumsize(context, cktype, cksumsize);
+ if (ret)
+ return ret;
+
+ *cksumtype = (uint32_t)cktype;
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_pac_sign(krb5_context context,
+ krb5_pac p,
+ time_t authtime,
+ krb5_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *priv_key,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp = NULL, *spdata = NULL;
+ uint32_t end;
+ size_t server_size, priv_size;
+ uint32_t server_offset = 0, priv_offset = 0;
+ uint32_t server_cksumtype = 0, priv_cksumtype = 0;
+ int i, num = 0;
+ krb5_data logon, d;
+
+ krb5_data_zero(&logon);
+
+ if (p->logon_name == NULL)
+ num++;
+ if (p->server_checksum == NULL)
+ num++;
+ if (p->privsvr_checksum == NULL)
+ num++;
+
+ if (num) {
+ void *ptr;
+
+ ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1)));
+ if (ptr == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ p->pac = ptr;
+
+ if (p->logon_name == NULL) {
+ p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->logon_name, 0, sizeof(*p->logon_name));
+ p->logon_name->type = PAC_LOGON_NAME;
+ }
+ if (p->server_checksum == NULL) {
+ p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->server_checksum, 0, sizeof(*p->server_checksum));
+ p->server_checksum->type = PAC_SERVER_CHECKSUM;
+ }
+ if (p->privsvr_checksum == NULL) {
+ p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
+ p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
+ }
+ }
+
+ /* Calculate LOGON NAME */
+ ret = build_logon_name(context, authtime, principal, &logon);
+ if (ret)
+ goto out;
+
+ /* Set lengths for checksum */
+ ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
+ if (ret)
+ goto out;
+ ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
+ if (ret)
+ goto out;
+
+ /* Encode PAC */
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ spdata = krb5_storage_emem();
+ if (spdata == NULL) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
+ CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
+
+ end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
+
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ uint32_t len;
+ size_t sret;
+ void *ptr = NULL;
+
+ /* store data */
+
+ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
+ len = server_size + 4;
+ server_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, server_size), out);
+ } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
+ len = priv_size + 4;
+ priv_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, priv_size), out);
+ } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
+ len = krb5_storage_write(spdata, logon.data, logon.length);
+ if (logon.length != len) {
+ ret = EINVAL;
+ goto out;
+ }
+ } else {
+ len = p->pac->buffers[i].buffersize;
+ ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;
+
+ sret = krb5_storage_write(spdata, ptr, len);
+ if (sret != len) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ /* XXX if not aligned, fill_zeros */
+ }
+
+ /* write header */
+ CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
+ CHECK(ret, krb5_store_uint32(sp, len), out);
+ CHECK(ret, krb5_store_uint32(sp, end), out);
+ CHECK(ret, krb5_store_uint32(sp, 0), out);
+
+ /* advance data endpointer and align */
+ {
+ int32_t e;
+
+ end += len;
+ e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
+ if (end != e) {
+ CHECK(ret, fill_zeros(context, spdata, e - end), out);
+ }
+ end = e;
+ }
+
+ }
+
+ /* assert (server_offset != 0 && priv_offset != 0); */
+
+ /* export PAC */
+ ret = krb5_storage_to_data(spdata, &d);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = krb5_storage_write(sp, d.data, d.length);
+ if (ret != d.length) {
+ krb5_data_free(&d);
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ krb5_data_free(&d);
+
+ ret = krb5_storage_to_data(sp, &d);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ /* sign */
+
+ ret = create_checksum(context, server_key,
+ d.data, d.length,
+ (char *)d.data + server_offset, server_size);
+ if (ret) {
+ krb5_data_free(&d);
+ goto out;
+ }
+
+ ret = create_checksum(context, priv_key,
+ (char *)d.data + server_offset, server_size,
+ (char *)d.data + priv_offset, priv_size);
+ if (ret) {
+ krb5_data_free(&d);
+ goto out;
+ }
+
+ /* done */
+ *data = d;
+
+ krb5_data_free(&logon);
+ krb5_storage_free(sp);
+ krb5_storage_free(spdata);
+
+ return 0;
+out:
+ krb5_data_free(&logon);
+ if (sp)
+ krb5_storage_free(sp);
+ if (spdata)
+ krb5_storage_free(spdata);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/padata.c b/source4/heimdal/lib/krb5/padata.c
new file mode 100644
index 0000000000..2cd3c18287
--- /dev/null
+++ b/source4/heimdal/lib/krb5/padata.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+PA_DATA *
+krb5_find_padata(PA_DATA *val, unsigned len, int type, int *idx)
+{
+ for(; *idx < len; (*idx)++)
+ if(val[*idx].padata_type == type)
+ return val + *idx;
+ return NULL;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_padata_add(krb5_context context, METHOD_DATA *md,
+ int type, void *buf, size_t len)
+{
+ PA_DATA *pa;
+
+ pa = realloc (md->val, (md->len + 1) * sizeof(*md->val));
+ if (pa == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ md->val = pa;
+
+ pa[md->len].padata_type = type;
+ pa[md->len].padata_value.length = len;
+ pa[md->len].padata_value.data = buf;
+ md->len++;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/pkinit.c b/source4/heimdal/lib/krb5/pkinit.c
new file mode 100644
index 0000000000..634ef26c7f
--- /dev/null
+++ b/source4/heimdal/lib/krb5/pkinit.c
@@ -0,0 +1,2097 @@
+/*
+ * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct krb5_dh_moduli {
+ char *name;
+ unsigned long bits;
+ heim_integer p;
+ heim_integer g;
+ heim_integer q;
+};
+
+#ifdef PKINIT
+
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <pkinit_asn1.h>
+#include <asn1_err.h>
+
+#include <der.h>
+
+struct krb5_pk_cert {
+ hx509_cert cert;
+};
+
+struct krb5_pk_init_ctx_data {
+ struct krb5_pk_identity *id;
+ DH *dh;
+ krb5_data *clientDHNonce;
+ struct krb5_dh_moduli **m;
+ hx509_peer_info peer;
+ enum krb5_pk_type type;
+ unsigned int require_binding:1;
+ unsigned int require_eku:1;
+ unsigned int require_krbtgt_otherName:1;
+ unsigned int require_hostname_match:1;
+ unsigned int trustedCertifiers:1;
+};
+
+static void
+pk_copy_error(krb5_context context,
+ hx509_context hx509ctx,
+ int hxret,
+ const char *fmt,
+ ...)
+ __attribute__ ((format (printf, 4, 5)));
+
+/*
+ *
+ */
+
+void KRB5_LIB_FUNCTION
+_krb5_pk_cert_free(struct krb5_pk_cert *cert)
+{
+ if (cert->cert) {
+ hx509_cert_free(cert->cert);
+ }
+ free(cert);
+}
+
+static krb5_error_code
+BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
+{
+ integer->length = BN_num_bytes(bn);
+ integer->data = malloc(integer->length);
+ if (integer->data == NULL) {
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, integer->data);
+ integer->negative = BN_is_negative(bn);
+ return 0;
+}
+
+static BIGNUM *
+integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
+ if (bn == NULL) {
+ krb5_set_error_message(context, ENOMEM, "PKINIT: parsing BN failed %s", field);
+ return NULL;
+ }
+ BN_set_negative(bn, f->negative);
+ return bn;
+}
+
+struct certfind {
+ const char *type;
+ const heim_oid *oid;
+};
+
+/*
+ * Try searchin the key by to use by first looking for for PK-INIT
+ * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
+ */
+
+static krb5_error_code
+find_cert(krb5_context context, struct krb5_pk_identity *id,
+ hx509_query *q, hx509_cert *cert)
+{
+ struct certfind cf[3] = {
+ { "PKINIT EKU" },
+ { "MS EKU" },
+ { "no" }
+ };
+ int i, ret;
+
+ cf[0].oid = oid_id_pkekuoid();
+ cf[1].oid = oid_id_pkinit_ms_eku();
+ cf[2].oid = NULL;
+
+ for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
+ ret = hx509_query_match_eku(q, cf[i].oid);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed setting %s OID", cf[i].type);
+ return ret;
+ }
+
+ ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert);
+ if (ret == 0)
+ break;
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed cert for finding %s OID", cf[i].type);
+ }
+ return ret;
+}
+
+
+static krb5_error_code
+create_signature(krb5_context context,
+ const heim_oid *eContentType,
+ krb5_data *eContent,
+ struct krb5_pk_identity *id,
+ hx509_peer_info peer,
+ krb5_data *sd_data)
+{
+ hx509_cert cert = NULL;
+ hx509_query *q = NULL;
+ int ret;
+
+ ret = hx509_query_alloc(id->hx509ctx, &q);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Allocate query to find signing certificate");
+ return ret;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = find_cert(context, id, q, &cert);
+ hx509_query_free(id->hx509ctx, q);
+ if (ret)
+ return ret;
+
+ ret = hx509_cms_create_signed_1(id->hx509ctx,
+ 0,
+ eContentType,
+ eContent->data,
+ eContent->length,
+ NULL,
+ cert,
+ peer,
+ NULL,
+ id->certs,
+ sd_data);
+ hx509_cert_free(cert);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Create CMS signedData");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+cert2epi(hx509_context context, void *ctx, hx509_cert c)
+{
+ ExternalPrincipalIdentifiers *ids = ctx;
+ ExternalPrincipalIdentifier id;
+ hx509_name subject = NULL;
+ void *p;
+ int ret;
+
+ memset(&id, 0, sizeof(id));
+
+ ret = hx509_cert_get_subject(c, &subject);
+ if (ret)
+ return ret;
+
+ if (hx509_name_is_null_p(subject) != 0) {
+
+ id.subjectName = calloc(1, sizeof(*id.subjectName));
+ if (id.subjectName == NULL) {
+ hx509_name_free(&subject);
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ ret = hx509_name_binary(subject, id.subjectName);
+ if (ret) {
+ hx509_name_free(&subject);
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+ }
+ hx509_name_free(&subject);
+
+
+ id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
+ if (id.issuerAndSerialNumber == NULL) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ {
+ IssuerAndSerialNumber iasn;
+ hx509_name issuer;
+ size_t size;
+
+ memset(&iasn, 0, sizeof(iasn));
+
+ ret = hx509_cert_get_issuer(c, &issuer);
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ret = hx509_name_to_Name(issuer, &iasn.issuer);
+ hx509_name_free(&issuer);
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
+ if (ret) {
+ free_IssuerAndSerialNumber(&iasn);
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
+ id.issuerAndSerialNumber->data,
+ id.issuerAndSerialNumber->length,
+ &iasn, &size, ret);
+ free_IssuerAndSerialNumber(&iasn);
+ if (ret)
+ return ret;
+ if (id.issuerAndSerialNumber->length != size)
+ abort();
+ }
+
+ id.subjectKeyIdentifier = NULL;
+
+ p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
+ if (p == NULL) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ ids->val = p;
+ ids->val[ids->len] = id;
+ ids->len++;
+
+ return 0;
+}
+
+static krb5_error_code
+build_edi(krb5_context context,
+ hx509_context hx509ctx,
+ hx509_certs certs,
+ ExternalPrincipalIdentifiers *ids)
+{
+ return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
+}
+
+static krb5_error_code
+build_auth_pack(krb5_context context,
+ unsigned nonce,
+ krb5_pk_init_ctx ctx,
+ DH *dh,
+ const KDC_REQ_BODY *body,
+ AuthPack *a)
+{
+ size_t buf_size, len;
+ krb5_error_code ret;
+ void *buf;
+ krb5_timestamp sec;
+ int32_t usec;
+ Checksum checksum;
+
+ krb5_clear_error_string(context);
+
+ memset(&checksum, 0, sizeof(checksum));
+
+ krb5_us_timeofday(context, &sec, &usec);
+ a->pkAuthenticator.ctime = sec;
+ a->pkAuthenticator.nonce = nonce;
+
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
+ if (ret)
+ return ret;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_SHA1,
+ buf,
+ len,
+ &checksum);
+ free(buf);
+ if (ret)
+ return ret;
+
+ ALLOC(a->pkAuthenticator.paChecksum, 1);
+ if (a->pkAuthenticator.paChecksum == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
+ checksum.checksum.data, checksum.checksum.length);
+ free_Checksum(&checksum);
+ if (ret)
+ return ret;
+
+ if (dh) {
+ DomainParameters dp;
+ heim_integer dh_pub_key;
+ krb5_data dhbuf;
+ size_t size;
+
+ if (1 /* support_cached_dh */) {
+ ALLOC(a->clientDHNonce, 1);
+ if (a->clientDHNonce == NULL) {
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+ ret = krb5_data_alloc(a->clientDHNonce, 40);
+ if (a->clientDHNonce == NULL) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+ memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
+ ret = krb5_copy_data(context, a->clientDHNonce,
+ &ctx->clientDHNonce);
+ if (ret)
+ return ret;
+ }
+
+ ALLOC(a->clientPublicValue, 1);
+ if (a->clientPublicValue == NULL)
+ return ENOMEM;
+ ret = der_copy_oid(oid_id_dhpublicnumber(),
+ &a->clientPublicValue->algorithm.algorithm);
+ if (ret)
+ return ret;
+
+ memset(&dp, 0, sizeof(dp));
+
+ ret = BN_to_integer(context, dh->p, &dp.p);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ ret = BN_to_integer(context, dh->g, &dp.g);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ ret = BN_to_integer(context, dh->q, &dp.q);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ dp.j = NULL;
+ dp.validationParms = NULL;
+
+ a->clientPublicValue->algorithm.parameters =
+ malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
+ if (a->clientPublicValue->algorithm.parameters == NULL) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(DomainParameters,
+ a->clientPublicValue->algorithm.parameters->data,
+ a->clientPublicValue->algorithm.parameters->length,
+ &dp, &size, ret);
+ free_DomainParameters(&dp);
+ if (ret)
+ return ret;
+ if (size != a->clientPublicValue->algorithm.parameters->length)
+ krb5_abortx(context, "Internal ASN1 encoder error");
+
+ ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
+ &dh_pub_key, &size, ret);
+ der_free_heim_integer(&dh_pub_key);
+ if (ret)
+ return ret;
+ if (size != dhbuf.length)
+ krb5_abortx(context, "asn1 internal error");
+
+ a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
+ a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
+ }
+
+ {
+ a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
+ if (a->supportedCMSTypes == NULL)
+ return ENOMEM;
+
+ ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
+ &a->supportedCMSTypes->val,
+ &a->supportedCMSTypes->len);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_mk_ContentInfo(krb5_context context,
+ const krb5_data *buf,
+ const heim_oid *oid,
+ struct ContentInfo *content_info)
+{
+ krb5_error_code ret;
+
+ ret = der_copy_oid(oid, &content_info->contentType);
+ if (ret)
+ return ret;
+ ALLOC(content_info->content, 1);
+ if (content_info->content == NULL)
+ return ENOMEM;
+ content_info->content->data = malloc(buf->length);
+ if (content_info->content->data == NULL)
+ return ENOMEM;
+ memcpy(content_info->content->data, buf->data, buf->length);
+ content_info->content->length = buf->length;
+ return 0;
+}
+
+static krb5_error_code
+pk_mk_padata(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ const KDC_REQ_BODY *req_body,
+ unsigned nonce,
+ METHOD_DATA *md)
+{
+ struct ContentInfo content_info;
+ krb5_error_code ret;
+ const heim_oid *oid;
+ size_t size;
+ krb5_data buf, sd_buf;
+ int pa_type;
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(&sd_buf);
+ memset(&content_info, 0, sizeof(content_info));
+
+ if (ctx->type == PKINIT_WIN2K) {
+ AuthPack_Win2k ap;
+ krb5_timestamp sec;
+ int32_t usec;
+
+ memset(&ap, 0, sizeof(ap));
+
+ /* fill in PKAuthenticator */
+ ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ krb5_us_timeofday(context, &sec, &usec);
+ ap.pkAuthenticator.ctime = sec;
+ ap.pkAuthenticator.cusec = usec;
+ ap.pkAuthenticator.nonce = nonce;
+
+ ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
+ &ap, &size, ret);
+ free_AuthPack_Win2k(&ap);
+ if (ret) {
+ krb5_set_error_message(context, ret, "AuthPack_Win2k: %d",
+ (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "internal ASN1 encoder error");
+
+ oid = oid_id_pkcs7_data();
+ } else if (ctx->type == PKINIT_27) {
+ AuthPack ap;
+
+ memset(&ap, 0, sizeof(ap));
+
+ ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
+ free_AuthPack(&ap);
+ if (ret) {
+ krb5_set_error_message(context, ret, "AuthPack: %d", (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "internal ASN1 encoder error");
+
+ oid = oid_id_pkauthdata();
+ } else
+ krb5_abortx(context, "internal pkinit error");
+
+ ret = create_signature(context, oid, &buf, ctx->id,
+ ctx->peer, &sd_buf);
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
+ krb5_data_free(&sd_buf);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "ContentInfo wrapping of signedData failed");
+ goto out;
+ }
+
+ if (ctx->type == PKINIT_WIN2K) {
+ PA_PK_AS_REQ_Win2k winreq;
+
+ pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
+
+ memset(&winreq, 0, sizeof(winreq));
+
+ winreq.signed_auth_pack = buf;
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
+ &winreq, &size, ret);
+ free_PA_PK_AS_REQ_Win2k(&winreq);
+
+ } else if (ctx->type == PKINIT_27) {
+ PA_PK_AS_REQ req;
+
+ pa_type = KRB5_PADATA_PK_AS_REQ;
+
+ memset(&req, 0, sizeof(req));
+ req.signedAuthPack = buf;
+
+ if (ctx->trustedCertifiers) {
+
+ req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
+ if (req.trustedCertifiers == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ free_PA_PK_AS_REQ(&req);
+ goto out;
+ }
+ ret = build_edi(context, ctx->id->hx509ctx,
+ ctx->id->anchors, req.trustedCertifiers);
+ if (ret) {
+ krb5_set_error_message(context, ret, "pk-init: failed to build trustedCertifiers");
+ free_PA_PK_AS_REQ(&req);
+ goto out;
+ }
+ }
+ req.kdcPkId = NULL;
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
+ &req, &size, ret);
+
+ free_PA_PK_AS_REQ(&req);
+
+ } else
+ krb5_abortx(context, "internal pkinit error");
+ if (ret) {
+ krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN1 encoder error");
+
+ ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
+ if (ret)
+ free(buf.data);
+
+ if (ret == 0 && ctx->type == PKINIT_WIN2K)
+ krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
+
+ out:
+ free_ContentInfo(&content_info);
+
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_mk_padata(krb5_context context,
+ void *c,
+ const KDC_REQ_BODY *req_body,
+ unsigned nonce,
+ METHOD_DATA *md)
+{
+ krb5_pk_init_ctx ctx = c;
+ int win2k_compat;
+
+ win2k_compat = krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "realms",
+ req_body->realm,
+ "pkinit_win2k",
+ NULL);
+
+ if (win2k_compat) {
+ ctx->require_binding =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "realms",
+ req_body->realm,
+ "pkinit_win2k_require_binding",
+ NULL);
+ ctx->type = PKINIT_WIN2K;
+ } else
+ ctx->type = PKINIT_27;
+
+ ctx->require_eku =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_eku",
+ NULL);
+ ctx->require_krbtgt_otherName =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_krbtgt_otherName",
+ NULL);
+
+ ctx->require_hostname_match =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_hostname_match",
+ NULL);
+
+ ctx->trustedCertifiers =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_trustedCertifiers",
+ NULL);
+
+ return pk_mk_padata(context, ctx, req_body, nonce, md);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_verify_sign(krb5_context context,
+ const void *data,
+ size_t length,
+ struct krb5_pk_identity *id,
+ heim_oid *contentType,
+ krb5_data *content,
+ struct krb5_pk_cert **signer)
+{
+ hx509_certs signer_certs;
+ int ret;
+
+ *signer = NULL;
+
+ ret = hx509_cms_verify_signed(id->hx509ctx,
+ id->verify_ctx,
+ data,
+ length,
+ NULL,
+ id->certpool,
+ contentType,
+ content,
+ &signer_certs);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "CMS verify signed failed");
+ return ret;
+ }
+
+ *signer = calloc(1, sizeof(**signer));
+ if (*signer == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to get on of the signer certs");
+ goto out;
+ }
+
+ out:
+ hx509_certs_free(&signer_certs);
+ if (ret) {
+ if (*signer) {
+ hx509_cert_free((*signer)->cert);
+ free(*signer);
+ *signer = NULL;
+ }
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+get_reply_key_win(krb5_context context,
+ const krb5_data *content,
+ unsigned nonce,
+ krb5_keyblock **key)
+{
+ ReplyKeyPack_Win2k key_pack;
+ krb5_error_code ret;
+ size_t size;
+
+ ret = decode_ReplyKeyPack_Win2k(content->data,
+ content->length,
+ &key_pack,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PKINIT decoding reply key failed");
+ free_ReplyKeyPack_Win2k(&key_pack);
+ return ret;
+ }
+
+ if (key_pack.nonce != nonce) {
+ krb5_set_error_message(context, ret, "PKINIT enckey nonce is wrong");
+ free_ReplyKeyPack_Win2k(&key_pack);
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ free_ReplyKeyPack_Win2k(&key_pack);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = copy_EncryptionKey(&key_pack.replyKey, *key);
+ free_ReplyKeyPack_Win2k(&key_pack);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PKINIT failed copying reply key");
+ free(*key);
+ *key = NULL;
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+get_reply_key(krb5_context context,
+ const krb5_data *content,
+ const krb5_data *req_buffer,
+ krb5_keyblock **key)
+{
+ ReplyKeyPack key_pack;
+ krb5_error_code ret;
+ size_t size;
+
+ ret = decode_ReplyKeyPack(content->data,
+ content->length,
+ &key_pack,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PKINIT decoding reply key failed");
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+
+ {
+ krb5_crypto crypto;
+
+ /*
+ * XXX Verify kp.replyKey is a allowed enctype in the
+ * configuration file
+ */
+
+ ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
+ if (ret) {
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+
+ ret = krb5_verify_checksum(context, crypto, 6,
+ req_buffer->data, req_buffer->length,
+ &key_pack.asChecksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ free_ReplyKeyPack(&key_pack);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = copy_EncryptionKey(&key_pack.replyKey, *key);
+ free_ReplyKeyPack(&key_pack);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PKINIT failed copying reply key");
+ free(*key);
+ *key = NULL;
+ }
+
+ return ret;
+}
+
+
+static krb5_error_code
+pk_verify_host(krb5_context context,
+ const char *realm,
+ const krb5_krbhst_info *hi,
+ struct krb5_pk_init_ctx_data *ctx,
+ struct krb5_pk_cert *host)
+{
+ krb5_error_code ret = 0;
+
+ if (ctx->require_eku) {
+ ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
+ oid_id_pkkdcekuoid(), 0);
+ if (ret) {
+ krb5_set_error_message(context, ret, "No PK-INIT KDC EKU in kdc certificate");
+ return ret;
+ }
+ }
+ if (ctx->require_krbtgt_otherName) {
+ hx509_octet_string_list list;
+ int i;
+
+ ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
+ host->cert,
+ oid_id_pkinit_san(),
+ &list);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to find the PK-INIT "
+ "subjectAltName in the KDC certificate");
+
+ return ret;
+ }
+
+ for (i = 0; i < list.len; i++) {
+ KRB5PrincipalName r;
+
+ ret = decode_KRB5PrincipalName(list.val[i].data,
+ list.val[i].length,
+ &r,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode the PK-INIT "
+ "subjectAltName in the KDC certificate");
+
+ break;
+ }
+
+ if (r.principalName.name_string.len != 2 ||
+ strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
+ strcmp(r.principalName.name_string.val[1], realm) != 0 ||
+ strcmp(r.realm, realm) != 0)
+ {
+ ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
+ krb5_set_error_message(context, ret, "KDC have wrong realm name in "
+ "the certificate");
+ }
+
+ free_KRB5PrincipalName(&r);
+ if (ret)
+ break;
+ }
+ hx509_free_octet_string_list(&list);
+ }
+ if (ret)
+ return ret;
+
+ if (hi) {
+ ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
+ ctx->require_hostname_match,
+ HX509_HN_HOSTNAME,
+ hi->hostname,
+ hi->ai->ai_addr, hi->ai->ai_addrlen);
+
+ if (ret)
+ krb5_set_error_message(context, ret, "Address mismatch in "
+ "the KDC certificate");
+ }
+ return ret;
+}
+
+static krb5_error_code
+pk_rd_pa_reply_enckey(krb5_context context,
+ int type,
+ const heim_octet_string *indata,
+ const heim_oid *dataType,
+ const char *realm,
+ krb5_pk_init_ctx ctx,
+ krb5_enctype etype,
+ const krb5_krbhst_info *hi,
+ unsigned nonce,
+ const krb5_data *req_buffer,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+ struct krb5_pk_cert *host = NULL;
+ krb5_data content;
+ heim_oid contentType = { 0, NULL };
+
+ if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
+ krb5_set_error_message(context, EINVAL, "PKINIT: Invalid content type");
+ return EINVAL;
+ }
+
+ ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
+ ctx->id->certs,
+ HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
+ indata->data,
+ indata->length,
+ NULL,
+ 0,
+ &contentType,
+ &content);
+ if (ret) {
+ pk_copy_error(context, ctx->id->hx509ctx, ret,
+ "Failed to unenvelope CMS data in PK-INIT reply");
+ return ret;
+ }
+ der_free_oid(&contentType);
+
+#if 0 /* windows LH with interesting CMS packets, leaks memory */
+ {
+ size_t ph = 1 + der_length_len (length);
+ unsigned char *ptr = malloc(length + ph);
+ size_t l;
+
+ memcpy(ptr + ph, p, length);
+
+ ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
+ ASN1_C_UNIV, CONS, UT_Sequence, &l);
+ if (ret)
+ return ret;
+ ptr += ph - l;
+ length += l;
+ p = ptr;
+ }
+#endif
+
+ /* win2k uses ContentInfo */
+ if (type == PKINIT_WIN2K) {
+ heim_oid type;
+ heim_octet_string out;
+
+ ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
+ if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
+ ret = EINVAL; /* XXX */
+ krb5_set_error_message(context, ret, "PKINIT: Invalid content type");
+ der_free_oid(&type);
+ der_free_octet_string(&out);
+ goto out;
+ }
+ der_free_oid(&type);
+ krb5_data_free(&content);
+ ret = krb5_data_copy(&content, out.data, out.length);
+ der_free_octet_string(&out);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PKINIT: out of memory");
+ goto out;
+ }
+ }
+
+ ret = _krb5_pk_verify_sign(context,
+ content.data,
+ content.length,
+ ctx->id,
+ &contentType,
+ &content,
+ &host);
+ if (ret)
+ goto out;
+
+ /* make sure that it is the kdc's certificate */
+ ret = pk_verify_host(context, realm, hi, ctx, host);
+ if (ret) {
+ goto out;
+ }
+
+#if 0
+ if (type == PKINIT_WIN2K) {
+ if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
+ goto out;
+ }
+ } else {
+ if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
+ goto out;
+ }
+ }
+#endif
+
+ switch(type) {
+ case PKINIT_WIN2K:
+ ret = get_reply_key(context, &content, req_buffer, key);
+ if (ret != 0 && ctx->require_binding == 0)
+ ret = get_reply_key_win(context, &content, nonce, key);
+ break;
+ case PKINIT_27:
+ ret = get_reply_key(context, &content, req_buffer, key);
+ break;
+ }
+ if (ret)
+ goto out;
+
+ /* XXX compare given etype with key->etype */
+
+ out:
+ if (host)
+ _krb5_pk_cert_free(host);
+ der_free_oid(&contentType);
+ krb5_data_free(&content);
+
+ return ret;
+}
+
+static krb5_error_code
+pk_rd_pa_reply_dh(krb5_context context,
+ const heim_octet_string *indata,
+ const heim_oid *dataType,
+ const char *realm,
+ krb5_pk_init_ctx ctx,
+ krb5_enctype etype,
+ const krb5_krbhst_info *hi,
+ const DHNonce *c_n,
+ const DHNonce *k_n,
+ unsigned nonce,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ unsigned char *p, *dh_gen_key = NULL;
+ struct krb5_pk_cert *host = NULL;
+ BIGNUM *kdc_dh_pubkey = NULL;
+ KDCDHKeyInfo kdc_dh_info;
+ heim_oid contentType = { 0, NULL };
+ krb5_data content;
+ krb5_error_code ret;
+ int dh_gen_keylen;
+ size_t size;
+
+ krb5_data_zero(&content);
+ memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
+
+ if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
+ krb5_set_error_message(context, EINVAL, "PKINIT: Invalid content type");
+ return EINVAL;
+ }
+
+ ret = _krb5_pk_verify_sign(context,
+ indata->data,
+ indata->length,
+ ctx->id,
+ &contentType,
+ &content,
+ &host);
+ if (ret)
+ goto out;
+
+ /* make sure that it is the kdc's certificate */
+ ret = pk_verify_host(context, realm, hi, ctx, host);
+ if (ret)
+ goto out;
+
+ if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret, "pkinit - dh reply contains wrong oid");
+ goto out;
+ }
+
+ ret = decode_KDCDHKeyInfo(content.data,
+ content.length,
+ &kdc_dh_info,
+ &size);
+
+ if (ret) {
+ krb5_set_error_message(context, ret, "pkinit - "
+ "failed to decode KDC DH Key Info");
+ goto out;
+ }
+
+ if (kdc_dh_info.nonce != nonce) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_set_error_message(context, ret, "PKINIT: DH nonce is wrong");
+ goto out;
+ }
+
+ if (kdc_dh_info.dhKeyExpiration) {
+ if (k_n == NULL) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "pkinit; got key expiration "
+ "without server nonce");
+ goto out;
+ }
+ if (c_n == NULL) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "pkinit; got DH reuse but no "
+ "client nonce");
+ goto out;
+ }
+ } else {
+ if (k_n) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "pkinit: got server nonce "
+ "without key expiration");
+ goto out;
+ }
+ c_n = NULL;
+ }
+
+
+ p = kdc_dh_info.subjectPublicKey.data;
+ size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
+
+ {
+ DHPublicKey k;
+ ret = decode_DHPublicKey(p, size, &k, NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "pkinit: can't decode "
+ "without key expiration");
+ goto out;
+ }
+
+ kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
+ free_DHPublicKey(&k);
+ if (kdc_dh_pubkey == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+
+ dh_gen_keylen = DH_size(ctx->dh);
+ size = BN_num_bytes(ctx->dh->p);
+ if (size < dh_gen_keylen)
+ size = dh_gen_keylen;
+
+ dh_gen_key = malloc(size);
+ if (dh_gen_key == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ memset(dh_gen_key, 0, size - dh_gen_keylen);
+
+ dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
+ kdc_dh_pubkey, ctx->dh);
+ if (dh_gen_keylen == -1) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ "PKINIT: Can't compute Diffie-Hellman key");
+ goto out;
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ret = _krb5_pk_octetstring2key(context,
+ etype,
+ dh_gen_key, dh_gen_keylen,
+ c_n, k_n,
+ *key);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "PKINIT: can't create key from DH key");
+ free(*key);
+ *key = NULL;
+ goto out;
+ }
+
+ out:
+ if (kdc_dh_pubkey)
+ BN_free(kdc_dh_pubkey);
+ if (dh_gen_key) {
+ memset(dh_gen_key, 0, DH_size(ctx->dh));
+ free(dh_gen_key);
+ }
+ if (host)
+ _krb5_pk_cert_free(host);
+ if (content.data)
+ krb5_data_free(&content);
+ der_free_oid(&contentType);
+ free_KDCDHKeyInfo(&kdc_dh_info);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_rd_pa_reply(krb5_context context,
+ const char *realm,
+ void *c,
+ krb5_enctype etype,
+ const krb5_krbhst_info *hi,
+ unsigned nonce,
+ const krb5_data *req_buffer,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ krb5_pk_init_ctx ctx = c;
+ krb5_error_code ret;
+ size_t size;
+
+ /* Check for IETF PK-INIT first */
+ if (ctx->type == PKINIT_27) {
+ PA_PK_AS_REP rep;
+ heim_octet_string os, data;
+ heim_oid oid;
+
+ if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
+ krb5_set_error_message(context, EINVAL, "PKINIT: wrong padata recv");
+ return EINVAL;
+ }
+
+ ret = decode_PA_PK_AS_REP(pa->padata_value.data,
+ pa->padata_value.length,
+ &rep,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode pkinit AS rep");
+ return ret;
+ }
+
+ switch (rep.element) {
+ case choice_PA_PK_AS_REP_dhInfo:
+ os = rep.u.dhInfo.dhSignedData;
+ break;
+ case choice_PA_PK_AS_REP_encKeyPack:
+ os = rep.u.encKeyPack;
+ break;
+ default:
+ free_PA_PK_AS_REP(&rep);
+ krb5_set_error_message(context, EINVAL, "PKINIT: -27 reply "
+ "invalid content type");
+ return EINVAL;
+ }
+
+ ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ krb5_set_error_message(context, ret, "PKINIT: failed to unwrap CI");
+ return ret;
+ }
+
+ switch (rep.element) {
+ case choice_PA_PK_AS_REP_dhInfo:
+ ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
+ ctx->clientDHNonce,
+ rep.u.dhInfo.serverDHNonce,
+ nonce, pa, key);
+ break;
+ case choice_PA_PK_AS_REP_encKeyPack:
+ ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
+ ctx, etype, hi, nonce, req_buffer, pa, key);
+ break;
+ default:
+ krb5_abortx(context, "pk-init as-rep case not possible to happen");
+ }
+ der_free_octet_string(&data);
+ der_free_oid(&oid);
+ free_PA_PK_AS_REP(&rep);
+
+ } else if (ctx->type == PKINIT_WIN2K) {
+ PA_PK_AS_REP_Win2k w2krep;
+
+ /* Check for Windows encoding of the AS-REP pa data */
+
+#if 0 /* should this be ? */
+ if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
+ krb5_set_error_message(context, EINVAL, "PKINIT: wrong padata recv");
+ return EINVAL;
+ }
+#endif
+
+ memset(&w2krep, 0, sizeof(w2krep));
+
+ ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
+ pa->padata_value.length,
+ &w2krep,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PKINIT: Failed decoding windows "
+ "pkinit reply %d", (int)ret);
+ return ret;
+ }
+
+ krb5_clear_error_string(context);
+
+ switch (w2krep.element) {
+ case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
+ heim_octet_string data;
+ heim_oid oid;
+
+ ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
+ &oid, &data, NULL);
+ free_PA_PK_AS_REP_Win2k(&w2krep);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PKINIT: failed to unwrap CI");
+ return ret;
+ }
+
+ ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
+ ctx, etype, hi, nonce, req_buffer, pa, key);
+ der_free_octet_string(&data);
+ der_free_oid(&oid);
+
+ break;
+ }
+ default:
+ free_PA_PK_AS_REP_Win2k(&w2krep);
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PKINIT: win2k reply invalid "
+ "content type");
+ break;
+ }
+
+ } else {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PKINIT: unknown reply type");
+ }
+
+ return ret;
+}
+
+struct prompter {
+ krb5_context context;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+};
+
+static int
+hx_pass_prompter(void *data, const hx509_prompt *prompter)
+{
+ krb5_error_code ret;
+ krb5_prompt prompt;
+ krb5_data password_data;
+ struct prompter *p = data;
+
+ password_data.data = prompter->reply.data;
+ password_data.length = prompter->reply.length;
+
+ prompt.prompt = prompter->prompt;
+ prompt.hidden = hx509_prompt_hidden(prompter->type);
+ prompt.reply = &password_data;
+
+ switch (prompter->type) {
+ case HX509_PROMPT_TYPE_INFO:
+ prompt.type = KRB5_PROMPT_TYPE_INFO;
+ break;
+ case HX509_PROMPT_TYPE_PASSWORD:
+ case HX509_PROMPT_TYPE_QUESTION:
+ default:
+ prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
+ break;
+ }
+
+ ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
+ if (ret) {
+ memset (prompter->reply.data, 0, prompter->reply.length);
+ return 1;
+ }
+ return 0;
+}
+
+
+void KRB5_LIB_FUNCTION
+_krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
+ int boolean)
+{
+ hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_load_id(krb5_context context,
+ struct krb5_pk_identity **ret_id,
+ const char *user_id,
+ const char *anchor_id,
+ char * const *chain_list,
+ char * const *revoke_list,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ char *password)
+{
+ struct krb5_pk_identity *id = NULL;
+ hx509_lock lock = NULL;
+ struct prompter p;
+ int ret;
+
+ *ret_id = NULL;
+
+ if (anchor_id == NULL) {
+ krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
+ "PKINIT: No anchor given");
+ return HEIM_PKINIT_NO_VALID_CA;
+ }
+
+ if (user_id == NULL) {
+ krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
+ "PKINIT: No user certificate given");
+ return HEIM_PKINIT_NO_PRIVATE_KEY;
+ }
+
+ /* load cert */
+
+ id = calloc(1, sizeof(*id));
+ if (id == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = hx509_context_init(&id->hx509ctx);
+ if (ret)
+ goto out;
+
+ ret = hx509_lock_init(id->hx509ctx, &lock);
+ if (password && password[0])
+ hx509_lock_add_password(lock, password);
+
+ if (prompter) {
+ p.context = context;
+ p.prompter = prompter;
+ p.prompter_data = prompter_data;
+
+ ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
+ if (ret)
+ goto out;
+ }
+
+ ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to init cert certs");
+ goto out;
+ }
+
+ ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to init anchors");
+ goto out;
+ }
+
+ ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
+ 0, NULL, &id->certpool);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to init chain");
+ goto out;
+ }
+
+ while (chain_list && *chain_list) {
+ ret = hx509_certs_append(id->hx509ctx, id->certpool,
+ NULL, *chain_list);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to laod chain %s",
+ *chain_list);
+ goto out;
+ }
+ chain_list++;
+ }
+
+ if (revoke_list) {
+ ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed init revoke list");
+ goto out;
+ }
+
+ while (*revoke_list) {
+ ret = hx509_revoke_add_crl(id->hx509ctx,
+ id->revokectx,
+ *revoke_list);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed load revoke list");
+ goto out;
+ }
+ revoke_list++;
+ }
+ } else
+ hx509_context_set_missing_revoke(id->hx509ctx, 1);
+
+ ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed init verify context");
+ goto out;
+ }
+
+ hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
+ hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
+
+ out:
+ if (ret) {
+ hx509_verify_destroy_ctx(id->verify_ctx);
+ hx509_certs_free(&id->certs);
+ hx509_certs_free(&id->anchors);
+ hx509_certs_free(&id->certpool);
+ hx509_revoke_free(&id->revokectx);
+ hx509_context_free(&id->hx509ctx);
+ free(id);
+ } else
+ *ret_id = id;
+
+ hx509_lock_free(lock);
+
+ return ret;
+}
+
+static krb5_error_code
+select_dh_group(krb5_context context, DH *dh, unsigned long bits,
+ struct krb5_dh_moduli **moduli)
+{
+ const struct krb5_dh_moduli *m;
+
+ if (bits == 0) {
+ m = moduli[1]; /* XXX */
+ if (m == NULL)
+ m = moduli[0]; /* XXX */
+ } else {
+ int i;
+ for (i = 0; moduli[i] != NULL; i++) {
+ if (bits < moduli[i]->bits)
+ break;
+ }
+ if (moduli[i] == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ "Did not find a DH group parameter "
+ "matching requirement of %lu bits",
+ bits);
+ return EINVAL;
+ }
+ m = moduli[i];
+ }
+
+ dh->p = integer_to_BN(context, "p", &m->p);
+ if (dh->p == NULL)
+ return ENOMEM;
+ dh->g = integer_to_BN(context, "g", &m->g);
+ if (dh->g == NULL)
+ return ENOMEM;
+ dh->q = integer_to_BN(context, "q", &m->q);
+ if (dh->q == NULL)
+ return ENOMEM;
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+pk_copy_error(krb5_context context,
+ hx509_context hx509ctx,
+ int hxret,
+ const char *fmt,
+ ...)
+{
+ va_list va;
+ char *s, *f;
+
+ va_start(va, fmt);
+ vasprintf(&f, fmt, va);
+ va_end(va);
+ if (f == NULL) {
+ krb5_clear_error_string(context);
+ return;
+ }
+
+ s = hx509_get_error_string(hx509ctx, hxret);
+ if (s == NULL) {
+ krb5_clear_error_string(context);
+ free(f);
+ return;
+ }
+ krb5_set_error_message(context, hxret, "%s: %s", f, s);
+ free(s);
+ free(f);
+}
+
+#endif /* PKINIT */
+
+static int
+parse_integer(krb5_context context, char **p, const char *file, int lineno,
+ const char *name, heim_integer *integer)
+{
+ int ret;
+ char *p1;
+ p1 = strsep(p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, EINVAL, "moduli file %s missing %s on line %d",
+ file, name, lineno);
+ return EINVAL;
+ }
+ ret = der_parse_hex_heim_integer(p1, integer);
+ if (ret) {
+ krb5_set_error_message(context, ret, "moduli file %s failed parsing %s "
+ "on line %d",
+ file, name, lineno);
+ return ret;
+ }
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_parse_moduli_line(krb5_context context,
+ const char *file,
+ int lineno,
+ char *p,
+ struct krb5_dh_moduli **m)
+{
+ struct krb5_dh_moduli *m1;
+ char *p1;
+ int ret;
+
+ *m = NULL;
+
+ m1 = calloc(1, sizeof(*m1));
+ if (m1 == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ while (isspace((unsigned char)*p))
+ p++;
+ if (*p == '#')
+ return 0;
+ ret = EINVAL;
+
+ p1 = strsep(&p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, ret, "moduli file %s missing name "
+ "on line %d", file, lineno);
+ goto out;
+ }
+ m1->name = strdup(p1);
+ if (p1 == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc - out of memeory");
+ goto out;
+ }
+
+ p1 = strsep(&p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, ret, "moduli file %s missing bits on line %d",
+ file, lineno);
+ goto out;
+ }
+
+ m1->bits = atoi(p1);
+ if (m1->bits == 0) {
+ krb5_set_error_message(context, ret, "moduli file %s have un-parsable "
+ "bits on line %d", file, lineno);
+ goto out;
+ }
+
+ ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
+ if (ret)
+ goto out;
+ ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
+ if (ret)
+ goto out;
+ ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
+ if (ret)
+ goto out;
+
+ *m = m1;
+
+ return 0;
+ out:
+ free(m1->name);
+ der_free_heim_integer(&m1->p);
+ der_free_heim_integer(&m1->g);
+ der_free_heim_integer(&m1->q);
+ free(m1);
+ return ret;
+}
+
+void
+_krb5_free_moduli(struct krb5_dh_moduli **moduli)
+{
+ int i;
+ for (i = 0; moduli[i] != NULL; i++) {
+ free(moduli[i]->name);
+ der_free_heim_integer(&moduli[i]->p);
+ der_free_heim_integer(&moduli[i]->g);
+ der_free_heim_integer(&moduli[i]->q);
+ free(moduli[i]);
+ }
+ free(moduli);
+}
+
+static const char *default_moduli_RFC2412_MODP_group2 =
+ /* name */
+ "RFC2412-MODP-group2 "
+ /* bits */
+ "1024 "
+ /* p */
+ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
+ "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
+ "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
+ "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
+ "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
+ "FFFFFFFF" "FFFFFFFF "
+ /* g */
+ "02 "
+ /* q */
+ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
+ "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
+ "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
+ "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
+ "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
+ "FFFFFFFF" "FFFFFFFF";
+
+static const char *default_moduli_rfc3526_MODP_group14 =
+ /* name */
+ "rfc3526-MODP-group14 "
+ /* bits */
+ "1760 "
+ /* p */
+ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
+ "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
+ "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
+ "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
+ "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
+ "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
+ "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
+ "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
+ "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
+ "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
+ "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
+ /* g */
+ "02 "
+ /* q */
+ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
+ "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
+ "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
+ "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
+ "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
+ "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
+ "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
+ "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
+ "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
+ "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
+ "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
+
+krb5_error_code
+_krb5_parse_moduli(krb5_context context, const char *file,
+ struct krb5_dh_moduli ***moduli)
+{
+ /* name bits P G Q */
+ krb5_error_code ret;
+ struct krb5_dh_moduli **m = NULL, **m2;
+ char buf[4096];
+ FILE *f;
+ int lineno = 0, n = 0;
+
+ *moduli = NULL;
+
+ m = calloc(1, sizeof(m[0]) * 3);
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
+ ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ n++;
+
+ strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
+ ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ n++;
+
+
+ if (file == NULL)
+ file = MODULI_FILE;
+
+ f = fopen(file, "r");
+ if (f == NULL) {
+ *moduli = m;
+ return 0;
+ }
+ rk_cloexec_file(f);
+
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ struct krb5_dh_moduli *element;
+
+ buf[strcspn(buf, "\n")] = '\0';
+ lineno++;
+
+ m2 = realloc(m, (n + 2) * sizeof(m[0]));
+ if (m2 == NULL) {
+ _krb5_free_moduli(m);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ m = m2;
+
+ m[n] = NULL;
+
+ ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ if (element == NULL)
+ continue;
+
+ m[n] = element;
+ m[n + 1] = NULL;
+ n++;
+ }
+ *moduli = m;
+ return 0;
+}
+
+krb5_error_code
+_krb5_dh_group_ok(krb5_context context, unsigned long bits,
+ heim_integer *p, heim_integer *g, heim_integer *q,
+ struct krb5_dh_moduli **moduli,
+ char **name)
+{
+ int i;
+
+ if (name)
+ *name = NULL;
+
+ for (i = 0; moduli[i] != NULL; i++) {
+ if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
+ der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
+ (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
+ {
+ if (bits && bits > moduli[i]->bits) {
+ krb5_set_error_message(context,
+ KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
+ "PKINIT: DH group parameter %s "
+ "no accepted, not enough bits generated",
+ moduli[i]->name);
+ return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+ }
+ if (name)
+ *name = strdup(moduli[i]->name);
+ return 0;
+ }
+ }
+ krb5_set_error_message(context,
+ KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
+ "PKINIT: DH group parameter no ok");
+ return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+}
+
+void KRB5_LIB_FUNCTION
+_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
+{
+#ifdef PKINIT
+ krb5_pk_init_ctx ctx;
+
+ if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
+ return;
+ ctx = opt->opt_private->pk_init_ctx;
+ if (ctx->dh)
+ DH_free(ctx->dh);
+ ctx->dh = NULL;
+ if (ctx->id) {
+ hx509_verify_destroy_ctx(ctx->id->verify_ctx);
+ hx509_certs_free(&ctx->id->certs);
+ hx509_certs_free(&ctx->id->anchors);
+ hx509_certs_free(&ctx->id->certpool);
+ hx509_context_free(&ctx->id->hx509ctx);
+
+ if (ctx->clientDHNonce) {
+ krb5_free_data(NULL, ctx->clientDHNonce);
+ ctx->clientDHNonce = NULL;
+ }
+ if (ctx->m)
+ _krb5_free_moduli(ctx->m);
+ free(ctx->id);
+ ctx->id = NULL;
+ }
+ free(opt->opt_private->pk_init_ctx);
+ opt->opt_private->pk_init_ctx = NULL;
+#endif
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_pkinit(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_principal principal,
+ const char *user_id,
+ const char *x509_anchors,
+ char * const * pool,
+ char * const * pki_revoke,
+ int flags,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ char *password)
+{
+#ifdef PKINIT
+ krb5_error_code ret;
+ char *anchors = NULL;
+
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, EINVAL, "PKINIT: on non extendable opt");
+ return EINVAL;
+ }
+
+ opt->opt_private->pk_init_ctx =
+ calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
+ if (opt->opt_private->pk_init_ctx == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ opt->opt_private->pk_init_ctx->dh = NULL;
+ opt->opt_private->pk_init_ctx->id = NULL;
+ opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
+ opt->opt_private->pk_init_ctx->require_binding = 0;
+ opt->opt_private->pk_init_ctx->require_eku = 1;
+ opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
+ opt->opt_private->pk_init_ctx->peer = NULL;
+
+ /* XXX implement krb5_appdefault_strings */
+ if (pool == NULL)
+ pool = krb5_config_get_strings(context, NULL,
+ "appdefaults",
+ "pkinit_pool",
+ NULL);
+
+ if (pki_revoke == NULL)
+ pki_revoke = krb5_config_get_strings(context, NULL,
+ "appdefaults",
+ "pkinit_revoke",
+ NULL);
+
+ if (x509_anchors == NULL) {
+ krb5_appdefault_string(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "pkinit_anchors", NULL, &anchors);
+ x509_anchors = anchors;
+ }
+
+ ret = _krb5_pk_load_id(context,
+ &opt->opt_private->pk_init_ctx->id,
+ user_id,
+ x509_anchors,
+ pool,
+ pki_revoke,
+ prompter,
+ prompter_data,
+ password);
+ if (ret) {
+ free(opt->opt_private->pk_init_ctx);
+ opt->opt_private->pk_init_ctx = NULL;
+ return ret;
+ }
+
+ if ((flags & 2) == 0) {
+ const char *moduli_file;
+ unsigned long dh_min_bits;
+
+ moduli_file = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "moduli",
+ NULL);
+
+ dh_min_bits =
+ krb5_config_get_int_default(context, NULL, 0,
+ "libdefaults",
+ "pkinit_dh_min_bits",
+ NULL);
+
+ ret = _krb5_parse_moduli(context, moduli_file,
+ &opt->opt_private->pk_init_ctx->m);
+ if (ret) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ return ret;
+ }
+
+ opt->opt_private->pk_init_ctx->dh = DH_new();
+ if (opt->opt_private->pk_init_ctx->dh == NULL) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
+ dh_min_bits,
+ opt->opt_private->pk_init_ctx->m);
+ if (ret) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ return ret;
+ }
+
+ if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ krb5_set_error_message(context, ENOMEM, "pkinit: failed to generate DH key");
+ return ENOMEM;
+ }
+ }
+
+ return 0;
+#else
+ krb5_set_error_message(context, EINVAL, "no support for PKINIT compiled in");
+ return EINVAL;
+#endif
+}
diff --git a/source4/heimdal/lib/krb5/plugin.c b/source4/heimdal/lib/krb5/plugin.c
new file mode 100644
index 0000000000..fb1ee32285
--- /dev/null
+++ b/source4/heimdal/lib/krb5/plugin.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#include <dirent.h>
+
+struct krb5_plugin {
+ void *symbol;
+ void *dsohandle;
+ struct krb5_plugin *next;
+};
+
+struct plugin {
+ enum krb5_plugin_type type;
+ void *name;
+ void *symbol;
+ struct plugin *next;
+};
+
+static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct plugin *registered = NULL;
+
+static const char *plugin_dir = LIBDIR "/plugin/krb5";
+
+/*
+ *
+ */
+
+void *
+_krb5_plugin_get_symbol(struct krb5_plugin *p)
+{
+ return p->symbol;
+}
+
+struct krb5_plugin *
+_krb5_plugin_get_next(struct krb5_plugin *p)
+{
+ return p->next;
+}
+
+/*
+ *
+ */
+
+#ifdef HAVE_DLOPEN
+
+static krb5_error_code
+loadlib(krb5_context context,
+ enum krb5_plugin_type type,
+ const char *name,
+ const char *lib,
+ struct krb5_plugin **e)
+{
+ *e = calloc(1, sizeof(**e));
+ if (*e == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+
+ (*e)->dsohandle = dlopen(lib, RTLD_LAZY);
+ if ((*e)->dsohandle == NULL) {
+ free(*e);
+ *e = NULL;
+ krb5_set_error_message(context, ENOMEM, "Failed to load %s: %s",
+ lib, dlerror());
+ return ENOMEM;
+ }
+
+ /* dlsym doesn't care about the type */
+ (*e)->symbol = dlsym((*e)->dsohandle, name);
+ if ((*e)->symbol == NULL) {
+ dlclose((*e)->dsohandle);
+ free(*e);
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+#endif /* HAVE_DLOPEN */
+
+/**
+ * Register a plugin symbol name of specific type.
+ * @param context a Keberos context
+ * @param type type of plugin symbol
+ * @param name name of plugin symbol
+ * @param symbol a pointer to the named symbol
+ * @return In case of error a non zero error com_err error is returned
+ * and the Kerberos error string is set.
+ *
+ * @ingroup krb5_support
+ */
+
+krb5_error_code
+krb5_plugin_register(krb5_context context,
+ enum krb5_plugin_type type,
+ const char *name,
+ void *symbol)
+{
+ struct plugin *e;
+
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ e->type = type;
+ e->name = strdup(name);
+ if (e->name == NULL) {
+ free(e);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ e->symbol = symbol;
+
+ HEIMDAL_MUTEX_lock(&plugin_mutex);
+ e->next = registered;
+ registered = e;
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_plugin_find(krb5_context context,
+ enum krb5_plugin_type type,
+ const char *name,
+ struct krb5_plugin **list)
+{
+ struct krb5_plugin *e;
+ struct plugin *p;
+ krb5_error_code ret;
+ char *sysdirs[2] = { NULL, NULL };
+ char **dirs = NULL, **di;
+ struct dirent *entry;
+ char *path;
+ DIR *d = NULL;
+
+ *list = NULL;
+
+ HEIMDAL_MUTEX_lock(&plugin_mutex);
+
+ for (p = registered; p != NULL; p = p->next) {
+ if (p->type != type || strcmp(p->name, name) != 0)
+ continue;
+
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ e->symbol = p->symbol;
+ e->dsohandle = NULL;
+ e->next = *list;
+ *list = e;
+ }
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+
+#ifdef HAVE_DLOPEN
+
+ dirs = krb5_config_get_strings(context, NULL, "libdefaults",
+ "plugin_dir", NULL);
+ if (dirs == NULL) {
+ sysdirs[0] = rk_UNCONST(plugin_dir);
+ dirs = sysdirs;
+ }
+
+ for (di = dirs; *di != NULL; di++) {
+
+ d = opendir(*di);
+ if (d == NULL)
+ continue;
+ rk_cloexec(dirfd(d));
+
+ while ((entry = readdir(d)) != NULL) {
+ asprintf(&path, "%s/%s", *di, entry->d_name);
+ if (path == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = loadlib(context, type, name, path, &e);
+ free(path);
+ if (ret)
+ continue;
+
+ e->next = *list;
+ *list = e;
+ }
+ closedir(d);
+ }
+ if (dirs != sysdirs)
+ krb5_config_free_strings(dirs);
+#endif /* HAVE_DLOPEN */
+
+ if (*list == NULL) {
+ krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name);
+ return ENOENT;
+ }
+
+ return 0;
+
+out:
+ if (dirs && dirs != sysdirs)
+ krb5_config_free_strings(dirs);
+ if (d)
+ closedir(d);
+ _krb5_plugin_free(*list);
+ *list = NULL;
+
+ return ret;
+}
+
+void
+_krb5_plugin_free(struct krb5_plugin *list)
+{
+ struct krb5_plugin *next;
+ while (list) {
+ next = list->next;
+ if (list->dsohandle)
+ dlclose(list->dsohandle);
+ free(list);
+ list = next;
+ }
+}
diff --git a/source4/heimdal/lib/krb5/principal.c b/source4/heimdal/lib/krb5/principal.c
new file mode 100644
index 0000000000..3a1d184c3d
--- /dev/null
+++ b/source4/heimdal/lib/krb5/principal.c
@@ -0,0 +1,1328 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * @page page_principal The principal handing functions.
+ *
+ * A Kerberos principal is a email address looking string that
+ * contains to parts separeted by a @. The later part is the kerbero
+ * realm the principal belongs to and the former is a list of 0 or
+ * more components. For example
+ * @verbatim
+lha@SU.SE
+host/hummel.it.su.se@SU.SE
+host/admin@H5L.ORG
+@endverbatim
+ *
+ * See the library functions here: @ref krb5_principal
+ */
+
+#include "krb5_locl.h"
+#ifdef HAVE_RES_SEARCH
+#define USE_RESOLVER
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#include <fnmatch.h>
+#include "resolve.h"
+
+RCSID("$Id$");
+
+#define princ_num_comp(P) ((P)->name.name_string.len)
+#define princ_type(P) ((P)->name.name_type)
+#define princ_comp(P) ((P)->name.name_string.val)
+#define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
+#define princ_realm(P) ((P)->realm)
+
+/**
+ * Frees a Kerberos principal allocated by the library with
+ * krb5_parse_name(), krb5_make_principal() or any other related
+ * principal functions.
+ *
+ * @param context A Kerberos context.
+ * @param p a principal to free.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+
+
+void KRB5_LIB_FUNCTION
+krb5_free_principal(krb5_context context,
+ krb5_principal p)
+{
+ if(p){
+ free_Principal(p);
+ free(p);
+ }
+}
+
+void KRB5_LIB_FUNCTION
+krb5_principal_set_type(krb5_context context,
+ krb5_principal principal,
+ int type)
+{
+ princ_type(principal) = type;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_principal_get_type(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_type(principal);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_principal_get_realm(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_realm(principal);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_principal_get_comp_string(krb5_context context,
+ krb5_const_principal principal,
+ unsigned int component)
+{
+ if(component >= princ_num_comp(principal))
+ return NULL;
+ return princ_ncomp(principal, component);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_parse_name_flags(krb5_context context,
+ const char *name,
+ int flags,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ heim_general_string *comp;
+ heim_general_string realm = NULL;
+ int ncomp;
+
+ const char *p;
+ char *q;
+ char *s;
+ char *start;
+
+ int n;
+ char c;
+ int got_realm = 0;
+ int first_at = 1;
+ int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
+
+ *principal = NULL;
+
+#define RFLAGS (KRB5_PRINCIPAL_PARSE_NO_REALM|KRB5_PRINCIPAL_PARSE_MUST_REALM)
+
+ if ((flags & RFLAGS) == RFLAGS) {
+ krb5_set_error_message(context, KRB5_ERR_NO_SERVICE,
+ "Can't require both realm and "
+ "no realm at the same time");
+ return KRB5_ERR_NO_SERVICE;
+ }
+#undef RFLAGS
+
+ /* count number of component,
+ * enterprise names only have one component
+ */
+ ncomp = 1;
+ if (!enterprise) {
+ for(p = name; *p; p++){
+ if(*p=='\\'){
+ if(!p[1]) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "trailing \\ in principal name");
+ return KRB5_PARSE_MALFORMED;
+ }
+ p++;
+ } else if(*p == '/')
+ ncomp++;
+ else if(*p == '@')
+ break;
+ }
+ }
+ comp = calloc(ncomp, sizeof(*comp));
+ if (comp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ n = 0;
+ p = start = q = s = strdup(name);
+ if (start == NULL) {
+ free (comp);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ while(*p){
+ c = *p++;
+ if(c == '\\'){
+ c = *p++;
+ if(c == 'n')
+ c = '\n';
+ else if(c == 't')
+ c = '\t';
+ else if(c == 'b')
+ c = '\b';
+ else if(c == '0')
+ c = '\0';
+ else if(c == '\0') {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ "trailing \\ in principal name");
+ goto exit;
+ }
+ }else if(enterprise && first_at) {
+ if (c == '@')
+ first_at = 0;
+ }else if((c == '/' && !enterprise) || c == '@'){
+ if(got_realm){
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ "part after realm in principal name");
+ goto exit;
+ }else{
+ comp[n] = malloc(q - start + 1);
+ if (comp[n] == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto exit;
+ }
+ memcpy(comp[n], start, q - start);
+ comp[n][q - start] = 0;
+ n++;
+ }
+ if(c == '@')
+ got_realm = 1;
+ start = q;
+ continue;
+ }
+ if(got_realm && (c == ':' || c == '/' || c == '\0')) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ "part after realm in principal name");
+ goto exit;
+ }
+ *q++ = c;
+ }
+ if(got_realm){
+ if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret, "realm found in 'short' principal "
+ "expected to be without one");
+ goto exit;
+ }
+ realm = malloc(q - start + 1);
+ if (realm == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto exit;
+ }
+ memcpy(realm, start, q - start);
+ realm[q - start] = 0;
+ }else{
+ if (flags & KRB5_PRINCIPAL_PARSE_MUST_REALM) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret, "realm NOT found in principal "
+ "expected to be with one");
+ goto exit;
+ } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
+ realm = NULL;
+ } else {
+ ret = krb5_get_default_realm (context, &realm);
+ if (ret)
+ goto exit;
+ }
+
+ comp[n] = malloc(q - start + 1);
+ if (comp[n] == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto exit;
+ }
+ memcpy(comp[n], start, q - start);
+ comp[n][q - start] = 0;
+ n++;
+ }
+ *principal = malloc(sizeof(**principal));
+ if (*principal == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto exit;
+ }
+ if (enterprise)
+ (*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL;
+ else
+ (*principal)->name.name_type = KRB5_NT_PRINCIPAL;
+ (*principal)->name.name_string.val = comp;
+ princ_num_comp(*principal) = n;
+ (*principal)->realm = realm;
+ free(s);
+ return 0;
+exit:
+ while(n>0){
+ free(comp[--n]);
+ }
+ free(comp);
+ free(realm);
+ free(s);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_parse_name(krb5_context context,
+ const char *name,
+ krb5_principal *principal)
+{
+ return krb5_parse_name_flags(context, name, 0, principal);
+}
+
+static const char quotable_chars[] = " \n\t\b\\/@";
+static const char replace_chars[] = " ntb\\/@";
+static const char nq_chars[] = " \\/@";
+
+#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
+
+static size_t
+quote_string(const char *s, char *out, size_t idx, size_t len, int display)
+{
+ const char *p, *q;
+ for(p = s; *p && idx < len; p++){
+ q = strchr(quotable_chars, *p);
+ if (q && display) {
+ add_char(out, idx, len, replace_chars[q - quotable_chars]);
+ } else if (q) {
+ add_char(out, idx, len, '\\');
+ add_char(out, idx, len, replace_chars[q - quotable_chars]);
+ }else
+ add_char(out, idx, len, *p);
+ }
+ if(idx < len)
+ out[idx] = '\0';
+ return idx;
+}
+
+
+static krb5_error_code
+unparse_name_fixed(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len,
+ int flags)
+{
+ size_t idx = 0;
+ int i;
+ int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
+ int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
+ int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
+
+ if (!no_realm && princ_realm(principal) == NULL) {
+ krb5_set_error_message(context, ERANGE,
+ "Realm missing from principal, "
+ "can't unparse");
+ return ERANGE;
+ }
+
+ for(i = 0; i < princ_num_comp(principal); i++){
+ if(i)
+ add_char(name, idx, len, '/');
+ idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
+ if(idx == len) {
+ krb5_set_error_message(context, ERANGE, "Out of space printing principal");
+ return ERANGE;
+ }
+ }
+ /* add realm if different from default realm */
+ if(short_form && !no_realm) {
+ krb5_realm r;
+ krb5_error_code ret;
+ ret = krb5_get_default_realm(context, &r);
+ if(ret)
+ return ret;
+ if(strcmp(princ_realm(principal), r) != 0)
+ short_form = 0;
+ free(r);
+ }
+ if(!short_form && !no_realm) {
+ add_char(name, idx, len, '@');
+ idx = quote_string(princ_realm(principal), name, idx, len, display);
+ if(idx == len) {
+ krb5_set_error_message(context, ERANGE,
+ "Out of space printing realm of principal");
+ return ERANGE;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_fixed(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len, 0);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_fixed_short(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len,
+ KRB5_PRINCIPAL_UNPARSE_SHORT);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_fixed_flags(krb5_context context,
+ krb5_const_principal principal,
+ int flags,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len, flags);
+}
+
+static krb5_error_code
+unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **name,
+ int flags)
+{
+ size_t len = 0, plen;
+ int i;
+ krb5_error_code ret;
+ /* count length */
+ if (princ_realm(principal)) {
+ plen = strlen(princ_realm(principal));
+
+ if(strcspn(princ_realm(principal), quotable_chars) == plen)
+ len += plen;
+ else
+ len += 2*plen;
+ len++; /* '@' */
+ }
+ for(i = 0; i < princ_num_comp(principal); i++){
+ plen = strlen(princ_ncomp(principal, i));
+ if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
+ len += plen;
+ else
+ len += 2*plen;
+ len++;
+ }
+ len++; /* '\0' */
+ *name = malloc(len);
+ if(*name == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = unparse_name_fixed(context, principal, *name, len, flags);
+ if(ret) {
+ free(*name);
+ *name = NULL;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **name)
+{
+ return unparse_name(context, principal, name, 0);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_flags(krb5_context context,
+ krb5_const_principal principal,
+ int flags,
+ char **name)
+{
+ return unparse_name(context, principal, name, flags);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_short(krb5_context context,
+ krb5_const_principal principal,
+ char **name)
+{
+ return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
+}
+
+#if 0 /* not implemented */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_ext(krb5_context context,
+ krb5_const_principal principal,
+ char **name,
+ size_t *size)
+{
+ krb5_abortx(context, "unimplemented krb5_unparse_name_ext called");
+}
+
+#endif
+
+krb5_realm * KRB5_LIB_FUNCTION
+krb5_princ_realm(krb5_context context,
+ krb5_principal principal)
+{
+ return &princ_realm(principal);
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_princ_set_realm(krb5_context context,
+ krb5_principal principal,
+ krb5_realm *realm)
+{
+ princ_realm(principal) = *realm;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_principal_set_realm(krb5_context context,
+ krb5_principal principal,
+ krb5_const_realm realm)
+{
+ if (princ_realm(principal))
+ free(princ_realm(principal));
+
+ princ_realm(principal) = strdup(realm);
+ if (princ_realm(principal) == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ va_list ap;
+ va_start(ap, realm);
+ ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
+ va_end(ap);
+ return ret;
+}
+
+static krb5_error_code
+append_component(krb5_context context, krb5_principal p,
+ const char *comp,
+ size_t comp_len)
+{
+ heim_general_string *tmp;
+ size_t len = princ_num_comp(p);
+
+ tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ princ_comp(p) = tmp;
+ princ_ncomp(p, len) = malloc(comp_len + 1);
+ if (princ_ncomp(p, len) == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy (princ_ncomp(p, len), comp, comp_len);
+ princ_ncomp(p, len)[comp_len] = '\0';
+ princ_num_comp(p)++;
+ return 0;
+}
+
+static void
+va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
+{
+ while(1){
+ const char *s;
+ int len;
+ len = va_arg(ap, int);
+ if(len == 0)
+ break;
+ s = va_arg(ap, const char*);
+ append_component(context, p, s, len);
+ }
+}
+
+static void
+va_princ(krb5_context context, krb5_principal p, va_list ap)
+{
+ while(1){
+ const char *s;
+ s = va_arg(ap, const char*);
+ if(s == NULL)
+ break;
+ append_component(context, p, s, strlen(s));
+ }
+}
+
+
+static krb5_error_code
+build_principal(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ void (*func)(krb5_context, krb5_principal, va_list),
+ va_list ap)
+{
+ krb5_principal p;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ princ_type(p) = KRB5_NT_PRINCIPAL;
+
+ princ_realm(p) = strdup(realm);
+ if(p->realm == NULL){
+ free(p);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*func)(context, p, ap);
+ *principal = p;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_make_principal(krb5_context context,
+ krb5_principal *principal,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ krb5_realm r = NULL;
+ va_list ap;
+ if(realm == NULL) {
+ ret = krb5_get_default_realm(context, &r);
+ if(ret)
+ return ret;
+ realm = r;
+ }
+ va_start(ap, realm);
+ ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
+ va_end(ap);
+ if(r)
+ free(r);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal_va(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ va_list ap)
+{
+ return build_principal(context, principal, rlen, realm, va_princ, ap);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal_va_ext(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ va_list ap)
+{
+ return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal_ext(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ va_list ap;
+ va_start(ap, realm);
+ ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_principal(krb5_context context,
+ krb5_const_principal inprinc,
+ krb5_principal *outprinc)
+{
+ krb5_principal p = malloc(sizeof(*p));
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ if(copy_Principal(inprinc, p)) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *outprinc = p;
+ return 0;
+}
+
+/*
+ * return TRUE iff princ1 == princ2 (without considering the realm)
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_compare_any_realm(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ int i;
+ if(princ_num_comp(princ1) != princ_num_comp(princ2))
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ1); i++){
+ if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+_krb5_principal_compare_PrincipalName(krb5_context context,
+ krb5_const_principal princ1,
+ PrincipalName *princ2)
+{
+ int i;
+ if (princ_num_comp(princ1) != princ2->name_string.len)
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ1); i++){
+ if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ * return TRUE iff princ1 == princ2
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_compare(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ if(!krb5_realm_compare(context, princ1, princ2))
+ return FALSE;
+ return krb5_principal_compare_any_realm(context, princ1, princ2);
+}
+
+/*
+ * return TRUE iff realm(princ1) == realm(princ2)
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_realm_compare(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
+}
+
+/*
+ * return TRUE iff princ matches pattern
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_match(krb5_context context,
+ krb5_const_principal princ,
+ krb5_const_principal pattern)
+{
+ int i;
+ if(princ_num_comp(princ) != princ_num_comp(pattern))
+ return FALSE;
+ if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ); i++){
+ if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static struct v4_name_convert {
+ const char *from;
+ const char *to;
+} default_v4_name_convert[] = {
+ { "ftp", "ftp" },
+ { "hprop", "hprop" },
+ { "pop", "pop" },
+ { "imap", "imap" },
+ { "rcmd", "host" },
+ { "smtp", "smtp" },
+ { NULL, NULL }
+};
+
+/*
+ * return the converted instance name of `name' in `realm'.
+ * look in the configuration file and then in the default set above.
+ * return NULL if no conversion is appropriate.
+ */
+
+static const char*
+get_name_conversion(krb5_context context, const char *realm, const char *name)
+{
+ struct v4_name_convert *q;
+ const char *p;
+
+ p = krb5_config_get_string(context, NULL, "realms", realm,
+ "v4_name_convert", "host", name, NULL);
+ if(p == NULL)
+ p = krb5_config_get_string(context, NULL, "libdefaults",
+ "v4_name_convert", "host", name, NULL);
+ if(p)
+ return p;
+
+ /* XXX should be possible to override default list */
+ p = krb5_config_get_string(context, NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p)
+ return NULL;
+ p = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p)
+ return NULL;
+ for(q = default_v4_name_convert; q->from; q++)
+ if(strcmp(q->from, name) == 0)
+ return q->to;
+ return NULL;
+}
+
+/*
+ * convert the v4 principal `name.instance@realm' to a v5 principal in `princ'.
+ * if `resolve', use DNS.
+ * if `func', use that function for validating the conversion
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_425_conv_principal_ext2(krb5_context context,
+ const char *name,
+ const char *instance,
+ const char *realm,
+ krb5_boolean (*func)(krb5_context,
+ void *, krb5_principal),
+ void *funcctx,
+ krb5_boolean resolve,
+ krb5_principal *princ)
+{
+ const char *p;
+ krb5_error_code ret;
+ krb5_principal pr;
+ char host[MAXHOSTNAMELEN];
+ char local_hostname[MAXHOSTNAMELEN];
+
+ /* do the following: if the name is found in the
+ `v4_name_convert:host' part, is assumed to be a `host' type
+ principal, and the instance is looked up in the
+ `v4_instance_convert' part. if not found there the name is
+ (optionally) looked up as a hostname, and if that doesn't yield
+ anything, the `default_domain' is appended to the instance
+ */
+
+ if(instance == NULL)
+ goto no_host;
+ if(instance[0] == 0){
+ instance = NULL;
+ goto no_host;
+ }
+ p = get_name_conversion(context, realm, name);
+ if(p == NULL)
+ goto no_host;
+ name = p;
+ p = krb5_config_get_string(context, NULL, "realms", realm,
+ "v4_instance_convert", instance, NULL);
+ if(p){
+ instance = p;
+ ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ *princ = NULL;
+ krb5_clear_error_string (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+ }
+ if(resolve){
+ krb5_boolean passed = FALSE;
+ char *inst = NULL;
+#ifdef USE_RESOLVER
+ struct dns_reply *r;
+
+ r = dns_lookup(instance, "aaaa");
+ if (r) {
+ if (r->head && r->head->type == T_AAAA) {
+ inst = strdup(r->head->domain);
+ passed = TRUE;
+ }
+ dns_free_data(r);
+ } else {
+ r = dns_lookup(instance, "a");
+ if (r) {
+ if(r->head && r->head->type == T_A) {
+ inst = strdup(r->head->domain);
+ passed = TRUE;
+ }
+ dns_free_data(r);
+ }
+ }
+#else
+ struct addrinfo hints, *ai;
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ ret = getaddrinfo(instance, NULL, &hints, &ai);
+ if (ret == 0) {
+ const struct addrinfo *a;
+ for (a = ai; a != NULL; a = a->ai_next) {
+ if (a->ai_canonname != NULL) {
+ inst = strdup (a->ai_canonname);
+ passed = TRUE;
+ break;
+ }
+ }
+ freeaddrinfo (ai);
+ }
+#endif
+ if (passed) {
+ if (inst == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ strlwr(inst);
+ ret = krb5_make_principal(context, &pr, realm, name, inst,
+ NULL);
+ free (inst);
+ if(ret == 0) {
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ }
+ }
+ }
+ if(func != NULL) {
+ snprintf(host, sizeof(host), "%s.%s", instance, realm);
+ strlwr(host);
+ ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
+ if((*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ }
+
+ /*
+ * if the instance is the first component of the local hostname,
+ * the converted host should be the long hostname.
+ */
+
+ if (func == NULL &&
+ gethostname (local_hostname, sizeof(local_hostname)) == 0 &&
+ strncmp(instance, local_hostname, strlen(instance)) == 0 &&
+ local_hostname[strlen(instance)] == '.') {
+ strlcpy(host, local_hostname, sizeof(host));
+ goto local_host;
+ }
+
+ {
+ char **domains, **d;
+ domains = krb5_config_get_strings(context, NULL, "realms", realm,
+ "v4_domains", NULL);
+ for(d = domains; d && *d; d++){
+ snprintf(host, sizeof(host), "%s.%s", instance, *d);
+ ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ krb5_config_free_strings(domains);
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ }
+ krb5_config_free_strings(domains);
+ }
+
+
+ p = krb5_config_get_string(context, NULL, "realms", realm,
+ "default_domain", NULL);
+ if(p == NULL){
+ /* this should be an error, just faking a name is not good */
+ krb5_clear_error_string (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+ }
+
+ if (*p == '.')
+ ++p;
+ snprintf(host, sizeof(host), "%s.%s", instance, p);
+local_host:
+ ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ krb5_clear_error_string (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+no_host:
+ p = krb5_config_get_string(context, NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p == NULL)
+ p = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p)
+ name = p;
+
+ ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ krb5_clear_error_string (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+}
+
+static krb5_boolean
+convert_func(krb5_context conxtext, void *funcctx, krb5_principal principal)
+{
+ krb5_boolean (*func)(krb5_context, krb5_principal) = funcctx;
+ return (*func)(conxtext, principal);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_425_conv_principal_ext(krb5_context context,
+ const char *name,
+ const char *instance,
+ const char *realm,
+ krb5_boolean (*func)(krb5_context, krb5_principal),
+ krb5_boolean resolve,
+ krb5_principal *principal)
+{
+ return krb5_425_conv_principal_ext2(context,
+ name,
+ instance,
+ realm,
+ func ? convert_func : NULL,
+ func,
+ resolve,
+ principal);
+}
+
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_425_conv_principal(krb5_context context,
+ const char *name,
+ const char *instance,
+ const char *realm,
+ krb5_principal *princ)
+{
+ krb5_boolean resolve = krb5_config_get_bool(context,
+ NULL,
+ "libdefaults",
+ "v4_instance_resolve",
+ NULL);
+
+ return krb5_425_conv_principal_ext(context, name, instance, realm,
+ NULL, resolve, princ);
+}
+
+
+static int
+check_list(const krb5_config_binding *l, const char *name, const char **out)
+{
+ while(l){
+ if (l->type != krb5_config_string)
+ continue;
+ if(strcmp(name, l->u.string) == 0) {
+ *out = l->name;
+ return 1;
+ }
+ l = l->next;
+ }
+ return 0;
+}
+
+static int
+name_convert(krb5_context context, const char *name, const char *realm,
+ const char **out)
+{
+ const krb5_config_binding *l;
+ l = krb5_config_get_list (context,
+ NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "host",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_SRV_HST;
+ l = krb5_config_get_list (context,
+ NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "host",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_SRV_HST;
+ l = krb5_config_get_list (context,
+ NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "plain",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_UNKNOWN;
+ l = krb5_config_get_list (context,
+ NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "host",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_UNKNOWN;
+
+ /* didn't find it in config file, try built-in list */
+ {
+ struct v4_name_convert *q;
+ for(q = default_v4_name_convert; q->from; q++) {
+ if(strcmp(name, q->to) == 0) {
+ *out = q->from;
+ return KRB5_NT_SRV_HST;
+ }
+ }
+ }
+ return -1;
+}
+
+/*
+ * convert the v5 principal in `principal' into a v4 corresponding one
+ * in `name, instance, realm'
+ * this is limited interface since there's no length given for these
+ * three parameters. They have to be 40 bytes each (ANAME_SZ).
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_524_conv_principal(krb5_context context,
+ const krb5_principal principal,
+ char *name,
+ char *instance,
+ char *realm)
+{
+ const char *n, *i, *r;
+ char tmpinst[40];
+ int type = princ_type(principal);
+ const int aname_sz = 40;
+
+ r = principal->realm;
+
+ switch(principal->name.name_string.len){
+ case 1:
+ n = principal->name.name_string.val[0];
+ i = "";
+ break;
+ case 2:
+ n = principal->name.name_string.val[0];
+ i = principal->name.name_string.val[1];
+ break;
+ default:
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "cannot convert a %d component principal",
+ principal->name.name_string.len);
+ return KRB5_PARSE_MALFORMED;
+ }
+
+ {
+ const char *tmp;
+ int t = name_convert(context, n, r, &tmp);
+ if(t >= 0) {
+ type = t;
+ n = tmp;
+ }
+ }
+
+ if(type == KRB5_NT_SRV_HST){
+ char *p;
+
+ strlcpy (tmpinst, i, sizeof(tmpinst));
+ p = strchr(tmpinst, '.');
+ if(p)
+ *p = 0;
+ i = tmpinst;
+ }
+
+ if (strlcpy (name, n, aname_sz) >= aname_sz) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "too long name component to convert");
+ return KRB5_PARSE_MALFORMED;
+ }
+ if (strlcpy (instance, i, aname_sz) >= aname_sz) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "too long instance component to convert");
+ return KRB5_PARSE_MALFORMED;
+ }
+ if (strlcpy (realm, r, aname_sz) >= aname_sz) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "too long realm component to convert");
+ return KRB5_PARSE_MALFORMED;
+ }
+ return 0;
+}
+
+/*
+ * Create a principal in `ret_princ' for the service `sname' running
+ * on host `hostname'. */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sname_to_principal (krb5_context context,
+ const char *hostname,
+ const char *sname,
+ int32_t type,
+ krb5_principal *ret_princ)
+{
+ krb5_error_code ret;
+ char localhost[MAXHOSTNAMELEN];
+ char **realms, *host = NULL;
+
+ if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
+ krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
+ "unsupported name type %d",
+ (int)type);
+ return KRB5_SNAME_UNSUPP_NAMETYPE;
+ }
+ if(hostname == NULL) {
+ ret = gethostname(localhost, sizeof(localhost) - 1);
+ if (ret != 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "Failed to get local hostname");
+ return ret;
+ }
+ localhost[sizeof(localhost) - 1] = '\0';
+ hostname = localhost;
+ }
+ if(sname == NULL)
+ sname = "host";
+ if(type == KRB5_NT_SRV_HST) {
+ ret = krb5_expand_hostname_realms (context, hostname,
+ &host, &realms);
+ if (ret)
+ return ret;
+ strlwr(host);
+ hostname = host;
+ } else {
+ ret = krb5_get_host_realm(context, hostname, &realms);
+ if(ret)
+ return ret;
+ }
+
+ ret = krb5_make_principal(context, ret_princ, realms[0], sname,
+ hostname, NULL);
+ if(host)
+ free(host);
+ krb5_free_host_realm(context, realms);
+ return ret;
+}
+
+static const struct {
+ const char *type;
+ int32_t value;
+} nametypes[] = {
+ { "UNKNOWN", KRB5_NT_UNKNOWN },
+ { "PRINCIPAL", KRB5_NT_PRINCIPAL },
+ { "SRV_INST", KRB5_NT_SRV_INST },
+ { "SRV_HST", KRB5_NT_SRV_HST },
+ { "SRV_XHST", KRB5_NT_SRV_XHST },
+ { "UID", KRB5_NT_UID },
+ { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
+ { "SMTP_NAME", KRB5_NT_SMTP_NAME },
+ { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
+ { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
+ { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
+ { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
+ { NULL }
+};
+
+krb5_error_code
+krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
+{
+ size_t i;
+
+ for(i = 0; nametypes[i].type; i++) {
+ if (strcasecmp(nametypes[i].type, str) == 0) {
+ *nametype = nametypes[i].value;
+ return 0;
+ }
+ }
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "Failed to find name type %s", str);
+ return KRB5_PARSE_MALFORMED;
+}
diff --git a/source4/heimdal/lib/krb5/prompter_posix.c b/source4/heimdal/lib/krb5/prompter_posix.c
new file mode 100644
index 0000000000..840bb328ca
--- /dev/null
+++ b/source4/heimdal/lib/krb5/prompter_posix.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+int KRB5_LIB_FUNCTION
+krb5_prompter_posix (krb5_context context,
+ void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ int i;
+
+ if (name)
+ fprintf (stderr, "%s\n", name);
+ if (banner)
+ fprintf (stderr, "%s\n", banner);
+ if (name || banner)
+ fflush(stderr);
+ for (i = 0; i < num_prompts; ++i) {
+ if (prompts[i].hidden) {
+ if(UI_UTIL_read_pw_string(prompts[i].reply->data,
+ prompts[i].reply->length,
+ prompts[i].prompt,
+ 0))
+ return 1;
+ } else {
+ char *s = prompts[i].reply->data;
+
+ fputs (prompts[i].prompt, stdout);
+ fflush (stdout);
+ if(fgets(prompts[i].reply->data,
+ prompts[i].reply->length,
+ stdin) == NULL)
+ return 1;
+ s[strcspn(s, "\n")] = '\0';
+ }
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/rd_cred.c b/source4/heimdal/lib/krb5/rd_cred.c
new file mode 100644
index 0000000000..e2807c20d0
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_cred.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+compare_addrs(krb5_context context,
+ krb5_address *a,
+ krb5_address *b,
+ const char *message)
+{
+ char a_str[64], b_str[64];
+ size_t len;
+
+ if(krb5_address_compare (context, a, b))
+ return 0;
+
+ krb5_print_address (a, a_str, sizeof(a_str), &len);
+ krb5_print_address (b, b_str, sizeof(b_str), &len);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_BADADDR,
+ "%s: %s != %s", message, b_str, a_str);
+ return KRB5KRB_AP_ERR_BADADDR;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_cred(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *in_data,
+ krb5_creds ***ret_creds,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ size_t len;
+ KRB_CRED cred;
+ EncKrbCredPart enc_krb_cred_part;
+ krb5_data enc_krb_cred_part_data;
+ krb5_crypto crypto;
+ int i;
+
+ memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
+ outdata == NULL)
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+
+ *ret_creds = NULL;
+
+ ret = decode_KRB_CRED(in_data->data, in_data->length,
+ &cred, &len);
+ if(ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ if (cred.pvno != 5) {
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+
+ if (cred.msg_type != krb_cred) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+
+ if (cred.enc_part.etype == ETYPE_NULL) {
+ /* DK: MIT GSS-API Compatibility */
+ enc_krb_cred_part_data.length = cred.enc_part.cipher.length;
+ enc_krb_cred_part_data.data = cred.enc_part.cipher.data;
+ } else {
+ /* Try both subkey and session key.
+ *
+ * RFC4120 claims we should use the session key, but Heimdal
+ * before 0.8 used the remote subkey if it was send in the
+ * auth_context.
+ */
+
+ if (auth_context->remote_subkey) {
+ ret = krb5_crypto_init(context, auth_context->remote_subkey,
+ 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ &cred.enc_part,
+ &enc_krb_cred_part_data);
+
+ krb5_crypto_destroy(context, crypto);
+ }
+
+ /*
+ * If there was not subkey, or we failed using subkey,
+ * retry using the session key
+ */
+ if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ {
+
+ ret = krb5_crypto_init(context, auth_context->keyblock,
+ 0, &crypto);
+
+ if (ret)
+ goto out;
+
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ &cred.enc_part,
+ &enc_krb_cred_part_data);
+
+ krb5_crypto_destroy(context, crypto);
+ }
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decode_EncKrbCredPart (context,
+ enc_krb_cred_part_data.data,
+ enc_krb_cred_part_data.length,
+ &enc_krb_cred_part,
+ &len);
+ if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data)
+ krb5_data_free(&enc_krb_cred_part_data);
+ if (ret)
+ goto out;
+
+ /* check sender address */
+
+ if (enc_krb_cred_part.s_address
+ && auth_context->remote_address
+ && auth_context->remote_port) {
+ krb5_address *a;
+
+ ret = krb5_make_addrport (context, &a,
+ auth_context->remote_address,
+ auth_context->remote_port);
+ if (ret)
+ goto out;
+
+
+ ret = compare_addrs(context, a, enc_krb_cred_part.s_address,
+ "sender address is wrong in received creds");
+ krb5_free_address(context, a);
+ free(a);
+ if(ret)
+ goto out;
+ }
+
+ /* check receiver address */
+
+ if (enc_krb_cred_part.r_address
+ && auth_context->local_address) {
+ if(auth_context->local_port &&
+ enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) {
+ krb5_address *a;
+ ret = krb5_make_addrport (context, &a,
+ auth_context->local_address,
+ auth_context->local_port);
+ if (ret)
+ goto out;
+
+ ret = compare_addrs(context, a, enc_krb_cred_part.r_address,
+ "receiver address is wrong in received creds");
+ krb5_free_address(context, a);
+ free(a);
+ if(ret)
+ goto out;
+ } else {
+ ret = compare_addrs(context, auth_context->local_address,
+ enc_krb_cred_part.r_address,
+ "receiver address is wrong in received creds");
+ if(ret)
+ goto out;
+ }
+ }
+
+ /* check timestamp */
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+
+ if (enc_krb_cred_part.timestamp == NULL ||
+ enc_krb_cred_part.usec == NULL ||
+ abs(*enc_krb_cred_part.timestamp - sec)
+ > context->max_skew) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_SKEW;
+ goto out;
+ }
+ }
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
+ /* if these fields are not present in the cred-part, silently
+ return zero */
+ memset(outdata, 0, sizeof(*outdata));
+ if(enc_krb_cred_part.timestamp)
+ outdata->timestamp = *enc_krb_cred_part.timestamp;
+ if(enc_krb_cred_part.usec)
+ outdata->usec = *enc_krb_cred_part.usec;
+ if(enc_krb_cred_part.nonce)
+ outdata->seq = *enc_krb_cred_part.nonce;
+ }
+
+ /* Convert to NULL terminated list of creds */
+
+ *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1,
+ sizeof(**ret_creds));
+
+ if (*ret_creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) {
+ KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i];
+ krb5_creds *creds;
+
+ creds = calloc(1, sizeof(*creds));
+ if(creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
+ &cred.tickets.val[i], &len, ret);
+ if (ret) {
+ free(creds);
+ goto out;
+ }
+ if(creds->ticket.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ copy_EncryptionKey (&kci->key, &creds->session);
+ if (kci->prealm && kci->pname)
+ _krb5_principalname2krb5_principal (context,
+ &creds->client,
+ *kci->pname,
+ *kci->prealm);
+ if (kci->flags)
+ creds->flags.b = *kci->flags;
+ if (kci->authtime)
+ creds->times.authtime = *kci->authtime;
+ if (kci->starttime)
+ creds->times.starttime = *kci->starttime;
+ if (kci->endtime)
+ creds->times.endtime = *kci->endtime;
+ if (kci->renew_till)
+ creds->times.renew_till = *kci->renew_till;
+ if (kci->srealm && kci->sname)
+ _krb5_principalname2krb5_principal (context,
+ &creds->server,
+ *kci->sname,
+ *kci->srealm);
+ if (kci->caddr)
+ krb5_copy_addresses (context,
+ kci->caddr,
+ &creds->addresses);
+
+ (*ret_creds)[i] = creds;
+
+ }
+ (*ret_creds)[i] = NULL;
+
+ free_KRB_CRED (&cred);
+ free_EncKrbCredPart(&enc_krb_cred_part);
+
+ return 0;
+
+ out:
+ free_EncKrbCredPart(&enc_krb_cred_part);
+ free_KRB_CRED (&cred);
+ if(*ret_creds) {
+ for(i = 0; (*ret_creds)[i]; i++)
+ krb5_free_creds(context, (*ret_creds)[i]);
+ free(*ret_creds);
+ *ret_creds = NULL;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_cred2 (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_ccache ccache,
+ krb5_data *in_data)
+{
+ krb5_error_code ret;
+ krb5_creds **creds;
+ int i;
+
+ ret = krb5_rd_cred(context, auth_context, in_data, &creds, NULL);
+ if(ret)
+ return ret;
+
+ /* Store the creds in the ccache */
+
+ for(i = 0; creds && creds[i]; i++) {
+ krb5_cc_store_cred(context, ccache, creds[i]);
+ krb5_free_creds(context, creds[i]);
+ }
+ free(creds);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/rd_error.c b/source4/heimdal/lib/krb5/rd_error.c
new file mode 100644
index 0000000000..9f23d8df29
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_error.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_error(krb5_context context,
+ const krb5_data *msg,
+ KRB_ERROR *result)
+{
+
+ size_t len;
+ krb5_error_code ret;
+
+ ret = decode_KRB_ERROR(msg->data, msg->length, result, &len);
+ if(ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+ result->error_code += KRB5KDC_ERR_NONE;
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_error_contents (krb5_context context,
+ krb5_error *error)
+{
+ free_KRB_ERROR(error);
+ memset(error, 0, sizeof(*error));
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_error (krb5_context context,
+ krb5_error *error)
+{
+ krb5_free_error_contents (context, error);
+ free (error);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_error_from_rd_error(krb5_context context,
+ const krb5_error *error,
+ const krb5_creds *creds)
+{
+ krb5_error_code ret;
+
+ ret = error->error_code;
+ if (error->e_text != NULL) {
+ krb5_set_error_message(context, ret, "%s", *error->e_text);
+ } else {
+ char clientname[256], servername[256];
+
+ if (creds != NULL) {
+ krb5_unparse_name_fixed(context, creds->client,
+ clientname, sizeof(clientname));
+ krb5_unparse_name_fixed(context, creds->server,
+ servername, sizeof(servername));
+ }
+
+ switch (ret) {
+ case KRB5KDC_ERR_NAME_EXP :
+ krb5_set_error_message(context, ret, "Client %s%s%s expired",
+ creds ? "(" : "",
+ creds ? clientname : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_SERVICE_EXP :
+ krb5_set_error_message(context, ret, "Server %s%s%s expired",
+ creds ? "(" : "",
+ creds ? servername : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN :
+ krb5_set_error_message(context, ret, "Client %s%s%s unknown",
+ creds ? "(" : "",
+ creds ? clientname : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN :
+ krb5_set_error_message(context, ret, "Server %s%s%s unknown",
+ creds ? "(" : "",
+ creds ? servername : "",
+ creds ? ")" : "");
+ break;
+ default :
+ krb5_clear_error_string(context);
+ break;
+ }
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/rd_priv.c b/source4/heimdal/lib/krb5/rd_priv.c
new file mode 100644
index 0000000000..da8f44febb
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_priv.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_priv(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *inbuf,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_PRIV priv;
+ EncKrbPrivPart part;
+ size_t len;
+ krb5_data plain;
+ krb5_keyblock *key;
+ krb5_crypto crypto;
+
+ krb5_data_zero(outbuf);
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)))
+ {
+ if (outdata == NULL) {
+ krb5_clear_error_string (context);
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+ }
+ /* if these fields are not present in the priv-part, silently
+ return zero */
+ memset(outdata, 0, sizeof(*outdata));
+ }
+
+ memset(&priv, 0, sizeof(priv));
+ ret = decode_KRB_PRIV (inbuf->data, inbuf->length, &priv, &len);
+ if (ret) {
+ krb5_clear_error_string (context);
+ goto failure;
+ }
+ if (priv.pvno != 5) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ goto failure;
+ }
+ if (priv.msg_type != krb_priv) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ goto failure;
+ }
+
+ if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else
+ key = auth_context->keyblock;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto failure;
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_PRIV,
+ &priv.enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto failure;
+
+ ret = decode_EncKrbPrivPart (plain.data, plain.length, &part, &len);
+ krb5_data_free (&plain);
+ if (ret) {
+ krb5_clear_error_string (context);
+ goto failure;
+ }
+
+ /* check sender address */
+
+ if (part.s_address
+ && auth_context->remote_address
+ && !krb5_address_compare (context,
+ auth_context->remote_address,
+ part.s_address)) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ goto failure_part;
+ }
+
+ /* check receiver address */
+
+ if (part.r_address
+ && auth_context->local_address
+ && !krb5_address_compare (context,
+ auth_context->local_address,
+ part.r_address)) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ goto failure_part;
+ }
+
+ /* check timestamp */
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+ if (part.timestamp == NULL ||
+ part.usec == NULL ||
+ abs(*part.timestamp - sec) > context->max_skew) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_SKEW;
+ goto failure_part;
+ }
+ }
+
+ /* XXX - check replay cache */
+
+ /* check sequence number. since MIT krb5 cannot generate a sequence
+ number of zero but instead generates no sequence number, we accept that
+ */
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if ((part.seq_number == NULL
+ && auth_context->remote_seqnumber != 0)
+ || (part.seq_number != NULL
+ && *part.seq_number != auth_context->remote_seqnumber)) {
+ krb5_clear_error_string (context);
+ ret = KRB5KRB_AP_ERR_BADORDER;
+ goto failure_part;
+ }
+ auth_context->remote_seqnumber++;
+ }
+
+ ret = krb5_data_copy (outbuf, part.user_data.data, part.user_data.length);
+ if (ret)
+ goto failure_part;
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
+ if(part.timestamp)
+ outdata->timestamp = *part.timestamp;
+ if(part.usec)
+ outdata->usec = *part.usec;
+ if(part.seq_number)
+ outdata->seq = *part.seq_number;
+ }
+
+ failure_part:
+ free_EncKrbPrivPart (&part);
+
+ failure:
+ free_KRB_PRIV (&priv);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/rd_rep.c b/source4/heimdal/lib/krb5/rd_rep.c
new file mode 100644
index 0000000000..846de26c60
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_rep.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_rep(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *inbuf,
+ krb5_ap_rep_enc_part **repl)
+{
+ krb5_error_code ret;
+ AP_REP ap_rep;
+ size_t len;
+ krb5_data data;
+ krb5_crypto crypto;
+
+ krb5_data_zero (&data);
+ ret = 0;
+
+ ret = decode_AP_REP(inbuf->data, inbuf->length, &ap_rep, &len);
+ if (ret)
+ return ret;
+ if (ap_rep.pvno != 5) {
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+ if (ap_rep.msg_type != krb_ap_rep) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
+ if (ret)
+ goto out;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_AP_REQ_ENC_PART,
+ &ap_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto out;
+
+ *repl = malloc(sizeof(**repl));
+ if (*repl == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = krb5_decode_EncAPRepPart(context,
+ data.data,
+ data.length,
+ *repl,
+ &len);
+ if (ret)
+ return ret;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ if ((*repl)->ctime != auth_context->authenticator->ctime ||
+ (*repl)->cusec != auth_context->authenticator->cusec)
+ {
+ krb5_free_ap_rep_enc_part(context, *repl);
+ *repl = NULL;
+ ret = KRB5KRB_AP_ERR_MUT_FAIL;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+ }
+ if ((*repl)->seq_number)
+ krb5_auth_con_setremoteseqnumber(context, auth_context,
+ *((*repl)->seq_number));
+ if ((*repl)->subkey)
+ krb5_auth_con_setremotesubkey(context, auth_context, (*repl)->subkey);
+
+ out:
+ krb5_data_free (&data);
+ free_AP_REP (&ap_rep);
+ return ret;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_ap_rep_enc_part (krb5_context context,
+ krb5_ap_rep_enc_part *val)
+{
+ if (val) {
+ free_EncAPRepPart (val);
+ free (val);
+ }
+}
diff --git a/source4/heimdal/lib/krb5/rd_req.c b/source4/heimdal/lib/krb5/rd_req.c
new file mode 100644
index 0000000000..ef91f9fdd6
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_req.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+decrypt_tkt_enc_part (krb5_context context,
+ krb5_keyblock *key,
+ EncryptedData *enc_part,
+ EncTicketPart *decr_part)
+{
+ krb5_error_code ret;
+ krb5_data plain;
+ size_t len;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TICKET,
+ enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_EncTicketPart(context, plain.data, plain.length,
+ decr_part, &len);
+ krb5_data_free (&plain);
+ return ret;
+}
+
+static krb5_error_code
+decrypt_authenticator (krb5_context context,
+ EncryptionKey *key,
+ EncryptedData *enc_part,
+ Authenticator *authenticator,
+ krb5_key_usage usage)
+{
+ krb5_error_code ret;
+ krb5_data plain;
+ size_t len;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage /* KRB5_KU_AP_REQ_AUTH */,
+ enc_part,
+ &plain);
+ /* for backwards compatibility, also try the old usage */
+ if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_AP_REQ_AUTH,
+ enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_Authenticator(context, plain.data, plain.length,
+ authenticator, &len);
+ krb5_data_free (&plain);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_ap_req(krb5_context context,
+ const krb5_data *inbuf,
+ krb5_ap_req *ap_req)
+{
+ krb5_error_code ret;
+ size_t len;
+ ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
+ if (ret)
+ return ret;
+ if (ap_req->pvno != 5){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_string (context);
+ return KRB5KRB_AP_ERR_BADVERSION;
+ }
+ if (ap_req->msg_type != krb_ap_req){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_string (context);
+ return KRB5KRB_AP_ERR_MSG_TYPE;
+ }
+ if (ap_req->ticket.tkt_vno != 5){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_string (context);
+ return KRB5KRB_AP_ERR_BADVERSION;
+ }
+ return 0;
+}
+
+static krb5_error_code
+check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
+{
+ char **realms;
+ unsigned int num_realms;
+ krb5_error_code ret;
+
+ /*
+ * Windows 2000 and 2003 uses this inside their TGT so it's normaly
+ * not seen by others, however, samba4 joined with a Windows AD as
+ * a Domain Controller gets exposed to this.
+ */
+ if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
+ return 0;
+
+ if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+
+ if(enc->transited.contents.length == 0)
+ return 0;
+
+ ret = krb5_domain_x500_decode(context, enc->transited.contents,
+ &realms, &num_realms,
+ enc->crealm,
+ ticket->realm);
+ if(ret)
+ return ret;
+ ret = krb5_check_transited(context, enc->crealm,
+ ticket->realm,
+ realms, num_realms, NULL);
+ free(realms);
+ return ret;
+}
+
+static krb5_error_code
+find_etypelist(krb5_context context,
+ krb5_auth_context auth_context,
+ EtypeList *etypes)
+{
+ krb5_error_code ret;
+ krb5_authdata *ad;
+ krb5_authdata adIfRelevant;
+ unsigned i;
+
+ adIfRelevant.len = 0;
+
+ etypes->len = 0;
+ etypes->val = NULL;
+
+ ad = auth_context->authenticator->authorization_data;
+ if (ad == NULL)
+ return 0;
+
+ for (i = 0; i < ad->len; i++) {
+ if (ad->val[i].ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
+ ret = decode_AD_IF_RELEVANT(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &adIfRelevant,
+ NULL);
+ if (ret)
+ return ret;
+
+ if (adIfRelevant.len == 1 &&
+ adIfRelevant.val[0].ad_type ==
+ KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION) {
+ break;
+ }
+ free_AD_IF_RELEVANT(&adIfRelevant);
+ adIfRelevant.len = 0;
+ }
+ }
+
+ if (adIfRelevant.len == 0)
+ return 0;
+
+ ret = decode_EtypeList(adIfRelevant.val[0].ad_data.data,
+ adIfRelevant.val[0].ad_data.length,
+ etypes,
+ NULL);
+ if (ret)
+ krb5_clear_error_string(context);
+
+ free_AD_IF_RELEVANT(&adIfRelevant);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_ticket(krb5_context context,
+ Ticket *ticket,
+ krb5_keyblock *key,
+ EncTicketPart *out,
+ krb5_flags flags)
+{
+ EncTicketPart t;
+ krb5_error_code ret;
+ ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
+ if (ret)
+ return ret;
+
+ {
+ krb5_timestamp now;
+ time_t start = t.authtime;
+
+ krb5_timeofday (context, &now);
+ if(t.starttime)
+ start = *t.starttime;
+ if(start - now > context->max_skew
+ || (t.flags.invalid
+ && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
+ free_EncTicketPart(&t);
+ krb5_clear_error_string (context);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ if(now - t.endtime > context->max_skew) {
+ free_EncTicketPart(&t);
+ krb5_clear_error_string (context);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+
+ if(!t.flags.transited_policy_checked) {
+ ret = check_transited(context, ticket, &t);
+ if(ret) {
+ free_EncTicketPart(&t);
+ return ret;
+ }
+ }
+ }
+
+ if(out)
+ *out = t;
+ else
+ free_EncTicketPart(&t);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_authenticator_checksum(krb5_context context,
+ krb5_auth_context ac,
+ void *data,
+ size_t len)
+{
+ krb5_error_code ret;
+ krb5_keyblock *key;
+ krb5_authenticator authenticator;
+ krb5_crypto crypto;
+
+ ret = krb5_auth_con_getauthenticator (context,
+ ac,
+ &authenticator);
+ if(ret)
+ return ret;
+ if(authenticator->cksum == NULL) {
+ krb5_free_authenticator(context, &authenticator);
+ return -17;
+ }
+ ret = krb5_auth_con_getkey(context, ac, &key);
+ if(ret) {
+ krb5_free_authenticator(context, &authenticator);
+ return ret;
+ }
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if(ret)
+ goto out;
+ ret = krb5_verify_checksum (context,
+ crypto,
+ KRB5_KU_AP_REQ_AUTH_CKSUM,
+ data,
+ len,
+ authenticator->cksum);
+ krb5_crypto_destroy(context, crypto);
+out:
+ krb5_free_authenticator(context, &authenticator);
+ krb5_free_keyblock(context, key);
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_ap_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags flags,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ return krb5_verify_ap_req2 (context,
+ auth_context,
+ ap_req,
+ server,
+ keyblock,
+ flags,
+ ap_req_options,
+ ticket,
+ KRB5_KU_AP_REQ_AUTH);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_ap_req2(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags flags,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket,
+ krb5_key_usage usage)
+{
+ krb5_ticket *t;
+ krb5_auth_context ac;
+ krb5_error_code ret;
+ EtypeList etypes;
+
+ if (ticket)
+ *ticket = NULL;
+
+ if (auth_context && *auth_context) {
+ ac = *auth_context;
+ } else {
+ ret = krb5_auth_con_init (context, &ac);
+ if (ret)
+ return ret;
+ }
+
+ t = calloc(1, sizeof(*t));
+ if (t == NULL) {
+ ret = ENOMEM;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+
+ if (ap_req->ap_options.use_session_key && ac->keyblock){
+ ret = krb5_decrypt_ticket(context, &ap_req->ticket,
+ ac->keyblock,
+ &t->ticket,
+ flags);
+ krb5_free_keyblock(context, ac->keyblock);
+ ac->keyblock = NULL;
+ }else
+ ret = krb5_decrypt_ticket(context, &ap_req->ticket,
+ keyblock,
+ &t->ticket,
+ flags);
+
+ if(ret)
+ goto out;
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &t->server,
+ ap_req->ticket.sname,
+ ap_req->ticket.realm);
+ if (ret) goto out;
+ ret = _krb5_principalname2krb5_principal(context,
+ &t->client,
+ t->ticket.cname,
+ t->ticket.crealm);
+ if (ret) goto out;
+
+ ret = decrypt_authenticator (context,
+ &t->ticket.key,
+ &ap_req->authenticator,
+ ac->authenticator,
+ usage);
+ if (ret)
+ goto out;
+
+ {
+ krb5_principal p1, p2;
+ krb5_boolean res;
+
+ _krb5_principalname2krb5_principal(context,
+ &p1,
+ ac->authenticator->cname,
+ ac->authenticator->crealm);
+ _krb5_principalname2krb5_principal(context,
+ &p2,
+ t->ticket.cname,
+ t->ticket.crealm);
+ res = krb5_principal_compare (context, p1, p2);
+ krb5_free_principal (context, p1);
+ krb5_free_principal (context, p2);
+ if (!res) {
+ ret = KRB5KRB_AP_ERR_BADMATCH;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+ }
+
+ /* check addresses */
+
+ if (t->ticket.caddr
+ && ac->remote_address
+ && !krb5_address_search (context,
+ ac->remote_address,
+ t->ticket.caddr)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+
+ /* check timestamp in authenticator */
+ {
+ krb5_timestamp now;
+
+ krb5_timeofday (context, &now);
+
+ if (abs(ac->authenticator->ctime - now) > context->max_skew) {
+ ret = KRB5KRB_AP_ERR_SKEW;
+ krb5_clear_error_string (context);
+ goto out;
+ }
+ }
+
+ if (ac->authenticator->seq_number)
+ krb5_auth_con_setremoteseqnumber(context, ac,
+ *ac->authenticator->seq_number);
+
+ /* XXX - Xor sequence numbers */
+
+ if (ac->authenticator->subkey) {
+ ret = krb5_auth_con_setremotesubkey(context, ac,
+ ac->authenticator->subkey);
+ if (ret)
+ goto out;
+ }
+
+ ret = find_etypelist(context, ac, &etypes);
+ if (ret)
+ goto out;
+
+ ac->keytype = ETYPE_NULL;
+
+ if (etypes.val) {
+ int i;
+
+ for (i = 0; i < etypes.len; i++) {
+ if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
+ ac->keytype = etypes.val[i];
+ break;
+ }
+ }
+ }
+
+ /* save key */
+ ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
+ if (ret) goto out;
+
+ if (ap_req_options) {
+ *ap_req_options = 0;
+ if (ac->keytype != ETYPE_NULL)
+ *ap_req_options |= AP_OPTS_USE_SUBKEY;
+ if (ap_req->ap_options.use_session_key)
+ *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
+ if (ap_req->ap_options.mutual_required)
+ *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
+ }
+
+ if(ticket)
+ *ticket = t;
+ else
+ krb5_free_ticket (context, t);
+ if (auth_context) {
+ if (*auth_context == NULL)
+ *auth_context = ac;
+ } else
+ krb5_auth_con_free (context, ac);
+ free_EtypeList(&etypes);
+ return 0;
+ out:
+ if (t)
+ krb5_free_ticket (context, t);
+ if (auth_context == NULL || *auth_context == NULL)
+ krb5_auth_con_free (context, ac);
+ return ret;
+}
+
+/*
+ *
+ */
+
+struct krb5_rd_req_in_ctx_data {
+ krb5_keytab keytab;
+ krb5_keyblock *keyblock;
+ krb5_boolean check_pac;
+};
+
+struct krb5_rd_req_out_ctx_data {
+ krb5_keyblock *keyblock;
+ krb5_flags ap_req_options;
+ krb5_ticket *ticket;
+};
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL) {
+ krb5_set_error_message(context, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_set_keytab(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_keytab keytab)
+{
+ in->keytab = keytab; /* XXX should make copy */
+ return 0;
+}
+
+/**
+ * Set if krb5_rq_red() is going to check the Windows PAC or not
+ *
+ * @param context Keberos 5 context.
+ * @param in krb5_rd_req_in_ctx to check the option on.
+ * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_set_pac_check(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_boolean flag)
+{
+ in->check_pac = flag;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_set_keyblock(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_keyblock *keyblock)
+{
+ in->keyblock = keyblock; /* XXX should make copy */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_out_get_ap_req_options(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_flags *ap_req_options)
+{
+ *ap_req_options = out->ap_req_options;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_out_get_ticket(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_ticket **ticket)
+{
+ return krb5_copy_ticket(context, out->ticket, ticket);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_out_get_keyblock(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_keyblock **keyblock)
+{
+ return krb5_copy_keyblock(context, out->keyblock, keyblock);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
+{
+ free(ctx);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_rd_req_out_ctx_alloc(krb5_context context, krb5_rd_req_out_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL) {
+ krb5_set_error_message(context, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
+{
+ krb5_free_keyblock(context, ctx->keyblock);
+ free(ctx);
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_keytab keytab,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ krb5_error_code ret;
+ krb5_rd_req_in_ctx in;
+ krb5_rd_req_out_ctx out;
+
+ ret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_req_in_set_keytab(context, in, keytab);
+ if (ret) {
+ krb5_rd_req_in_ctx_free(context, in);
+ return ret;
+ }
+
+ ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (ret)
+ return ret;
+
+ if (ap_req_options)
+ *ap_req_options = out->ap_req_options;
+ if (ticket) {
+ ret = krb5_copy_ticket(context, out->ticket, ticket);
+ if (ret)
+ goto out;
+ }
+
+out:
+ krb5_rd_req_out_ctx_free(context, out);
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_with_keyblock(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ krb5_error_code ret;
+ krb5_rd_req_in_ctx in;
+ krb5_rd_req_out_ctx out;
+
+ ret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_req_in_set_keyblock(context, in, keyblock);
+ if (ret) {
+ krb5_rd_req_in_ctx_free(context, in);
+ return ret;
+ }
+
+ ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (ret)
+ return ret;
+
+ if (ap_req_options)
+ *ap_req_options = out->ap_req_options;
+ if (ticket) {
+ ret = krb5_copy_ticket(context, out->ticket, ticket);
+ if (ret)
+ goto out;
+ }
+
+out:
+ krb5_rd_req_out_ctx_free(context, out);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+get_key_from_keytab(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keytab keytab,
+ krb5_keyblock **out_key)
+{
+ krb5_keytab_entry entry;
+ krb5_error_code ret;
+ int kvno;
+ krb5_keytab real_keytab;
+
+ if(keytab == NULL)
+ krb5_kt_default(context, &real_keytab);
+ else
+ real_keytab = keytab;
+
+ if (ap_req->ticket.enc_part.kvno)
+ kvno = *ap_req->ticket.enc_part.kvno;
+ else
+ kvno = 0;
+
+ ret = krb5_kt_get_entry (context,
+ real_keytab,
+ server,
+ kvno,
+ ap_req->ticket.enc_part.etype,
+ &entry);
+ if(ret)
+ goto out;
+ ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
+ krb5_kt_free_entry (context, &entry);
+out:
+ if(keytab == NULL)
+ krb5_kt_close(context, real_keytab);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_ctx(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_rd_req_in_ctx inctx,
+ krb5_rd_req_out_ctx *outctx)
+{
+ krb5_error_code ret;
+ krb5_ap_req ap_req;
+ krb5_principal service = NULL;
+ krb5_rd_req_out_ctx o = NULL;
+
+ ret = _krb5_rd_req_out_ctx_alloc(context, &o);
+ if (ret)
+ goto out;
+
+ if (*auth_context == NULL) {
+ ret = krb5_auth_con_init(context, auth_context);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decode_ap_req(context, inbuf, &ap_req);
+ if(ret)
+ goto out;
+
+ if(server == NULL){
+ ret = _krb5_principalname2krb5_principal(context,
+ &service,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+ if (ret)
+ goto out;
+ server = service;
+ }
+ if (ap_req.ap_options.use_session_key &&
+ (*auth_context)->keyblock == NULL) {
+ ret = KRB5KRB_AP_ERR_NOKEY;
+ krb5_set_error_message(context, ret, "krb5_rd_req: user to user auth "
+ "without session key given");
+ goto out;
+ }
+
+ if((*auth_context)->keyblock){
+ ret = krb5_copy_keyblock(context,
+ (*auth_context)->keyblock,
+ &o->keyblock);
+ if (ret)
+ goto out;
+ } else if(inctx->keyblock){
+ ret = krb5_copy_keyblock(context,
+ inctx->keyblock,
+ &o->keyblock);
+ if (ret)
+ goto out;
+ } else {
+ krb5_keytab keytab = NULL;
+
+ if (inctx && inctx->keytab)
+ keytab = inctx->keytab;
+
+ ret = get_key_from_keytab(context,
+ auth_context,
+ &ap_req,
+ server,
+ keytab,
+ &o->keyblock);
+ if(ret)
+ goto out;
+ }
+
+ ret = krb5_verify_ap_req2(context,
+ auth_context,
+ &ap_req,
+ server,
+ o->keyblock,
+ 0,
+ &o->ap_req_options,
+ &o->ticket,
+ KRB5_KU_AP_REQ_AUTH);
+
+ if (ret)
+ goto out;
+
+ /* If there is a PAC, verify its server signature */
+ if (inctx->check_pac) {
+ krb5_pac pac;
+ krb5_data data;
+
+ ret = krb5_ticket_get_authorization_data_type(context,
+ o->ticket,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &data);
+ if (ret == 0) {
+ ret = krb5_pac_parse(context, data.data, data.length, &pac);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+
+ ret = krb5_pac_verify(context,
+ pac,
+ o->ticket->ticket.authtime,
+ o->ticket->client,
+ o->keyblock,
+ NULL);
+ krb5_pac_free(context, pac);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+ }
+out:
+ if (ret || outctx == NULL) {
+ krb5_rd_req_out_ctx_free(context, o);
+ } else
+ *outctx = o;
+
+ free_AP_REQ(&ap_req);
+ if(service)
+ krb5_free_principal(context, service);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/replay.c b/source4/heimdal/lib/krb5/replay.c
new file mode 100644
index 0000000000..cd717f27ac
--- /dev/null
+++ b/source4/heimdal/lib/krb5/replay.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <vis.h>
+
+RCSID("$Id$");
+
+struct krb5_rcache_data {
+ char *name;
+};
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_resolve(krb5_context context,
+ krb5_rcache id,
+ const char *name)
+{
+ id->name = strdup(name);
+ if(id->name == NULL) {
+ krb5_set_error_message(context, KRB5_RC_MALLOC, "malloc: out of memory");
+ return KRB5_RC_MALLOC;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_resolve_type(krb5_context context,
+ krb5_rcache *id,
+ const char *type)
+{
+ *id = NULL;
+ if(strcmp(type, "FILE")) {
+ krb5_set_error_message (context, KRB5_RC_TYPE_NOTFOUND,
+ "replay cache type %s not supported",
+ type);
+ return KRB5_RC_TYPE_NOTFOUND;
+ }
+ *id = calloc(1, sizeof(**id));
+ if(*id == NULL) {
+ krb5_set_error_message(context, KRB5_RC_MALLOC, "malloc: out of memory");
+ return KRB5_RC_MALLOC;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_resolve_full(krb5_context context,
+ krb5_rcache *id,
+ const char *string_name)
+{
+ krb5_error_code ret;
+
+ *id = NULL;
+
+ if(strncmp(string_name, "FILE:", 5)) {
+ krb5_set_error_message(context, KRB5_RC_TYPE_NOTFOUND,
+ "replay cache type %s not supported",
+ string_name);
+ return KRB5_RC_TYPE_NOTFOUND;
+ }
+ ret = krb5_rc_resolve_type(context, id, "FILE");
+ if(ret)
+ return ret;
+ ret = krb5_rc_resolve(context, *id, string_name + 5);
+ if (ret) {
+ krb5_rc_close(context, *id);
+ *id = NULL;
+ }
+ return ret;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_default_name(krb5_context context)
+{
+ return "FILE:/var/run/default_rcache";
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_default_type(krb5_context context)
+{
+ return "FILE";
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_default(krb5_context context,
+ krb5_rcache *id)
+{
+ return krb5_rc_resolve_full(context, id, krb5_rc_default_name(context));
+}
+
+struct rc_entry{
+ time_t stamp;
+ unsigned char data[16];
+};
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_initialize(krb5_context context,
+ krb5_rcache id,
+ krb5_deltat auth_lifespan)
+{
+ FILE *f = fopen(id->name, "w");
+ struct rc_entry tmp;
+ int ret;
+
+ if(f == NULL) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", id->name,
+ strerror(ret));
+ return ret;
+ }
+ tmp.stamp = auth_lifespan;
+ fwrite(&tmp, 1, sizeof(tmp), f);
+ fclose(f);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_recover(krb5_context context,
+ krb5_rcache id)
+{
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_destroy(krb5_context context,
+ krb5_rcache id)
+{
+ int ret;
+
+ if(remove(id->name) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "remove(%s): %s", id->name,
+ strerror(ret));
+ return ret;
+ }
+ return krb5_rc_close(context, id);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_close(krb5_context context,
+ krb5_rcache id)
+{
+ free(id->name);
+ free(id);
+ return 0;
+}
+
+static void
+checksum_authenticator(Authenticator *auth, void *data)
+{
+ MD5_CTX md5;
+ int i;
+
+ MD5_Init (&md5);
+ MD5_Update (&md5, auth->crealm, strlen(auth->crealm));
+ for(i = 0; i < auth->cname.name_string.len; i++)
+ MD5_Update(&md5, auth->cname.name_string.val[i],
+ strlen(auth->cname.name_string.val[i]));
+ MD5_Update (&md5, &auth->ctime, sizeof(auth->ctime));
+ MD5_Update (&md5, &auth->cusec, sizeof(auth->cusec));
+ MD5_Final (data, &md5);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_store(krb5_context context,
+ krb5_rcache id,
+ krb5_donot_replay *rep)
+{
+ struct rc_entry ent, tmp;
+ time_t t;
+ FILE *f;
+ int ret;
+
+ ent.stamp = time(NULL);
+ checksum_authenticator(rep, ent.data);
+ f = fopen(id->name, "r");
+ if(f == NULL) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", id->name,
+ strerror(ret));
+ return ret;
+ }
+ rk_cloexec_file(f);
+ fread(&tmp, sizeof(ent), 1, f);
+ t = ent.stamp - tmp.stamp;
+ while(fread(&tmp, sizeof(ent), 1, f)){
+ if(tmp.stamp < t)
+ continue;
+ if(memcmp(tmp.data, ent.data, sizeof(ent.data)) == 0){
+ fclose(f);
+ krb5_clear_error_string (context);
+ return KRB5_RC_REPLAY;
+ }
+ }
+ if(ferror(f)){
+ ret = errno;
+ fclose(f);
+ krb5_set_error_message(context, ret, "%s: %s",
+ id->name, strerror(ret));
+ return ret;
+ }
+ fclose(f);
+ f = fopen(id->name, "a");
+ if(f == NULL) {
+ krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
+ "open(%s): %s", id->name,
+ strerror(errno));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ fwrite(&ent, 1, sizeof(ent), f);
+ fclose(f);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_expunge(krb5_context context,
+ krb5_rcache id)
+{
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_get_lifespan(krb5_context context,
+ krb5_rcache id,
+ krb5_deltat *auth_lifespan)
+{
+ FILE *f = fopen(id->name, "r");
+ int r;
+ struct rc_entry ent;
+ r = fread(&ent, sizeof(ent), 1, f);
+ fclose(f);
+ if(r){
+ *auth_lifespan = ent.stamp;
+ return 0;
+ }
+ krb5_clear_error_string (context);
+ return KRB5_RC_IO_UNKNOWN;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_get_name(krb5_context context,
+ krb5_rcache id)
+{
+ return id->name;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_get_type(krb5_context context,
+ krb5_rcache id)
+{
+ return "FILE";
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_server_rcache(krb5_context context,
+ const krb5_data *piece,
+ krb5_rcache *id)
+{
+ krb5_rcache rcache;
+ krb5_error_code ret;
+
+ char *tmp = malloc(4 * piece->length + 1);
+ char *name;
+
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ strvisx(tmp, piece->data, piece->length, VIS_WHITE | VIS_OCTAL);
+#ifdef HAVE_GETEUID
+ asprintf(&name, "FILE:rc_%s_%u", tmp, (unsigned)geteuid());
+#else
+ asprintf(&name, "FILE:rc_%s", tmp);
+#endif
+ free(tmp);
+ if(name == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = krb5_rc_resolve_full(context, &rcache, name);
+ free(name);
+ if(ret)
+ return ret;
+ *id = rcache;
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/send_to_kdc.c b/source4/heimdal/lib/krb5/send_to_kdc.c
new file mode 100644
index 0000000000..45b728aa6c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/send_to_kdc.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "send_to_kdc_plugin.h"
+
+RCSID("$Id$");
+
+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)
+ * waiting `tmout' for a reply and returning the reply in `rep'.
+ * iff limit read up to this many bytes
+ * returns 0 and data in `rep' if succesful, otherwise -1
+ */
+
+static int
+recv_loop (int fd,
+ time_t tmout,
+ int udp,
+ size_t limit,
+ krb5_data *rep)
+{
+ fd_set fdset;
+ struct timeval timeout;
+ int ret;
+ int nbytes;
+
+ if (fd >= FD_SETSIZE) {
+ return -1;
+ }
+
+ krb5_data_zero(rep);
+ do {
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+ timeout.tv_sec = tmout;
+ timeout.tv_usec = 0;
+ ret = select (fd + 1, &fdset, NULL, NULL, &timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ } else if (ret == 0) {
+ return 0;
+ } else {
+ void *tmp;
+
+ if (ioctl (fd, FIONREAD, &nbytes) < 0) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ if(nbytes <= 0)
+ return 0;
+
+ if (limit)
+ nbytes = min(nbytes, limit - rep->length);
+
+ tmp = realloc (rep->data, rep->length + nbytes);
+ if (tmp == NULL) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ rep->data = tmp;
+ ret = recv (fd, (char*)tmp + rep->length, nbytes, 0);
+ if (ret < 0) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ rep->length += ret;
+ }
+ } while(!udp && (limit == 0 || rep->length < limit));
+ return 0;
+}
+
+/*
+ * Send kerberos requests and receive a reply on a udp or any other kind
+ * of a datagram socket. See `recv_loop'.
+ */
+
+static int
+send_and_recv_udp(int fd,
+ time_t tmout,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ if (send (fd, req->data, req->length, 0) < 0)
+ return -1;
+
+ return recv_loop(fd, tmout, 1, 0, rep);
+}
+
+/*
+ * `send_and_recv' for a TCP (or any other stream) socket.
+ * Since there are no record limits on a stream socket the protocol here
+ * is to prepend the request with 4 bytes of its length and the reply
+ * is similarly encoded.
+ */
+
+static int
+send_and_recv_tcp(int fd,
+ time_t tmout,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ unsigned char len[4];
+ unsigned long rep_len;
+ krb5_data len_data;
+
+ _krb5_put_int(len, req->length, 4);
+ if(net_write(fd, len, sizeof(len)) < 0)
+ return -1;
+ if(net_write(fd, req->data, req->length) < 0)
+ return -1;
+ if (recv_loop (fd, tmout, 0, 4, &len_data) < 0)
+ return -1;
+ if (len_data.length != 4) {
+ krb5_data_free (&len_data);
+ return -1;
+ }
+ _krb5_get_int(len_data.data, &rep_len, 4);
+ krb5_data_free (&len_data);
+ if (recv_loop (fd, tmout, 0, rep_len, rep) < 0)
+ return -1;
+ if(rep->length != rep_len) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ 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.
+ */
+
+static int
+send_and_recv_http(int fd,
+ time_t tmout,
+ const char *prefix,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ char *request;
+ char *str;
+ int ret;
+ int len = base64_encode(req->data, req->length, &str);
+
+ if(len < 0)
+ return -1;
+ asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str);
+ free(str);
+ if (request == NULL)
+ return -1;
+ ret = net_write (fd, request, strlen(request));
+ free (request);
+ if (ret < 0)
+ return ret;
+ ret = recv_loop(fd, tmout, 0, 0, rep);
+ if(ret)
+ return ret;
+ {
+ unsigned long rep_len;
+ char *s, *p;
+
+ s = realloc(rep->data, rep->length + 1);
+ if (s == NULL) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ s[rep->length] = 0;
+ p = strstr(s, "\r\n\r\n");
+ if(p == NULL) {
+ krb5_data_zero(rep);
+ free(s);
+ return -1;
+ }
+ p += 4;
+ 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;
+ }
+ memmove(rep->data, p + 4, rep->length);
+ }
+ return 0;
+}
+
+static int
+init_port(const char *s, int fallback)
+{
+ if (s) {
+ int tmp;
+
+ sscanf (s, "%d", &tmp);
+ return htons(tmp);
+ } else
+ return fallback;
+}
+
+/*
+ * Return 0 if succesful, otherwise 1
+ */
+
+static int
+send_via_proxy (krb5_context context,
+ const krb5_krbhst_info *hi,
+ const krb5_data *send_data,
+ krb5_data *receive)
+{
+ char *proxy2 = strdup(context->http_proxy);
+ char *proxy = proxy2;
+ char *prefix;
+ char *colon;
+ struct addrinfo hints;
+ struct addrinfo *ai, *a;
+ int ret;
+ int s = -1;
+ char portstr[NI_MAXSERV];
+
+ if (proxy == NULL)
+ return ENOMEM;
+ if (strncmp (proxy, "http://", 7) == 0)
+ proxy += 7;
+
+ colon = strchr(proxy, ':');
+ if(colon != NULL)
+ *colon++ = '\0';
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf (portstr, sizeof(portstr), "%d",
+ ntohs(init_port (colon, htons(80))));
+ ret = getaddrinfo (proxy, portstr, &hints, &ai);
+ free (proxy2);
+ if (ret)
+ return krb5_eai_to_heim_errno(ret, errno);
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ s = socket (a->ai_family, a->ai_socktype, a->ai_protocol | SOCK_CLOEXEC);
+ if (s < 0)
+ continue;
+ rk_cloexec(s);
+ if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
+ close (s);
+ continue;
+ }
+ break;
+ }
+ if (a == NULL) {
+ freeaddrinfo (ai);
+ return 1;
+ }
+ freeaddrinfo (ai);
+
+ asprintf(&prefix, "http://%s/", hi->hostname);
+ if(prefix == NULL) {
+ close(s);
+ return 1;
+ }
+ ret = send_and_recv_http(s, context->kdc_timeout,
+ prefix, send_data, receive);
+ close (s);
+ free(prefix);
+ if(ret == 0 && receive->length != 0)
+ return 0;
+ return 1;
+}
+
+static krb5_error_code
+send_via_plugin(krb5_context context,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_data,
+ krb5_data *receive)
+{
+ struct krb5_plugin *list = NULL, *e;
+ krb5_error_code ret;
+
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_SEND_TO_KDC, &list);
+ if(ret != 0 || list == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+ krb5plugin_send_to_kdc_ftable *service;
+ void *ctx;
+
+ service = _krb5_plugin_get_symbol(e);
+ if (service->minor_version != 0)
+ continue;
+
+ (*service->init)(context, &ctx);
+ ret = (*service->send_to_kdc)(context, ctx, hi,
+ timeout, send_data, receive);
+ (*service->fini)(ctx);
+ if (ret == 0)
+ break;
+ if (ret != KRB5_PLUGIN_NO_HANDLE) {
+ krb5_set_error_message(context, ret,
+ "Plugin %s failed to lookup with error: %d",
+ KRB5_PLUGIN_SEND_TO_KDC, ret);
+ break;
+ }
+ }
+ _krb5_plugin_free(list);
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+
+/*
+ * Send the data `send' to one host from `handle` and get back the reply
+ * in `receive'.
+ */
+
+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;
+ 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) {
+ 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, context->kdc_timeout, send_data, receive);
+ if (ret == 0 && receive->length != 0)
+ goto out;
+ continue;
+ }
+
+ ret = send_via_plugin(context, hi, context->kdc_timeout,
+ send_data, receive);
+ if (ret == 0 && receive->length != 0)
+ goto out;
+ else if (ret != KRB5_PLUGIN_NO_HANDLE)
+ continue;
+
+ if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
+ if (send_via_proxy (context, hi, send_data, receive) == 0) {
+ ret = 0;
+ goto out;
+ }
+ continue;
+ }
+
+ ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+ if (ret)
+ continue;
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
+ if (fd < 0)
+ continue;
+ rk_cloexec(fd);
+ if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) {
+ close (fd);
+ continue;
+ }
+ switch (hi->proto) {
+ case KRB5_KRBHST_HTTP :
+ ret = send_and_recv_http(fd, context->kdc_timeout,
+ "", send_data, receive);
+ break;
+ case KRB5_KRBHST_TCP :
+ ret = send_and_recv_tcp (fd, context->kdc_timeout,
+ send_data, receive);
+ break;
+ case KRB5_KRBHST_UDP :
+ ret = send_and_recv_udp (fd, context->kdc_timeout,
+ send_data, receive);
+ break;
+ }
+ close (fd);
+ if(ret == 0 && receive->length != 0)
+ goto out;
+ }
+ }
+ krb5_krbhst_reset(context, handle);
+ }
+ krb5_clear_error_string (context);
+ ret = KRB5_KDC_UNREACH;
+out:
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sendto_kdc(krb5_context context,
+ const krb5_data *send_data,
+ const krb5_realm *realm,
+ krb5_data *receive)
+{
+ return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0);
+}
+
+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_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_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ 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_message(context, ENOMEM, "malloc: 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_message(context, ret,
+ "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;
+}
diff --git a/source4/heimdal/lib/krb5/send_to_kdc_plugin.h b/source4/heimdal/lib/krb5/send_to_kdc_plugin.h
new file mode 100644
index 0000000000..e0c2979e28
--- /dev/null
+++ b/source4/heimdal/lib/krb5/send_to_kdc_plugin.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H
+#define HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H 1
+
+#include <krb5.h>
+
+#define KRB5_PLUGIN_SEND_TO_KDC "send_to_kdc"
+
+typedef krb5_error_code
+(*krb5plugin_send_to_kdc_func)(krb5_context,
+ void *,
+ krb5_krbhst_info *,
+ time_t timeout,
+ const krb5_data *,
+ krb5_data *);
+
+typedef struct krb5plugin_send_to_kdc_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_send_to_kdc_func send_to_kdc;
+} krb5plugin_send_to_kdc_ftable;
+
+#endif /* HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H */
diff --git a/source4/heimdal/lib/krb5/set_default_realm.c b/source4/heimdal/lib/krb5/set_default_realm.c
new file mode 100644
index 0000000000..c21ac453a2
--- /dev/null
+++ b/source4/heimdal/lib/krb5/set_default_realm.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Convert the simple string `s' into a NULL-terminated and freshly allocated
+ * list in `list'. Return an error code.
+ */
+
+static krb5_error_code
+string_to_list (krb5_context context, const char *s, krb5_realm **list)
+{
+
+ *list = malloc (2 * sizeof(**list));
+ if (*list == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*list)[0] = strdup (s);
+ if ((*list)[0] == NULL) {
+ free (*list);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*list)[1] = NULL;
+ return 0;
+}
+
+/*
+ * Set the knowledge of the default realm(s) in `context'.
+ * If realm != NULL, that's the new default realm.
+ * Otherwise, the realm(s) are figured out from configuration or DNS.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_default_realm(krb5_context context,
+ const char *realm)
+{
+ krb5_error_code ret = 0;
+ krb5_realm *realms = NULL;
+
+ if (realm == NULL) {
+ realms = krb5_config_get_strings (context, NULL,
+ "libdefaults",
+ "default_realm",
+ NULL);
+ if (realms == NULL)
+ ret = krb5_get_host_realm(context, NULL, &realms);
+ } else {
+ ret = string_to_list (context, realm, &realms);
+ }
+ if (ret)
+ return ret;
+ krb5_free_host_realm (context, context->default_realms);
+ context->default_realms = realms;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/store-int.h b/source4/heimdal/lib/krb5/store-int.h
new file mode 100644
index 0000000000..42e695a11b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store-int.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __store_int_h__
+#define __store_int_h__
+
+struct krb5_storage_data {
+ void *data;
+ ssize_t (*fetch)(struct krb5_storage_data*, void*, size_t);
+ ssize_t (*store)(struct krb5_storage_data*, const void*, size_t);
+ off_t (*seek)(struct krb5_storage_data*, off_t, int);
+ void (*free)(struct krb5_storage_data*);
+ krb5_flags flags;
+ int eof_code;
+};
+
+#endif /* __store_int_h__ */
diff --git a/source4/heimdal/lib/krb5/store.c b/source4/heimdal/lib/krb5/store.c
new file mode 100644
index 0000000000..321ca633a6
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+#define BYTEORDER_IS(SP, V) (((SP)->flags & KRB5_STORAGE_BYTEORDER_MASK) == (V))
+#define BYTEORDER_IS_LE(SP) BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_LE)
+#define BYTEORDER_IS_BE(SP) BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_BE)
+#define BYTEORDER_IS_HOST(SP) (BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_HOST) || \
+ krb5_storage_is_flags((SP), KRB5_STORAGE_HOST_BYTEORDER))
+
+void KRB5_LIB_FUNCTION
+krb5_storage_set_flags(krb5_storage *sp, krb5_flags flags)
+{
+ sp->flags |= flags;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_storage_clear_flags(krb5_storage *sp, krb5_flags flags)
+{
+ sp->flags &= ~flags;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_storage_is_flags(krb5_storage *sp, krb5_flags flags)
+{
+ return (sp->flags & flags) == flags;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_storage_set_byteorder(krb5_storage *sp, krb5_flags byteorder)
+{
+ sp->flags &= ~KRB5_STORAGE_BYTEORDER_MASK;
+ sp->flags |= byteorder;
+}
+
+krb5_flags KRB5_LIB_FUNCTION
+krb5_storage_get_byteorder(krb5_storage *sp, krb5_flags byteorder)
+{
+ return sp->flags & KRB5_STORAGE_BYTEORDER_MASK;
+}
+
+off_t KRB5_LIB_FUNCTION
+krb5_storage_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ return (*sp->seek)(sp, offset, whence);
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+krb5_storage_read(krb5_storage *sp, void *buf, size_t len)
+{
+ return sp->fetch(sp, buf, len);
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+krb5_storage_write(krb5_storage *sp, const void *buf, size_t len)
+{
+ return sp->store(sp, buf, len);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_storage_set_eof_code(krb5_storage *sp, int code)
+{
+ sp->eof_code = code;
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+_krb5_put_int(void *buffer, unsigned long value, size_t size)
+{
+ unsigned char *p = buffer;
+ int i;
+ for (i = size - 1; i >= 0; i--) {
+ p[i] = value & 0xff;
+ value >>= 8;
+ }
+ return size;
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+_krb5_get_int(void *buffer, unsigned long *value, size_t size)
+{
+ unsigned char *p = buffer;
+ unsigned long v = 0;
+ int i;
+ for (i = 0; i < size; i++)
+ v = (v << 8) + p[i];
+ *value = v;
+ return size;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_storage_free(krb5_storage *sp)
+{
+ if(sp->free)
+ (*sp->free)(sp);
+ free(sp->data);
+ free(sp);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_storage_to_data(krb5_storage *sp, krb5_data *data)
+{
+ off_t pos;
+ size_t size;
+ krb5_error_code ret;
+
+ pos = sp->seek(sp, 0, SEEK_CUR);
+ size = (size_t)sp->seek(sp, 0, SEEK_END);
+ ret = krb5_data_alloc (data, size);
+ if (ret) {
+ sp->seek(sp, pos, SEEK_SET);
+ return ret;
+ }
+ if (size) {
+ sp->seek(sp, 0, SEEK_SET);
+ sp->fetch(sp, data->data, data->length);
+ sp->seek(sp, pos, SEEK_SET);
+ }
+ return 0;
+}
+
+static krb5_error_code
+krb5_store_int(krb5_storage *sp,
+ int32_t value,
+ size_t len)
+{
+ int ret;
+ unsigned char v[16];
+
+ if(len > sizeof(v))
+ return EINVAL;
+ _krb5_put_int(v, value, len);
+ ret = sp->store(sp, v, len);
+ if (ret != len)
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_int32(krb5_storage *sp,
+ int32_t value)
+{
+ if(BYTEORDER_IS_HOST(sp))
+ value = htonl(value);
+ else if(BYTEORDER_IS_LE(sp))
+ value = bswap32(value);
+ return krb5_store_int(sp, value, 4);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_uint32(krb5_storage *sp,
+ uint32_t value)
+{
+ return krb5_store_int32(sp, (int32_t)value);
+}
+
+static krb5_error_code
+krb5_ret_int(krb5_storage *sp,
+ int32_t *value,
+ size_t len)
+{
+ int ret;
+ unsigned char v[4];
+ unsigned long w;
+ ret = sp->fetch(sp, v, len);
+ if(ret != len)
+ return (ret<0)?errno:sp->eof_code;
+ _krb5_get_int(v, &w, len);
+ *value = w;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_int32(krb5_storage *sp,
+ int32_t *value)
+{
+ krb5_error_code ret = krb5_ret_int(sp, value, 4);
+ if(ret)
+ return ret;
+ if(BYTEORDER_IS_HOST(sp))
+ *value = htonl(*value);
+ else if(BYTEORDER_IS_LE(sp))
+ *value = bswap32(*value);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_uint32(krb5_storage *sp,
+ uint32_t *value)
+{
+ krb5_error_code ret;
+ int32_t v;
+
+ ret = krb5_ret_int32(sp, &v);
+ if (ret == 0)
+ *value = (uint32_t)v;
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_int16(krb5_storage *sp,
+ int16_t value)
+{
+ if(BYTEORDER_IS_HOST(sp))
+ value = htons(value);
+ else if(BYTEORDER_IS_LE(sp))
+ value = bswap16(value);
+ return krb5_store_int(sp, value, 2);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_uint16(krb5_storage *sp,
+ uint16_t value)
+{
+ return krb5_store_int16(sp, (int16_t)value);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_int16(krb5_storage *sp,
+ int16_t *value)
+{
+ int32_t v;
+ int ret;
+ ret = krb5_ret_int(sp, &v, 2);
+ if(ret)
+ return ret;
+ *value = v;
+ if(BYTEORDER_IS_HOST(sp))
+ *value = htons(*value);
+ else if(BYTEORDER_IS_LE(sp))
+ *value = bswap16(*value);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_uint16(krb5_storage *sp,
+ uint16_t *value)
+{
+ krb5_error_code ret;
+ int16_t v;
+
+ ret = krb5_ret_int16(sp, &v);
+ if (ret == 0)
+ *value = (uint16_t)v;
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_int8(krb5_storage *sp,
+ int8_t value)
+{
+ int ret;
+
+ ret = sp->store(sp, &value, sizeof(value));
+ if (ret != sizeof(value))
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_uint8(krb5_storage *sp,
+ uint8_t value)
+{
+ return krb5_store_int8(sp, (int8_t)value);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_int8(krb5_storage *sp,
+ int8_t *value)
+{
+ int ret;
+
+ ret = sp->fetch(sp, value, sizeof(*value));
+ if (ret != sizeof(*value))
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_uint8(krb5_storage *sp,
+ uint8_t *value)
+{
+ krb5_error_code ret;
+ int8_t v;
+
+ ret = krb5_ret_int8(sp, &v);
+ if (ret == 0)
+ *value = (uint8_t)v;
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_data(krb5_storage *sp,
+ krb5_data data)
+{
+ int ret;
+ ret = krb5_store_int32(sp, data.length);
+ if(ret < 0)
+ return ret;
+ ret = sp->store(sp, data.data, data.length);
+ if(ret != data.length){
+ if(ret < 0)
+ return errno;
+ return sp->eof_code;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_data(krb5_storage *sp,
+ krb5_data *data)
+{
+ int ret;
+ int32_t size;
+
+ ret = krb5_ret_int32(sp, &size);
+ if(ret)
+ return ret;
+ ret = krb5_data_alloc (data, size);
+ if (ret)
+ return ret;
+ if (size) {
+ ret = sp->fetch(sp, data->data, size);
+ if(ret != size)
+ return (ret < 0)? errno : sp->eof_code;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_string(krb5_storage *sp, const char *s)
+{
+ krb5_data data;
+ data.length = strlen(s);
+ data.data = rk_UNCONST(s);
+ return krb5_store_data(sp, data);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_string(krb5_storage *sp,
+ char **string)
+{
+ int ret;
+ krb5_data data;
+ ret = krb5_ret_data(sp, &data);
+ if(ret)
+ return ret;
+ *string = realloc(data.data, data.length + 1);
+ if(*string == NULL){
+ free(data.data);
+ return ENOMEM;
+ }
+ (*string)[data.length] = 0;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_stringz(krb5_storage *sp, const char *s)
+{
+ size_t len = strlen(s) + 1;
+ ssize_t ret;
+
+ ret = sp->store(sp, s, len);
+ if(ret != len) {
+ if(ret < 0)
+ return ret;
+ else
+ return sp->eof_code;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_stringz(krb5_storage *sp,
+ char **string)
+{
+ char c;
+ char *s = NULL;
+ size_t len = 0;
+ ssize_t ret;
+
+ while((ret = sp->fetch(sp, &c, 1)) == 1){
+ char *tmp;
+
+ len++;
+ tmp = realloc (s, len);
+ if (tmp == NULL) {
+ free (s);
+ return ENOMEM;
+ }
+ s = tmp;
+ s[len - 1] = c;
+ if(c == 0)
+ break;
+ }
+ if(ret != 1){
+ free(s);
+ if(ret == 0)
+ return sp->eof_code;
+ return ret;
+ }
+ *string = s;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_stringnl(krb5_storage *sp, const char *s)
+{
+ size_t len = strlen(s);
+ ssize_t ret;
+
+ ret = sp->store(sp, s, len);
+ if(ret != len) {
+ if(ret < 0)
+ return ret;
+ else
+ return sp->eof_code;
+ }
+ ret = sp->store(sp, "\n", 1);
+ if(ret != 1) {
+ if(ret < 0)
+ return ret;
+ else
+ return sp->eof_code;
+ }
+
+ return 0;
+
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_stringnl(krb5_storage *sp,
+ char **string)
+{
+ int expect_nl = 0;
+ char c;
+ char *s = NULL;
+ size_t len = 0;
+ ssize_t ret;
+
+ while((ret = sp->fetch(sp, &c, 1)) == 1){
+ char *tmp;
+
+ if (c == '\r') {
+ expect_nl = 1;
+ continue;
+ }
+ if (expect_nl && c != '\n') {
+ free(s);
+ return KRB5_BADMSGTYPE;
+ }
+
+ len++;
+ tmp = realloc (s, len);
+ if (tmp == NULL) {
+ free (s);
+ return ENOMEM;
+ }
+ s = tmp;
+ if(c == '\n') {
+ s[len - 1] = '\0';
+ break;
+ }
+ s[len - 1] = c;
+ }
+ if(ret != 1){
+ free(s);
+ if(ret == 0)
+ return sp->eof_code;
+ return ret;
+ }
+ *string = s;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_principal(krb5_storage *sp,
+ krb5_const_principal p)
+{
+ int i;
+ int ret;
+
+ if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
+ ret = krb5_store_int32(sp, p->name.name_type);
+ if(ret) return ret;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ret = krb5_store_int32(sp, p->name.name_string.len + 1);
+ else
+ ret = krb5_store_int32(sp, p->name.name_string.len);
+
+ if(ret) return ret;
+ ret = krb5_store_string(sp, p->realm);
+ if(ret) return ret;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_store_string(sp, p->name.name_string.val[i]);
+ if(ret) return ret;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_principal(krb5_storage *sp,
+ krb5_principal *princ)
+{
+ int i;
+ int ret;
+ krb5_principal p;
+ int32_t type;
+ int32_t ncomp;
+
+ p = calloc(1, sizeof(*p));
+ if(p == NULL)
+ return ENOMEM;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
+ type = KRB5_NT_UNKNOWN;
+ else if((ret = krb5_ret_int32(sp, &type))){
+ free(p);
+ return ret;
+ }
+ if((ret = krb5_ret_int32(sp, &ncomp))){
+ free(p);
+ return ret;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ncomp--;
+ if (ncomp < 0) {
+ free(p);
+ return EINVAL;
+ }
+ p->name.name_type = type;
+ p->name.name_string.len = ncomp;
+ ret = krb5_ret_string(sp, &p->realm);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ p->name.name_string.val = calloc(ncomp, sizeof(*p->name.name_string.val));
+ if(p->name.name_string.val == NULL && ncomp != 0){
+ free(p->realm);
+ free(p);
+ return ENOMEM;
+ }
+ for(i = 0; i < ncomp; i++){
+ ret = krb5_ret_string(sp, &p->name.name_string.val[i]);
+ if(ret) {
+ while (i >= 0)
+ free(p->name.name_string.val[i--]);
+ free(p->realm);
+ free(p);
+ return ret;
+ }
+ }
+ *princ = p;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_keyblock(krb5_storage *sp, krb5_keyblock p)
+{
+ int ret;
+ ret = krb5_store_int16(sp, p.keytype);
+ if(ret) return ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE)){
+ /* this should really be enctype, but it is the same as
+ keytype nowadays */
+ ret = krb5_store_int16(sp, p.keytype);
+ if(ret) return ret;
+ }
+
+ ret = krb5_store_data(sp, p.keyvalue);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_keyblock(krb5_storage *sp, krb5_keyblock *p)
+{
+ int ret;
+ int16_t tmp;
+
+ ret = krb5_ret_int16(sp, &tmp);
+ if(ret) return ret;
+ p->keytype = tmp;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE)){
+ ret = krb5_ret_int16(sp, &tmp);
+ if(ret) return ret;
+ }
+
+ ret = krb5_ret_data(sp, &p->keyvalue);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_times(krb5_storage *sp, krb5_times times)
+{
+ int ret;
+ ret = krb5_store_int32(sp, times.authtime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.starttime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.endtime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.renew_till);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_times(krb5_storage *sp, krb5_times *times)
+{
+ int ret;
+ int32_t tmp;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->authtime = tmp;
+ if(ret) return ret;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->starttime = tmp;
+ if(ret) return ret;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->endtime = tmp;
+ if(ret) return ret;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->renew_till = tmp;
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_address(krb5_storage *sp, krb5_address p)
+{
+ int ret;
+ ret = krb5_store_int16(sp, p.addr_type);
+ if(ret) return ret;
+ ret = krb5_store_data(sp, p.address);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_address(krb5_storage *sp, krb5_address *adr)
+{
+ int16_t t;
+ int ret;
+ ret = krb5_ret_int16(sp, &t);
+ if(ret) return ret;
+ adr->addr_type = t;
+ ret = krb5_ret_data(sp, &adr->address);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_addrs(krb5_storage *sp, krb5_addresses p)
+{
+ int i;
+ int ret;
+ ret = krb5_store_int32(sp, p.len);
+ if(ret) return ret;
+ for(i = 0; i<p.len; i++){
+ ret = krb5_store_address(sp, p.val[i]);
+ if(ret) break;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_addrs(krb5_storage *sp, krb5_addresses *adr)
+{
+ int i;
+ int ret;
+ int32_t tmp;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret) return ret;
+ adr->len = tmp;
+ ALLOC(adr->val, adr->len);
+ if (adr->val == NULL && adr->len != 0)
+ return ENOMEM;
+ for(i = 0; i < adr->len; i++){
+ ret = krb5_ret_address(sp, &adr->val[i]);
+ if(ret) break;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_authdata(krb5_storage *sp, krb5_authdata auth)
+{
+ krb5_error_code ret;
+ int i;
+ ret = krb5_store_int32(sp, auth.len);
+ if(ret) return ret;
+ for(i = 0; i < auth.len; i++){
+ ret = krb5_store_int16(sp, auth.val[i].ad_type);
+ if(ret) break;
+ ret = krb5_store_data(sp, auth.val[i].ad_data);
+ if(ret) break;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_authdata(krb5_storage *sp, krb5_authdata *auth)
+{
+ krb5_error_code ret;
+ int32_t tmp;
+ int16_t tmp2;
+ int i;
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret) return ret;
+ ALLOC_SEQ(auth, tmp);
+ if (auth->val == NULL && tmp != 0)
+ return ENOMEM;
+ for(i = 0; i < tmp; i++){
+ ret = krb5_ret_int16(sp, &tmp2);
+ if(ret) break;
+ auth->val[i].ad_type = tmp2;
+ ret = krb5_ret_data(sp, &auth->val[i].ad_data);
+ if(ret) break;
+ }
+ return ret;
+}
+
+static int32_t
+bitswap32(int32_t b)
+{
+ int32_t r = 0;
+ int i;
+ for (i = 0; i < 32; i++) {
+ r = r << 1 | (b & 1);
+ b = b >> 1;
+ }
+ return r;
+}
+
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_creds(krb5_storage *sp, krb5_creds *creds)
+{
+ int ret;
+
+ ret = krb5_store_principal(sp, creds->client);
+ if(ret)
+ return ret;
+ ret = krb5_store_principal(sp, creds->server);
+ if(ret)
+ return ret;
+ ret = krb5_store_keyblock(sp, creds->session);
+ if(ret)
+ return ret;
+ ret = krb5_store_times(sp, creds->times);
+ if(ret)
+ return ret;
+ ret = krb5_store_int8(sp, creds->second_ticket.length != 0); /* is_skey */
+ if(ret)
+ return ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER))
+ ret = krb5_store_int32(sp, creds->flags.i);
+ else
+ ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
+ if(ret)
+ return ret;
+
+ ret = krb5_store_addrs(sp, creds->addresses);
+ if(ret)
+ return ret;
+ ret = krb5_store_authdata(sp, creds->authdata);
+ if(ret)
+ return ret;
+ ret = krb5_store_data(sp, creds->ticket);
+ if(ret)
+ return ret;
+ ret = krb5_store_data(sp, creds->second_ticket);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_creds(krb5_storage *sp, krb5_creds *creds)
+{
+ krb5_error_code ret;
+ int8_t dummy8;
+ int32_t dummy32;
+
+ memset(creds, 0, sizeof(*creds));
+ ret = krb5_ret_principal (sp, &creds->client);
+ if(ret) goto cleanup;
+ ret = krb5_ret_principal (sp, &creds->server);
+ if(ret) goto cleanup;
+ ret = krb5_ret_keyblock (sp, &creds->session);
+ if(ret) goto cleanup;
+ ret = krb5_ret_times (sp, &creds->times);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int8 (sp, &dummy8);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int32 (sp, &dummy32);
+ if(ret) goto cleanup;
+ /*
+ * Runtime detect the what is the higher bits of the bitfield. If
+ * any of the higher bits are set in the input data, it's either a
+ * new ticket flag (and this code need to be removed), or it's a
+ * MIT cache (or new Heimdal cache), lets change it to our current
+ * format.
+ */
+ {
+ uint32_t mask = 0xffff0000;
+ creds->flags.i = 0;
+ creds->flags.b.anonymous = 1;
+ if (creds->flags.i & mask)
+ mask = ~mask;
+ if (dummy32 & mask)
+ dummy32 = bitswap32(dummy32);
+ }
+ creds->flags.i = dummy32;
+ ret = krb5_ret_addrs (sp, &creds->addresses);
+ if(ret) goto cleanup;
+ ret = krb5_ret_authdata (sp, &creds->authdata);
+ if(ret) goto cleanup;
+ ret = krb5_ret_data (sp, &creds->ticket);
+ if(ret) goto cleanup;
+ ret = krb5_ret_data (sp, &creds->second_ticket);
+cleanup:
+ if(ret) {
+#if 0
+ krb5_free_cred_contents(context, creds); /* XXX */
+#endif
+ }
+ return ret;
+}
+
+#define SC_CLIENT_PRINCIPAL 0x0001
+#define SC_SERVER_PRINCIPAL 0x0002
+#define SC_SESSION_KEY 0x0004
+#define SC_TICKET 0x0008
+#define SC_SECOND_TICKET 0x0010
+#define SC_AUTHDATA 0x0020
+#define SC_ADDRESSES 0x0040
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_creds_tag(krb5_storage *sp, krb5_creds *creds)
+{
+ int ret;
+ int32_t header = 0;
+
+ if (creds->client)
+ header |= SC_CLIENT_PRINCIPAL;
+ if (creds->server)
+ header |= SC_SERVER_PRINCIPAL;
+ if (creds->session.keytype != ETYPE_NULL)
+ header |= SC_SESSION_KEY;
+ if (creds->ticket.data)
+ header |= SC_TICKET;
+ if (creds->second_ticket.length)
+ header |= SC_SECOND_TICKET;
+ if (creds->authdata.len)
+ header |= SC_AUTHDATA;
+ if (creds->addresses.len)
+ header |= SC_ADDRESSES;
+
+ ret = krb5_store_int32(sp, header);
+
+ if (creds->client) {
+ ret = krb5_store_principal(sp, creds->client);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->server) {
+ ret = krb5_store_principal(sp, creds->server);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->session.keytype != ETYPE_NULL) {
+ ret = krb5_store_keyblock(sp, creds->session);
+ if(ret)
+ return ret;
+ }
+
+ ret = krb5_store_times(sp, creds->times);
+ if(ret)
+ return ret;
+ ret = krb5_store_int8(sp, creds->second_ticket.length != 0); /* is_skey */
+ if(ret)
+ return ret;
+
+ ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
+ if(ret)
+ return ret;
+
+ if (creds->addresses.len) {
+ ret = krb5_store_addrs(sp, creds->addresses);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->authdata.len) {
+ ret = krb5_store_authdata(sp, creds->authdata);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->ticket.data) {
+ ret = krb5_store_data(sp, creds->ticket);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->second_ticket.data) {
+ ret = krb5_store_data(sp, creds->second_ticket);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_creds_tag(krb5_storage *sp,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ int8_t dummy8;
+ int32_t dummy32, header;
+
+ memset(creds, 0, sizeof(*creds));
+
+ ret = krb5_ret_int32 (sp, &header);
+ if (ret) goto cleanup;
+
+ if (header & SC_CLIENT_PRINCIPAL) {
+ ret = krb5_ret_principal (sp, &creds->client);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SERVER_PRINCIPAL) {
+ ret = krb5_ret_principal (sp, &creds->server);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SESSION_KEY) {
+ ret = krb5_ret_keyblock (sp, &creds->session);
+ if(ret) goto cleanup;
+ }
+ ret = krb5_ret_times (sp, &creds->times);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int8 (sp, &dummy8);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int32 (sp, &dummy32);
+ if(ret) goto cleanup;
+ /*
+ * Runtime detect the what is the higher bits of the bitfield. If
+ * any of the higher bits are set in the input data, it's either a
+ * new ticket flag (and this code need to be removed), or it's a
+ * MIT cache (or new Heimdal cache), lets change it to our current
+ * format.
+ */
+ {
+ uint32_t mask = 0xffff0000;
+ creds->flags.i = 0;
+ creds->flags.b.anonymous = 1;
+ if (creds->flags.i & mask)
+ mask = ~mask;
+ if (dummy32 & mask)
+ dummy32 = bitswap32(dummy32);
+ }
+ creds->flags.i = dummy32;
+ if (header & SC_ADDRESSES) {
+ ret = krb5_ret_addrs (sp, &creds->addresses);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_AUTHDATA) {
+ ret = krb5_ret_authdata (sp, &creds->authdata);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_TICKET) {
+ ret = krb5_ret_data (sp, &creds->ticket);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SECOND_TICKET) {
+ ret = krb5_ret_data (sp, &creds->second_ticket);
+ if(ret) goto cleanup;
+ }
+
+cleanup:
+ if(ret) {
+#if 0
+ krb5_free_cred_contents(context, creds); /* XXX */
+#endif
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/store_emem.c b/source4/heimdal/lib/krb5/store_emem.c
new file mode 100644
index 0000000000..3cb561ec77
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store_emem.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+typedef struct emem_storage{
+ unsigned char *base;
+ size_t size;
+ size_t len;
+ unsigned char *ptr;
+}emem_storage;
+
+static ssize_t
+emem_fetch(krb5_storage *sp, void *data, size_t size)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ if(s->base + s->len - s->ptr < size)
+ size = s->base + s->len - s->ptr;
+ memmove(data, s->ptr, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+emem_store(krb5_storage *sp, const void *data, size_t size)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ if(size > s->base + s->size - s->ptr){
+ void *base;
+ size_t sz, off;
+ off = s->ptr - s->base;
+ sz = off + size;
+ if (sz < 4096)
+ sz *= 2;
+ base = realloc(s->base, sz);
+ if(base == NULL)
+ return 0;
+ s->size = sz;
+ s->base = base;
+ s->ptr = (unsigned char*)base + off;
+ }
+ memmove(s->ptr, data, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static off_t
+emem_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ switch(whence){
+ case SEEK_SET:
+ if(offset > s->size)
+ offset = s->size;
+ if(offset < 0)
+ offset = 0;
+ s->ptr = s->base + offset;
+ if(offset > s->len)
+ s->len = offset;
+ break;
+ case SEEK_CUR:
+ sp->seek(sp,s->ptr - s->base + offset, SEEK_SET);
+ break;
+ case SEEK_END:
+ sp->seek(sp, s->len + offset, SEEK_SET);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return s->ptr - s->base;
+}
+
+static void
+emem_free(krb5_storage *sp)
+{
+ emem_storage *s = sp->data;
+ memset(s->base, 0, s->len);
+ free(s->base);
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_emem(void)
+{
+ krb5_storage *sp;
+ emem_storage *s;
+
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL)
+ return NULL;
+
+ s = malloc(sizeof(*s));
+ if (s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->size = 1024;
+ s->base = malloc(s->size);
+ if (s->base == NULL) {
+ free(sp);
+ free(s);
+ return NULL;
+ }
+ s->len = 0;
+ s->ptr = s->base;
+ sp->fetch = emem_fetch;
+ sp->store = emem_store;
+ sp->seek = emem_seek;
+ sp->free = emem_free;
+ return sp;
+}
diff --git a/source4/heimdal/lib/krb5/store_fd.c b/source4/heimdal/lib/krb5/store_fd.c
new file mode 100644
index 0000000000..21fa171c28
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store_fd.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+typedef struct fd_storage {
+ int fd;
+} fd_storage;
+
+#define FD(S) (((fd_storage*)(S)->data)->fd)
+
+static ssize_t
+fd_fetch(krb5_storage * sp, void *data, size_t size)
+{
+ return net_read(FD(sp), data, size);
+}
+
+static ssize_t
+fd_store(krb5_storage * sp, const void *data, size_t size)
+{
+ return net_write(FD(sp), data, size);
+}
+
+static off_t
+fd_seek(krb5_storage * sp, off_t offset, int whence)
+{
+ return lseek(FD(sp), offset, whence);
+}
+
+static void
+fd_free(krb5_storage * sp)
+{
+ close(FD(sp));
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_fd(int fd)
+{
+ krb5_storage *sp;
+
+ fd = dup(fd);
+ if (fd < 0)
+ return NULL;
+
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL) {
+ close(fd);
+ return NULL;
+ }
+
+ sp->data = malloc(sizeof(fd_storage));
+ if (sp->data == NULL) {
+ close(fd);
+ free(sp);
+ return NULL;
+ }
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ FD(sp) = fd;
+ sp->fetch = fd_fetch;
+ sp->store = fd_store;
+ sp->seek = fd_seek;
+ sp->free = fd_free;
+ return sp;
+}
diff --git a/source4/heimdal/lib/krb5/store_mem.c b/source4/heimdal/lib/krb5/store_mem.c
new file mode 100644
index 0000000000..6d8306051a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store_mem.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1997 - 2000, 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+typedef struct mem_storage{
+ unsigned char *base;
+ size_t size;
+ unsigned char *ptr;
+}mem_storage;
+
+static ssize_t
+mem_fetch(krb5_storage *sp, void *data, size_t size)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ if(size > s->base + s->size - s->ptr)
+ size = s->base + s->size - s->ptr;
+ memmove(data, s->ptr, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+mem_store(krb5_storage *sp, const void *data, size_t size)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ if(size > s->base + s->size - s->ptr)
+ size = s->base + s->size - s->ptr;
+ memmove(s->ptr, data, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+mem_no_store(krb5_storage *sp, const void *data, size_t size)
+{
+ return -1;
+}
+
+static off_t
+mem_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ switch(whence){
+ case SEEK_SET:
+ if(offset > s->size)
+ offset = s->size;
+ if(offset < 0)
+ offset = 0;
+ s->ptr = s->base + offset;
+ break;
+ case SEEK_CUR:
+ return sp->seek(sp, s->ptr - s->base + offset, SEEK_SET);
+ case SEEK_END:
+ return sp->seek(sp, s->size + offset, SEEK_SET);
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return s->ptr - s->base;
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_mem(void *buf, size_t len)
+{
+ krb5_storage *sp = malloc(sizeof(krb5_storage));
+ mem_storage *s;
+ if(sp == NULL)
+ return NULL;
+ s = malloc(sizeof(*s));
+ if(s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->base = buf;
+ s->size = len;
+ s->ptr = buf;
+ sp->fetch = mem_fetch;
+ sp->store = mem_store;
+ sp->seek = mem_seek;
+ sp->free = NULL;
+ return sp;
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_data(krb5_data *data)
+{
+ return krb5_storage_from_mem(data->data, data->length);
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_readonly_mem(const void *buf, size_t len)
+{
+ krb5_storage *sp = malloc(sizeof(krb5_storage));
+ mem_storage *s;
+ if(sp == NULL)
+ return NULL;
+ s = malloc(sizeof(*s));
+ if(s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->base = rk_UNCONST(buf);
+ s->size = len;
+ s->ptr = rk_UNCONST(buf);
+ sp->fetch = mem_fetch;
+ sp->store = mem_no_store;
+ sp->seek = mem_seek;
+ sp->free = NULL;
+ return sp;
+}
diff --git a/source4/heimdal/lib/krb5/ticket.c b/source4/heimdal/lib/krb5/ticket.c
new file mode 100644
index 0000000000..77ce8cb221
--- /dev/null
+++ b/source4/heimdal/lib/krb5/ticket.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_ticket(krb5_context context,
+ krb5_ticket *ticket)
+{
+ free_EncTicketPart(&ticket->ticket);
+ krb5_free_principal(context, ticket->client);
+ krb5_free_principal(context, ticket->server);
+ free(ticket);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_ticket(krb5_context context,
+ const krb5_ticket *from,
+ krb5_ticket **to)
+{
+ krb5_error_code ret;
+ krb5_ticket *tmp;
+
+ *to = NULL;
+ tmp = malloc(sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
+ free(tmp);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, from->client, &tmp->client);
+ if(ret){
+ free_EncTicketPart(&tmp->ticket);
+ free(tmp);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, from->server, &tmp->server);
+ if(ret){
+ krb5_free_principal(context, tmp->client);
+ free_EncTicketPart(&tmp->ticket);
+ free(tmp);
+ return ret;
+ }
+ *to = tmp;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ticket_get_client(krb5_context context,
+ const krb5_ticket *ticket,
+ krb5_principal *client)
+{
+ return krb5_copy_principal(context, ticket->client, client);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ticket_get_server(krb5_context context,
+ const krb5_ticket *ticket,
+ krb5_principal *server)
+{
+ return krb5_copy_principal(context, ticket->server, server);
+}
+
+time_t KRB5_LIB_FUNCTION
+krb5_ticket_get_endtime(krb5_context context,
+ const krb5_ticket *ticket)
+{
+ return ticket->ticket.endtime;
+}
+
+static int
+find_type_in_ad(krb5_context context,
+ int type,
+ krb5_data *data,
+ krb5_boolean *found,
+ krb5_boolean failp,
+ krb5_keyblock *sessionkey,
+ const AuthorizationData *ad,
+ int level)
+{
+ krb5_error_code ret = 0;
+ int i;
+
+ if (level > 9) {
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret,
+ "Authorization data nested deeper "
+ "then %d levels, stop searching", level);
+ goto out;
+ }
+
+ /*
+ * Only copy out the element the first time we get to it, we need
+ * to run over the whole authorization data fields to check if
+ * there are any container clases we need to care about.
+ */
+ for (i = 0; i < ad->len; i++) {
+ if (!*found && ad->val[i].ad_type == type) {
+ ret = der_copy_octet_string(&ad->val[i].ad_data, data);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ *found = TRUE;
+ continue;
+ }
+ switch (ad->val[i].ad_type) {
+ case KRB5_AUTHDATA_IF_RELEVANT: {
+ AuthorizationData child;
+ ret = decode_AuthorizationData(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode "
+ "IF_RELEVANT with %d", (int)ret);
+ goto out;
+ }
+ ret = find_type_in_ad(context, type, data, found, FALSE,
+ sessionkey, &child, level + 1);
+ free_AuthorizationData(&child);
+ if (ret)
+ goto out;
+ break;
+ }
+#if 0 /* XXX test */
+ case KRB5_AUTHDATA_KDC_ISSUED: {
+ AD_KDCIssued child;
+
+ ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode "
+ "AD_KDCIssued with %d", ret);
+ goto out;
+ }
+ if (failp) {
+ krb5_boolean valid;
+ krb5_data buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
+ &child.elements, &len, ret);
+ if (ret) {
+ free_AD_KDCIssued(&child);
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ if(buf.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
+ &child.ad_checksum, &valid);
+ krb5_data_free(&buf);
+ if (ret) {
+ free_AD_KDCIssued(&child);
+ goto out;
+ }
+ if (!valid) {
+ krb5_clear_error_string(context);
+ ret = ENOENT;
+ free_AD_KDCIssued(&child);
+ goto out;
+ }
+ }
+ ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
+ &child.elements, level + 1);
+ free_AD_KDCIssued(&child);
+ if (ret)
+ goto out;
+ break;
+ }
+#endif
+ case KRB5_AUTHDATA_AND_OR:
+ if (!failp)
+ break;
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret, "Authorization data contains "
+ "AND-OR element that is unknown to the "
+ "application");
+ goto out;
+ default:
+ if (!failp)
+ break;
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret, "Authorization data contains "
+ "unknown type (%d) ", ad->val[i].ad_type);
+ goto out;
+ }
+ }
+out:
+ if (ret) {
+ if (*found) {
+ krb5_data_free(data);
+ *found = 0;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Extract the authorization data type of `type' from the
+ * 'ticket'. Store the field in `data'. This function is to use for
+ * kerberos applications.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ticket_get_authorization_data_type(krb5_context context,
+ krb5_ticket *ticket,
+ int type,
+ krb5_data *data)
+{
+ AuthorizationData *ad;
+ krb5_error_code ret;
+ krb5_boolean found = FALSE;
+
+ krb5_data_zero(data);
+
+ ad = ticket->ticket.authorization_data;
+ if (ticket->ticket.authorization_data == NULL) {
+ krb5_set_error_message(context, ENOENT,
+ "Ticket have not authorization data");
+ return ENOENT; /* XXX */
+ }
+
+ ret = find_type_in_ad(context, type, data, &found, TRUE,
+ &ticket->ticket.key, ad, 0);
+ if (ret)
+ return ret;
+ if (!found) {
+ krb5_set_error_message(context, ENOENT, "Ticket have not "
+ "authorization data of type %d", type);
+ return ENOENT; /* XXX */
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/time.c b/source4/heimdal/lib/krb5/time.c
new file mode 100644
index 0000000000..7a9b36372c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/time.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Set the absolute time that the caller knows the kdc has so the
+ * kerberos library can calculate the relative diffrence beteen the
+ * KDC time and local system time.
+ *
+ * @param context Keberos 5 context.
+ * @param sec The applications new of "now" in seconds
+ * @param usec The applications new of "now" in micro seconds
+
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_real_time (krb5_context context,
+ krb5_timestamp sec,
+ int32_t usec)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ context->kdc_sec_offset = sec - tv.tv_sec;
+
+ /**
+ * If the caller passes in a negative usec, its assumed to be
+ * unknown and the function will use the current time usec.
+ */
+ if (usec >= 0) {
+ context->kdc_usec_offset = usec - tv.tv_usec;
+
+ if (context->kdc_usec_offset < 0) {
+ context->kdc_sec_offset--;
+ context->kdc_usec_offset += 1000000;
+ }
+ } else
+ context->kdc_usec_offset = tv.tv_usec;
+
+ return 0;
+}
+
+/*
+ * return ``corrected'' time in `timeret'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_timeofday (krb5_context context,
+ krb5_timestamp *timeret)
+{
+ *timeret = time(NULL) + context->kdc_sec_offset;
+ return 0;
+}
+
+/*
+ * like gettimeofday but with time correction to the KDC
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_us_timeofday (krb5_context context,
+ krb5_timestamp *sec,
+ int32_t *usec)
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+
+ *sec = tv.tv_sec + context->kdc_sec_offset;
+ *usec = tv.tv_usec; /* XXX */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_format_time(krb5_context context, time_t t,
+ char *s, size_t len, krb5_boolean include_time)
+{
+ struct tm *tm;
+ if(context->log_utc)
+ tm = gmtime (&t);
+ else
+ tm = localtime(&t);
+ if(tm == NULL ||
+ strftime(s, len, include_time ? context->time_fmt : context->date_fmt, tm) == 0)
+ snprintf(s, len, "%ld", (long)t);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_deltat(const char *string, krb5_deltat *deltat)
+{
+ if((*deltat = parse_time(string, "s")) == -1)
+ return KRB5_DELTAT_BADFORMAT;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/transited.c b/source4/heimdal/lib/krb5/transited.c
new file mode 100644
index 0000000000..c9db832348
--- /dev/null
+++ b/source4/heimdal/lib/krb5/transited.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/* this is an attempt at one of the most horrible `compression'
+ schemes that has ever been invented; it's so amazingly brain-dead
+ that words can not describe it, and all this just to save a few
+ silly bytes */
+
+struct tr_realm {
+ char *realm;
+ unsigned leading_space:1;
+ unsigned leading_slash:1;
+ unsigned trailing_dot:1;
+ struct tr_realm *next;
+};
+
+static void
+free_realms(struct tr_realm *r)
+{
+ struct tr_realm *p;
+ while(r){
+ p = r;
+ r = r->next;
+ free(p->realm);
+ free(p);
+ }
+}
+
+static int
+make_path(krb5_context context, struct tr_realm *r,
+ const char *from, const char *to)
+{
+ const char *p;
+ struct tr_realm *path = r->next;
+ struct tr_realm *tmp;
+
+ if(strlen(from) < strlen(to)){
+ const char *str;
+ str = from;
+ from = to;
+ to = str;
+ }
+
+ if(strcmp(from + strlen(from) - strlen(to), to) == 0){
+ p = from;
+ while(1){
+ p = strchr(p, '.');
+ if(p == NULL) {
+ krb5_clear_error_string (context);
+ return KRB5KDC_ERR_POLICY;
+ }
+ p++;
+ if(strcmp(p, to) == 0)
+ break;
+ tmp = calloc(1, sizeof(*tmp));
+ if(tmp == NULL){
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ tmp->next = path;
+ path = tmp;
+ path->realm = strdup(p);
+ if(path->realm == NULL){
+ r->next = path; /* XXX */
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;;
+ }
+ }
+ }else if(strncmp(from, to, strlen(to)) == 0){
+ p = from + strlen(from);
+ while(1){
+ while(p >= from && *p != '/') p--;
+ if(p == from) {
+ r->next = path; /* XXX */
+ return KRB5KDC_ERR_POLICY;
+ }
+ if(strncmp(to, from, p - from) == 0)
+ break;
+ tmp = calloc(1, sizeof(*tmp));
+ if(tmp == NULL){
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ tmp->next = path;
+ path = tmp;
+ path->realm = malloc(p - from + 1);
+ if(path->realm == NULL){
+ r->next = path; /* XXX */
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(path->realm, from, p - from);
+ path->realm[p - from] = '\0';
+ p--;
+ }
+ } else {
+ krb5_clear_error_string (context);
+ return KRB5KDC_ERR_POLICY;
+ }
+ r->next = path;
+
+ return 0;
+}
+
+static int
+make_paths(krb5_context context,
+ struct tr_realm *realms, const char *client_realm,
+ const char *server_realm)
+{
+ struct tr_realm *r;
+ int ret;
+ const char *prev_realm = client_realm;
+ const char *next_realm = NULL;
+ for(r = realms; r; r = r->next){
+ /* it *might* be that you can have more than one empty
+ component in a row, at least that's how I interpret the
+ "," exception in 1510 */
+ if(r->realm[0] == '\0'){
+ while(r->next && r->next->realm[0] == '\0')
+ r = r->next;
+ if(r->next)
+ next_realm = r->next->realm;
+ else
+ next_realm = server_realm;
+ ret = make_path(context, r, prev_realm, next_realm);
+ if(ret){
+ free_realms(realms);
+ return ret;
+ }
+ }
+ prev_realm = r->realm;
+ }
+ return 0;
+}
+
+static int
+expand_realms(krb5_context context,
+ struct tr_realm *realms, const char *client_realm)
+{
+ struct tr_realm *r;
+ const char *prev_realm = NULL;
+ for(r = realms; r; r = r->next){
+ if(r->trailing_dot){
+ char *tmp;
+ size_t len;
+
+ if(prev_realm == NULL)
+ prev_realm = client_realm;
+
+ len = strlen(r->realm) + strlen(prev_realm) + 1;
+
+ tmp = realloc(r->realm, len);
+ if(tmp == NULL){
+ free_realms(realms);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ r->realm = tmp;
+ strlcat(r->realm, prev_realm, len);
+ }else if(r->leading_slash && !r->leading_space && prev_realm){
+ /* yet another exception: if you use x500-names, the
+ leading realm doesn't have to be "quoted" with a space */
+ char *tmp;
+ size_t len = strlen(r->realm) + strlen(prev_realm) + 1;
+
+ tmp = malloc(len);
+ if(tmp == NULL){
+ free_realms(realms);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ strlcpy(tmp, prev_realm, len);
+ strlcat(tmp, r->realm, len);
+ free(r->realm);
+ r->realm = tmp;
+ }
+ prev_realm = r->realm;
+ }
+ return 0;
+}
+
+static struct tr_realm *
+make_realm(char *realm)
+{
+ struct tr_realm *r;
+ char *p, *q;
+ int quote = 0;
+ r = calloc(1, sizeof(*r));
+ if(r == NULL){
+ free(realm);
+ return NULL;
+ }
+ r->realm = realm;
+ for(p = q = r->realm; *p; p++){
+ if(p == r->realm && *p == ' '){
+ r->leading_space = 1;
+ continue;
+ }
+ if(q == r->realm && *p == '/')
+ r->leading_slash = 1;
+ if(quote){
+ *q++ = *p;
+ quote = 0;
+ continue;
+ }
+ if(*p == '\\'){
+ quote = 1;
+ continue;
+ }
+ if(p[0] == '.' && p[1] == '\0')
+ r->trailing_dot = 1;
+ *q++ = *p;
+ }
+ *q = '\0';
+ return r;
+}
+
+static struct tr_realm*
+append_realm(struct tr_realm *head, struct tr_realm *r)
+{
+ struct tr_realm *p;
+ if(head == NULL){
+ r->next = NULL;
+ return r;
+ }
+ p = head;
+ while(p->next) p = p->next;
+ p->next = r;
+ return head;
+}
+
+static int
+decode_realms(krb5_context context,
+ const char *tr, int length, struct tr_realm **realms)
+{
+ struct tr_realm *r = NULL;
+
+ char *tmp;
+ int quote = 0;
+ const char *start = tr;
+ int i;
+
+ for(i = 0; i < length; i++){
+ if(quote){
+ quote = 0;
+ continue;
+ }
+ if(tr[i] == '\\'){
+ quote = 1;
+ continue;
+ }
+ if(tr[i] == ','){
+ tmp = malloc(tr + i - start + 1);
+ if(tmp == NULL){
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(tmp, start, tr + i - start);
+ tmp[tr + i - start] = '\0';
+ r = make_realm(tmp);
+ if(r == NULL){
+ free_realms(*realms);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *realms = append_realm(*realms, r);
+ start = tr + i + 1;
+ }
+ }
+ tmp = malloc(tr + i - start + 1);
+ if(tmp == NULL){
+ free(*realms);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(tmp, start, tr + i - start);
+ tmp[tr + i - start] = '\0';
+ r = make_realm(tmp);
+ if(r == NULL){
+ free_realms(*realms);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *realms = append_realm(*realms, r);
+
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_domain_x500_decode(krb5_context context,
+ krb5_data tr, char ***realms, unsigned int *num_realms,
+ const char *client_realm, const char *server_realm)
+{
+ struct tr_realm *r = NULL;
+ struct tr_realm *p, **q;
+ int ret;
+
+ if(tr.length == 0) {
+ *realms = NULL;
+ *num_realms = 0;
+ return 0;
+ }
+
+ /* split string in components */
+ ret = decode_realms(context, tr.data, tr.length, &r);
+ if(ret)
+ return ret;
+
+ /* apply prefix rule */
+ ret = expand_realms(context, r, client_realm);
+ if(ret)
+ return ret;
+
+ ret = make_paths(context, r, client_realm, server_realm);
+ if(ret)
+ return ret;
+
+ /* remove empty components and count realms */
+ q = &r;
+ *num_realms = 0;
+ for(p = r; p; ){
+ if(p->realm[0] == '\0'){
+ free(p->realm);
+ *q = p->next;
+ free(p);
+ p = *q;
+ }else{
+ q = &p->next;
+ p = p->next;
+ (*num_realms)++;
+ }
+ }
+ if (*num_realms < 0 || *num_realms + 1 > UINT_MAX/sizeof(**realms))
+ return ERANGE;
+
+ {
+ char **R;
+ R = malloc((*num_realms + 1) * sizeof(*R));
+ if (R == NULL)
+ return ENOMEM;
+ *realms = R;
+ while(r){
+ *R++ = r->realm;
+ p = r->next;
+ free(r);
+ r = p;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_domain_x500_encode(char **realms, unsigned int num_realms,
+ krb5_data *encoding)
+{
+ char *s = NULL;
+ int len = 0;
+ unsigned int i;
+ krb5_data_zero(encoding);
+ if (num_realms == 0)
+ return 0;
+ for(i = 0; i < num_realms; i++){
+ len += strlen(realms[i]);
+ if(realms[i][0] == '/')
+ len++;
+ }
+ len += num_realms - 1;
+ s = malloc(len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ *s = '\0';
+ for(i = 0; i < num_realms; i++){
+ if(i && i < num_realms - 1)
+ strlcat(s, ",", len + 1);
+ if(realms[i][0] == '/')
+ strlcat(s, " ", len + 1);
+ strlcat(s, realms[i], len + 1);
+ }
+ encoding->data = s;
+ encoding->length = strlen(s);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_check_transited(krb5_context context,
+ krb5_const_realm client_realm,
+ krb5_const_realm server_realm,
+ krb5_realm *realms,
+ unsigned int num_realms,
+ int *bad_realm)
+{
+ char **tr_realms;
+ char **p;
+ int i;
+
+ if(num_realms == 0)
+ return 0;
+
+ tr_realms = krb5_config_get_strings(context, NULL,
+ "capaths",
+ client_realm,
+ server_realm,
+ NULL);
+ for(i = 0; i < num_realms; i++) {
+ for(p = tr_realms; p && *p; p++) {
+ if(strcmp(*p, realms[i]) == 0)
+ break;
+ }
+ if(p == NULL || *p == NULL) {
+ krb5_config_free_strings(tr_realms);
+ krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
+ "no transit through realm %s",
+ realms[i]);
+ if(bad_realm)
+ *bad_realm = i;
+ return KRB5KRB_AP_ERR_ILL_CR_TKT;
+ }
+ }
+ krb5_config_free_strings(tr_realms);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_check_transited_realms(krb5_context context,
+ const char *const *realms,
+ unsigned int num_realms,
+ int *bad_realm)
+{
+ int i;
+ int ret = 0;
+ char **bad_realms = krb5_config_get_strings(context, NULL,
+ "libdefaults",
+ "transited_realms_reject",
+ NULL);
+ if(bad_realms == NULL)
+ return 0;
+
+ for(i = 0; i < num_realms; i++) {
+ char **p;
+ for(p = bad_realms; *p; p++)
+ if(strcmp(*p, realms[i]) == 0) {
+ ret = KRB5KRB_AP_ERR_ILL_CR_TKT;
+ krb5_set_error_message (context, ret,
+ "no transit through realm %s", *p);
+ if(bad_realm)
+ *bad_realm = i;
+ break;
+ }
+ }
+ krb5_config_free_strings(bad_realms);
+ return ret;
+}
+
+#if 0
+int
+main(int argc, char **argv)
+{
+ krb5_data x;
+ char **r;
+ int num, i;
+ x.data = argv[1];
+ x.length = strlen(x.data);
+ if(domain_expand(x, &r, &num, argv[2], argv[3]))
+ exit(1);
+ for(i = 0; i < num; i++)
+ printf("%s\n", r[i]);
+ return 0;
+}
+#endif
+
diff --git a/source4/heimdal/lib/krb5/v4_glue.c b/source4/heimdal/lib/krb5/v4_glue.c
new file mode 100644
index 0000000000..baa4bd6892
--- /dev/null
+++ b/source4/heimdal/lib/krb5/v4_glue.c
@@ -0,0 +1,940 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+#include "krb5-v4compat.h"
+
+/*
+ *
+ */
+
+#define RCHECK(r,func,label) \
+ do { (r) = func ; if (r) goto label; } while(0);
+
+
+/* include this here, to avoid dependencies on libkrb */
+
+static const int _tkt_lifetimes[TKTLIFENUMFIXED] = {
+ 38400, 41055, 43894, 46929, 50174, 53643, 57352, 61318,
+ 65558, 70091, 74937, 80119, 85658, 91581, 97914, 104684,
+ 111922, 119661, 127935, 136781, 146239, 156350, 167161, 178720,
+ 191077, 204289, 218415, 233517, 249664, 266926, 285383, 305116,
+ 326213, 348769, 372885, 398668, 426234, 455705, 487215, 520904,
+ 556921, 595430, 636601, 680618, 727680, 777995, 831789, 889303,
+ 950794, 1016537, 1086825, 1161973, 1242318, 1328218, 1420057, 1518247,
+ 1623226, 1735464, 1855462, 1983758, 2120925, 2267576, 2424367, 2592000
+};
+
+int KRB5_LIB_FUNCTION
+_krb5_krb_time_to_life(time_t start, time_t end)
+{
+ int i;
+ time_t life = end - start;
+
+ if (life > MAXTKTLIFETIME || life <= 0)
+ return 0;
+#if 0
+ if (krb_no_long_lifetimes)
+ return (life + 5*60 - 1)/(5*60);
+#endif
+
+ if (end >= NEVERDATE)
+ return TKTLIFENOEXPIRE;
+ if (life < _tkt_lifetimes[0])
+ return (life + 5*60 - 1)/(5*60);
+ for (i=0; i<TKTLIFENUMFIXED; i++)
+ if (life <= _tkt_lifetimes[i])
+ return i + TKTLIFEMINFIXED;
+ return 0;
+
+}
+
+time_t KRB5_LIB_FUNCTION
+_krb5_krb_life_to_time(int start, int life_)
+{
+ unsigned char life = (unsigned char) life_;
+
+#if 0
+ if (krb_no_long_lifetimes)
+ return start + life*5*60;
+#endif
+
+ if (life == TKTLIFENOEXPIRE)
+ return NEVERDATE;
+ if (life < TKTLIFEMINFIXED)
+ return start + life*5*60;
+ if (life > TKTLIFEMAXFIXED)
+ return start + MAXTKTLIFETIME;
+ return start + _tkt_lifetimes[life - TKTLIFEMINFIXED];
+}
+
+/*
+ * Get the name of the krb4 credentials cache, will use `tkfile' as
+ * the name if that is passed in. `cc' must be free()ed by caller,
+ */
+
+static krb5_error_code
+get_krb4_cc_name(const char *tkfile, char **cc)
+{
+
+ *cc = NULL;
+ if(tkfile == NULL) {
+ char *path;
+ if(!issuid()) {
+ path = getenv("KRBTKFILE");
+ if (path)
+ *cc = strdup(path);
+ }
+ if(*cc == NULL)
+ if (asprintf(cc, "%s%u", TKT_ROOT, (unsigned)getuid()) < 0)
+ return errno;
+ } else {
+ *cc = strdup(tkfile);
+ if (*cc == NULL)
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * Write a Kerberos 4 ticket file
+ */
+
+#define KRB5_TF_LCK_RETRY_COUNT 50
+#define KRB5_TF_LCK_RETRY 1
+
+static krb5_error_code
+write_v4_cc(krb5_context context, const char *tkfile,
+ krb5_storage *sp, int append)
+{
+ krb5_error_code ret;
+ struct stat sb;
+ krb5_data data;
+ char *path;
+ int fd, i;
+
+ ret = get_krb4_cc_name(tkfile, &path);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "krb5_krb_tf_setup: failed getting "
+ "the krb4 credentials cache name");
+ return ret;
+ }
+
+ fd = open(path, O_WRONLY|O_CREAT, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "krb5_krb_tf_setup: error opening file %s",
+ path);
+ free(path);
+ return ret;
+ }
+ rk_cloexec(fd);
+
+ if (fstat(fd, &sb) != 0 || !S_ISREG(sb.st_mode)) {
+ krb5_set_error_message(context, ret,
+ "krb5_krb_tf_setup: tktfile %s is not a file",
+ path);
+ free(path);
+ close(fd);
+ return KRB5_FCC_PERM;
+ }
+
+ for (i = 0; i < KRB5_TF_LCK_RETRY_COUNT; i++) {
+ if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
+ sleep(KRB5_TF_LCK_RETRY);
+ } else
+ break;
+ }
+ if (i == KRB5_TF_LCK_RETRY_COUNT) {
+ krb5_set_error_message(context, KRB5_FCC_PERM,
+ "krb5_krb_tf_setup: failed to lock %s",
+ path);
+ free(path);
+ close(fd);
+ return KRB5_FCC_PERM;
+ }
+
+ if (!append) {
+ ret = ftruncate(fd, 0);
+ if (ret < 0) {
+ flock(fd, LOCK_UN);
+ krb5_set_error_message(context, KRB5_FCC_PERM,
+ "krb5_krb_tf_setup: failed to truncate %s",
+ path);
+ free(path);
+ close(fd);
+ return KRB5_FCC_PERM;
+ }
+ }
+ ret = lseek(fd, 0L, SEEK_END);
+ if (ret < 0) {
+ ret = errno;
+ flock(fd, LOCK_UN);
+ free(path);
+ close(fd);
+ return ret;
+ }
+
+ krb5_storage_to_data(sp, &data);
+
+ ret = write(fd, data.data, data.length);
+ if (ret != data.length)
+ ret = KRB5_CC_IO;
+
+ krb5_free_data_contents(context, &data);
+
+ flock(fd, LOCK_UN);
+ free(path);
+ close(fd);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_tf_setup(krb5_context context,
+ struct credentials *v4creds,
+ const char *tkfile,
+ int append)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_HOST);
+ krb5_storage_set_eof_code(sp, KRB5_CC_IO);
+
+ krb5_clear_error_string(context);
+
+ if (!append) {
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->pname), error);
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->pinst), error);
+ }
+
+ /* cred */
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->service), error);
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->instance), error);
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->realm), error);
+ ret = krb5_storage_write(sp, v4creds->session, 8);
+ if (ret != 8) {
+ ret = KRB5_CC_IO;
+ goto error;
+ }
+ RCHECK(ret, krb5_store_int32(sp, v4creds->lifetime), error);
+ RCHECK(ret, krb5_store_int32(sp, v4creds->kvno), error);
+ RCHECK(ret, krb5_store_int32(sp, v4creds->ticket_st.length), error);
+
+ ret = krb5_storage_write(sp, v4creds->ticket_st.dat,
+ v4creds->ticket_st.length);
+ if (ret != v4creds->ticket_st.length) {
+ ret = KRB5_CC_IO;
+ goto error;
+ }
+ RCHECK(ret, krb5_store_int32(sp, v4creds->issue_date), error);
+
+ ret = write_v4_cc(context, tkfile, sp, append);
+
+ error:
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_dest_tkt(krb5_context context, const char *tkfile)
+{
+ krb5_error_code ret;
+ char *path;
+
+ ret = get_krb4_cc_name(tkfile, &path);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "krb5_krb_tf_setup: failed getting "
+ "the krb4 credentials cache name");
+ return ret;
+ }
+
+ if (unlink(path) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "krb5_krb_dest_tkt failed removing the cache "
+ "with error %s", strerror(ret));
+ }
+ free(path);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+decrypt_etext(krb5_context context, const krb5_keyblock *key,
+ const krb5_data *cdata, krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, ETYPE_DES_PCBC_NONE, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decrypt(context, crypto, 0, cdata->data, cdata->length, data);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+static const char eightzeros[8] = "\x00\x00\x00\x00\x00\x00\x00\x00";
+
+static krb5_error_code
+storage_to_etext(krb5_context context,
+ krb5_storage *sp,
+ const krb5_keyblock *key,
+ krb5_data *enc_data)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_ssize_t size;
+ krb5_data data;
+
+ /* multiple of eight bytes, don't round up */
+
+ size = krb5_storage_seek(sp, 0, SEEK_END);
+ if (size < 0)
+ return KRB4ET_RD_AP_UNDEC;
+ size = ((size+7) & ~7) - size;
+
+ ret = krb5_storage_write(sp, eightzeros, size);
+ if (ret != size)
+ return KRB4ET_RD_AP_UNDEC;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, key, ETYPE_DES_PCBC_NONE, &crypto);
+ if (ret) {
+ krb5_data_free(&data);
+ return ret;
+ }
+
+ ret = krb5_encrypt(context, crypto, 0, data.data, data.length, enc_data);
+
+ krb5_data_free(&data);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+put_nir(krb5_storage *sp, const char *name,
+ const char *instance, const char *realm)
+{
+ krb5_error_code ret;
+
+ RCHECK(ret, krb5_store_stringz(sp, name), error);
+ RCHECK(ret, krb5_store_stringz(sp, instance), error);
+ if (realm) {
+ RCHECK(ret, krb5_store_stringz(sp, realm), error);
+ }
+ error:
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_create_ticket(krb5_context context,
+ unsigned char flags,
+ const char *pname,
+ const char *pinstance,
+ const char *prealm,
+ int32_t paddress,
+ const krb5_keyblock *session,
+ int16_t life,
+ int32_t life_sec,
+ const char *sname,
+ const char *sinstance,
+ const krb5_keyblock *key,
+ krb5_data *enc_data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(enc_data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_store_int8(sp, flags), error);
+ RCHECK(ret, put_nir(sp, pname, pinstance, prealm), error);
+ RCHECK(ret, krb5_store_int32(sp, ntohl(paddress)), error);
+
+ /* session key */
+ ret = krb5_storage_write(sp,
+ session->keyvalue.data,
+ session->keyvalue.length);
+ if (ret != session->keyvalue.length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ RCHECK(ret, krb5_store_int8(sp, life), error);
+ RCHECK(ret, krb5_store_int32(sp, life_sec), error);
+ RCHECK(ret, put_nir(sp, sname, sinstance, NULL), error);
+
+ ret = storage_to_etext(context, sp, key, enc_data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret, "Failed to encode kerberos 4 ticket");
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_create_ciph(krb5_context context,
+ const krb5_keyblock *session,
+ const char *service,
+ const char *instance,
+ const char *realm,
+ uint32_t life,
+ unsigned char kvno,
+ const krb5_data *ticket,
+ uint32_t kdc_time,
+ const krb5_keyblock *key,
+ krb5_data *enc_data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(enc_data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ /* session key */
+ ret = krb5_storage_write(sp,
+ session->keyvalue.data,
+ session->keyvalue.length);
+ if (ret != session->keyvalue.length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ RCHECK(ret, put_nir(sp, service, instance, realm), error);
+ RCHECK(ret, krb5_store_int8(sp, life), error);
+ RCHECK(ret, krb5_store_int8(sp, kvno), error);
+ RCHECK(ret, krb5_store_int8(sp, ticket->length), error);
+ ret = krb5_storage_write(sp, ticket->data, ticket->length);
+ if (ret != ticket->length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+ RCHECK(ret, krb5_store_int32(sp, kdc_time), error);
+
+ ret = storage_to_etext(context, sp, key, enc_data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret, "Failed to encode kerberos 4 ticket");
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_create_auth_reply(krb5_context context,
+ const char *pname,
+ const char *pinst,
+ const char *prealm,
+ int32_t time_ws,
+ int n,
+ uint32_t x_date,
+ unsigned char kvno,
+ const krb5_data *cipher,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_store_int8(sp, KRB_PROT_VERSION), error);
+ RCHECK(ret, krb5_store_int8(sp, AUTH_MSG_KDC_REPLY), error);
+ RCHECK(ret, put_nir(sp, pname, pinst, prealm), error);
+ RCHECK(ret, krb5_store_int32(sp, time_ws), error);
+ RCHECK(ret, krb5_store_int8(sp, n), error);
+ RCHECK(ret, krb5_store_int32(sp, x_date), error);
+ RCHECK(ret, krb5_store_int8(sp, kvno), error);
+ RCHECK(ret, krb5_store_int16(sp, cipher->length), error);
+ ret = krb5_storage_write(sp, cipher->data, cipher->length);
+ if (ret != cipher->length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ ret = krb5_storage_to_data(sp, data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret, "Failed to encode kerberos 4 ticket");
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_cr_err_reply(krb5_context context,
+ const char *name,
+ const char *inst,
+ const char *realm,
+ uint32_t time_ws,
+ uint32_t e,
+ const char *e_string,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(data);
+
+ if (name == NULL) name = "";
+ if (inst == NULL) inst = "";
+ if (realm == NULL) realm = "";
+ if (e_string == NULL) e_string = "";
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_store_int8(sp, KRB_PROT_VERSION), error);
+ RCHECK(ret, krb5_store_int8(sp, AUTH_MSG_ERR_REPLY), error);
+ RCHECK(ret, put_nir(sp, name, inst, realm), error);
+ RCHECK(ret, krb5_store_int32(sp, time_ws), error);
+ /* If it is a Kerberos 4 error-code, remove the et BASE */
+ if (e >= ERROR_TABLE_BASE_krb && e <= ERROR_TABLE_BASE_krb + 255)
+ e -= ERROR_TABLE_BASE_krb;
+ RCHECK(ret, krb5_store_int32(sp, e), error);
+ RCHECK(ret, krb5_store_stringz(sp, e_string), error);
+
+ ret = krb5_storage_to_data(sp, data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret, "Failed to encode kerberos 4 error");
+
+ return 0;
+}
+
+static krb5_error_code
+get_v4_stringz(krb5_storage *sp, char **str, size_t max_len)
+{
+ krb5_error_code ret;
+
+ ret = krb5_ret_stringz(sp, str);
+ if (ret)
+ return ret;
+ if (strlen(*str) > max_len) {
+ free(*str);
+ *str = NULL;
+ return KRB4ET_INTK_PROT;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_decomp_ticket(krb5_context context,
+ const krb5_data *enc_ticket,
+ const krb5_keyblock *key,
+ const char *local_realm,
+ char **sname,
+ char **sinstance,
+ struct _krb5_krb_auth_data *ad)
+{
+ krb5_error_code ret;
+ krb5_ssize_t size;
+ krb5_storage *sp = NULL;
+ krb5_data ticket;
+ unsigned char des_key[8];
+
+ memset(ad, 0, sizeof(*ad));
+ krb5_data_zero(&ticket);
+
+ *sname = NULL;
+ *sinstance = NULL;
+
+ RCHECK(ret, decrypt_etext(context, key, enc_ticket, &ticket), error);
+
+ sp = krb5_storage_from_data(&ticket);
+ if (sp == NULL) {
+ krb5_data_free(&ticket);
+ krb5_set_error_message(context, ENOMEM, "alloc: out of memory");
+ return ENOMEM;
+ }
+
+ krb5_storage_set_eof_code(sp, KRB4ET_INTK_PROT);
+
+ RCHECK(ret, krb5_ret_int8(sp, &ad->k_flags), error);
+ RCHECK(ret, get_v4_stringz(sp, &ad->pname, ANAME_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &ad->pinst, INST_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &ad->prealm, REALM_SZ), error);
+ RCHECK(ret, krb5_ret_uint32(sp, &ad->address), error);
+
+ size = krb5_storage_read(sp, des_key, sizeof(des_key));
+ if (size != sizeof(des_key)) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ RCHECK(ret, krb5_ret_uint8(sp, &ad->life), error);
+
+ if (ad->k_flags & 1)
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+ else
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_ret_uint32(sp, &ad->time_sec), error);
+
+ RCHECK(ret, get_v4_stringz(sp, sname, ANAME_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, sinstance, INST_SZ), error);
+
+ ret = krb5_keyblock_init(context, ETYPE_DES_PCBC_NONE,
+ des_key, sizeof(des_key), &ad->session);
+ if (ret)
+ goto error;
+
+ if (strlen(ad->prealm) == 0) {
+ free(ad->prealm);
+ ad->prealm = strdup(local_realm);
+ if (ad->prealm == NULL) {
+ ret = ENOMEM;
+ goto error;
+ }
+ }
+
+ error:
+ memset(des_key, 0, sizeof(des_key));
+ if (sp)
+ krb5_storage_free(sp);
+ krb5_data_free(&ticket);
+ if (ret) {
+ if (*sname) {
+ free(*sname);
+ *sname = NULL;
+ }
+ if (*sinstance) {
+ free(*sinstance);
+ *sinstance = NULL;
+ }
+ _krb5_krb_free_auth_data(context, ad);
+ krb5_set_error_message(context, ret, "Failed to decode v4 ticket");
+ }
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_rd_req(krb5_context context,
+ krb5_data *authent,
+ const char *service,
+ const char *instance,
+ const char *local_realm,
+ int32_t from_addr,
+ const krb5_keyblock *key,
+ struct _krb5_krb_auth_data *ad)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ krb5_data ticket, eaut, aut;
+ krb5_ssize_t size;
+ int little_endian;
+ int8_t pvno;
+ int8_t type;
+ int8_t s_kvno;
+ uint8_t ticket_length;
+ uint8_t eaut_length;
+ uint8_t time_5ms;
+ char *realm = NULL;
+ char *sname = NULL;
+ char *sinstance = NULL;
+ char *r_realm = NULL;
+ char *r_name = NULL;
+ char *r_instance = NULL;
+
+ uint32_t r_time_sec; /* Coarse time from authenticator */
+ unsigned long delta_t; /* Time in authenticator - local time */
+ long tkt_age; /* Age of ticket */
+
+ struct timeval tv;
+
+ krb5_data_zero(&ticket);
+ krb5_data_zero(&eaut);
+ krb5_data_zero(&aut);
+
+ sp = krb5_storage_from_data(authent);
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "alloc: out of memory");
+ return ENOMEM;
+ }
+
+ krb5_storage_set_eof_code(sp, KRB4ET_INTK_PROT);
+
+ ret = krb5_ret_int8(sp, &pvno);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed reading v4 pvno");
+ goto error;
+ }
+
+ if (pvno != KRB_PROT_VERSION) {
+ ret = KRB4ET_RD_AP_VERSION;
+ krb5_set_error_message(context, ret, "Failed v4 pvno not 4");
+ goto error;
+ }
+
+ ret = krb5_ret_int8(sp, &type);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed readin v4 type");
+ goto error;
+ }
+
+ little_endian = type & 1;
+ type &= ~1;
+
+ if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) {
+ ret = KRB4ET_RD_AP_MSG_TYPE;
+ krb5_set_error_message(context, ret, "Not a valid v4 request type");
+ goto error;
+ }
+
+ RCHECK(ret, krb5_ret_int8(sp, &s_kvno), error);
+ RCHECK(ret, get_v4_stringz(sp, &realm, REALM_SZ), error);
+ RCHECK(ret, krb5_ret_uint8(sp, &ticket_length), error);
+ RCHECK(ret, krb5_ret_uint8(sp, &eaut_length), error);
+ RCHECK(ret, krb5_data_alloc(&ticket, ticket_length), error);
+
+ size = krb5_storage_read(sp, ticket.data, ticket.length);
+ if (size != ticket.length) {
+ ret = KRB4ET_INTK_PROT;
+ krb5_set_error_message(context, ret, "Failed reading v4 ticket");
+ goto error;
+ }
+
+ /* Decrypt and take apart ticket */
+ ret = _krb5_krb_decomp_ticket(context, &ticket, key, local_realm,
+ &sname, &sinstance, ad);
+ if (ret)
+ goto error;
+
+ RCHECK(ret, krb5_data_alloc(&eaut, eaut_length), error);
+
+ size = krb5_storage_read(sp, eaut.data, eaut.length);
+ if (size != eaut.length) {
+ ret = KRB4ET_INTK_PROT;
+ krb5_set_error_message(context, ret, "Failed reading v4 authenticator");
+ goto error;
+ }
+
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ ret = decrypt_etext(context, &ad->session, &eaut, &aut);
+ if (ret)
+ goto error;
+
+ sp = krb5_storage_from_data(&aut);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "alloc: out of memory");
+ goto error;
+ }
+
+ if (little_endian)
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+ else
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, get_v4_stringz(sp, &r_name, ANAME_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &r_instance, INST_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &r_realm, REALM_SZ), error);
+
+ RCHECK(ret, krb5_ret_uint32(sp, &ad->checksum), error);
+ RCHECK(ret, krb5_ret_uint8(sp, &time_5ms), error);
+ RCHECK(ret, krb5_ret_uint32(sp, &r_time_sec), error);
+
+ if (strcmp(ad->pname, r_name) != 0 ||
+ strcmp(ad->pinst, r_instance) != 0 ||
+ strcmp(ad->prealm, r_realm) != 0) {
+ ret = KRB4ET_RD_AP_INCON;
+ krb5_set_error_message(context, ret, "v4 principal mismatch");
+ goto error;
+ }
+
+ if (from_addr && ad->address && from_addr != ad->address) {
+ ret = KRB4ET_RD_AP_BADD;
+ krb5_set_error_message(context, ret, "v4 bad address in ticket");
+ goto error;
+ }
+
+ gettimeofday(&tv, NULL);
+ delta_t = abs((int)(tv.tv_sec - r_time_sec));
+ if (delta_t > CLOCK_SKEW) {
+ ret = KRB4ET_RD_AP_TIME;
+ krb5_set_error_message(context, ret, "v4 clock skew");
+ goto error;
+ }
+
+ /* Now check for expiration of ticket */
+
+ tkt_age = tv.tv_sec - ad->time_sec;
+
+ if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) {
+ ret = KRB4ET_RD_AP_NYV;
+ krb5_set_error_message(context, ret, "v4 clock skew for expiration");
+ goto error;
+ }
+
+ if (tv.tv_sec > _krb5_krb_life_to_time(ad->time_sec, ad->life)) {
+ ret = KRB4ET_RD_AP_EXP;
+ krb5_set_error_message(context, ret, "v4 ticket expired");
+ goto error;
+ }
+
+ ret = 0;
+ error:
+ krb5_data_free(&ticket);
+ krb5_data_free(&eaut);
+ krb5_data_free(&aut);
+ if (realm)
+ free(realm);
+ if (sname)
+ free(sname);
+ if (sinstance)
+ free(sinstance);
+ if (r_name)
+ free(r_name);
+ if (r_instance)
+ free(r_instance);
+ if (r_realm)
+ free(r_realm);
+ if (sp)
+ krb5_storage_free(sp);
+
+ if (ret)
+ krb5_clear_error_string(context);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+void KRB5_LIB_FUNCTION
+_krb5_krb_free_auth_data(krb5_context context, struct _krb5_krb_auth_data *ad)
+{
+ if (ad->pname)
+ free(ad->pname);
+ if (ad->pinst)
+ free(ad->pinst);
+ if (ad->prealm)
+ free(ad->prealm);
+ krb5_free_keyblock_contents(context, &ad->session);
+ memset(ad, 0, sizeof(*ad));
+}
diff --git a/source4/heimdal/lib/krb5/version.c b/source4/heimdal/lib/krb5/version.c
new file mode 100644
index 0000000000..cbc4f8c3e1
--- /dev/null
+++ b/source4/heimdal/lib/krb5/version.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/* this is just to get a version stamp in the library file */
+
+#define heimdal_version __heimdal_version
+#define heimdal_long_version __heimdal_long_version
+#include "version.h"
+
diff --git a/source4/heimdal/lib/krb5/warn.c b/source4/heimdal/lib/krb5/warn.c
new file mode 100644
index 0000000000..c7fe5640b5
--- /dev/null
+++ b/source4/heimdal/lib/krb5/warn.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <err.h>
+
+RCSID("$Id$");
+
+static krb5_error_code _warnerr(krb5_context context, int do_errtext,
+ krb5_error_code code, int level, const char *fmt, va_list ap)
+ __attribute__((__format__(__printf__, 5, 0)));
+
+static krb5_error_code
+_warnerr(krb5_context context, int do_errtext,
+ krb5_error_code code, int level, const char *fmt, va_list ap)
+{
+ char xfmt[7] = "";
+ const char *args[2], **arg;
+ char *msg = NULL;
+ const char *err_str = NULL;
+
+ args[0] = args[1] = NULL;
+ arg = args;
+ if(fmt){
+ strlcat(xfmt, "%s", sizeof(xfmt));
+ if(do_errtext)
+ strlcat(xfmt, ": ", sizeof(xfmt));
+ vasprintf(&msg, fmt, ap);
+ if(msg == NULL)
+ return ENOMEM;
+ *arg++ = msg;
+ }
+ if(context && do_errtext){
+ const char *err_msg;
+
+ strlcat(xfmt, "%s", sizeof(xfmt));
+
+ err_str = krb5_get_error_message(context, code);
+ if (err_str != NULL) {
+ *arg++ = err_str;
+ } else {
+ err_msg = krb5_get_err_text(context, code);
+ if (err_msg)
+ *arg++ = err_msg;
+ else
+ *arg++ = "<unknown error>";
+ }
+ }
+
+ if(context && context->warn_dest)
+ krb5_log(context, context->warn_dest, level, xfmt, args[0], args[1]);
+ else
+ warnx(xfmt, args[0], args[1]);
+ free(msg);
+ krb5_free_error_message(context, err_str);
+ return 0;
+}
+
+#define FUNC(ETEXT, CODE, LEVEL) \
+ krb5_error_code ret; \
+ va_list ap; \
+ va_start(ap, fmt); \
+ ret = _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
+ va_end(ap);
+
+#undef __attribute__
+#define __attribute__(X)
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vwarn(krb5_context context, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((format (printf, 3, 0)))
+{
+ return _warnerr(context, 1, code, 1, fmt, ap);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_warn(krb5_context context, krb5_error_code code, const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)))
+{
+ FUNC(1, code, 1);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vwarnx(krb5_context context, const char *fmt, va_list ap)
+ __attribute__ ((format (printf, 2, 0)))
+{
+ return _warnerr(context, 0, 0, 1, fmt, ap);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_warnx(krb5_context context, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)))
+{
+ FUNC(0, 0, 1);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verr(krb5_context context, int eval, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 4, 0)))
+{
+ _warnerr(context, 1, code, 0, fmt, ap);
+ exit(eval);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_err(krb5_context context, int eval, krb5_error_code code,
+ const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 4, 5)))
+{
+ FUNC(1, code, 0);
+ exit(eval);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verrx(krb5_context context, int eval, const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 3, 0)))
+{
+ _warnerr(context, 0, 0, 0, fmt, ap);
+ exit(eval);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_errx(krb5_context context, int eval, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 3, 4)))
+{
+ FUNC(0, 0, 0);
+ exit(eval);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vabort(krb5_context context, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 3, 0)))
+{
+ _warnerr(context, 1, code, 0, fmt, ap);
+ abort();
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_abort(krb5_context context, krb5_error_code code, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 3, 4)))
+{
+ FUNC(1, code, 0);
+ abort();
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vabortx(krb5_context context, const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 2, 0)))
+{
+ _warnerr(context, 0, 0, 0, fmt, ap);
+ abort();
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_abortx(krb5_context context, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 2, 3)))
+{
+ FUNC(0, 0, 0);
+ abort();
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_warn_dest(krb5_context context, krb5_log_facility *fac)
+{
+ context->warn_dest = fac;
+ return 0;
+}
+
+krb5_log_facility * KRB5_LIB_FUNCTION
+krb5_get_warn_dest(krb5_context context)
+{
+ return context->warn_dest;
+}