aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Yu <tlyu@mit.edu>1997-12-06 08:00:17 +0000
committerTom Yu <tlyu@mit.edu>1997-12-06 08:00:17 +0000
commit32ab452e321b0d79d788c41c1b81bc039f9fe7fb (patch)
tree8dfbc45b401efd5e9e8ea5a498793fb7e95f2de4 /src
parent555d66fca2b801e59618eda5433cf4389eba60f9 (diff)
downloadkrb5-32ab452e321b0d79d788c41c1b81bc039f9fe7fb.zip
krb5-32ab452e321b0d79d788c41c1b81bc039f9fe7fb.tar.gz
krb5-32ab452e321b0d79d788c41c1b81bc039f9fe7fb.tar.bz2
* Makefile.in: Add files chpw.c, gic_*, preauth2.c, vfy_increds.c,
vic_opt.c. * chpw.c: New file; implement Cygnus chpw. * get_in_tkt.c: Implement support for Cygnus initial credentials API. * gic_keytab.c: New file; Cygnus initial creds. * gic_opt.c: New file; Cygnus initial creds. * gic_pwd.c: New file; Cygnus initial creds. * preauth.c: Add more SAM support (from Cygnus). * preauth2.c: New file; additional SAM support from Cygnus. * send_tgs.c: Account for additional parameter to sendto_kdc. * vfy_increds.c: New file; Cygnus initial creds. * vic_opt.c: New file; Cygnus initial creds. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@10321 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r--src/lib/krb5/krb/ChangeLog26
-rw-r--r--src/lib/krb5/krb/Makefile.in21
-rw-r--r--src/lib/krb5/krb/chpw.c221
-rw-r--r--src/lib/krb5/krb/get_in_tkt.c560
-rw-r--r--src/lib/krb5/krb/gic_keytab.c126
-rw-r--r--src/lib/krb5/krb/gic_opt.c85
-rw-r--r--src/lib/krb5/krb/gic_pwd.c317
-rw-r--r--src/lib/krb5/krb/preauth.c18
-rw-r--r--src/lib/krb5/krb/preauth2.c471
-rw-r--r--src/lib/krb5/krb/send_tgs.c2
-rw-r--r--src/lib/krb5/krb/vfy_increds.c238
-rw-r--r--src/lib/krb5/krb/vic_opt.c17
12 files changed, 2067 insertions, 35 deletions
diff --git a/src/lib/krb5/krb/ChangeLog b/src/lib/krb5/krb/ChangeLog
index 043f0f7..8c2d5e2 100644
--- a/src/lib/krb5/krb/ChangeLog
+++ b/src/lib/krb5/krb/ChangeLog
@@ -1,3 +1,29 @@
+Sat Dec 6 02:28:17 1997 Tom Yu <tlyu@mit.edu>
+
+ * Makefile.in: Add files chpw.c, gic_*, preauth2.c, vfy_increds.c,
+ vic_opt.c.
+
+ * chpw.c: New file; implement Cygnus chpw.
+
+ * get_in_tkt.c: Implement support for Cygnus initial credentials
+ API.
+
+ * gic_keytab.c: New file; Cygnus initial creds.
+
+ * gic_opt.c: New file; Cygnus initial creds.
+
+ * gic_pwd.c: New file; Cygnus initial creds.
+
+ * preauth.c: Add more SAM support (from Cygnus).
+
+ * preauth2.c: New file; additional SAM support from Cygnus.
+
+ * send_tgs.c: Account for additional parameter to sendto_kdc.
+
+ * vfy_increds.c: New file; Cygnus initial creds.
+
+ * vic_opt.c: New file; Cygnus initial creds.
+
Wed Oct 22 00:29:33 1997 Theodore Y. Ts'o <tytso@mit.edu>
* send_tgs.c (krb5_send_tgs): Don't send a zero endtime; if the
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index 8d08fce..87f93ea 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -16,6 +16,7 @@ STLIBOBJS= \
bld_pr_ext.o \
bld_princ.o \
chk_trans.o \
+ chpw.o \
conv_princ.o \
copy_addrs.o \
copy_auth.o \
@@ -39,6 +40,9 @@ STLIBOBJS= \
gen_subkey.o \
get_creds.o \
get_in_tkt.o \
+ gic_keytab.o \
+ gic_opt.o \
+ gic_pwd.o \
in_tkt_ktb.o \
in_tkt_pwd.o \
in_tkt_sky.o \
@@ -54,6 +58,7 @@ STLIBOBJS= \
parse.o \
pr_to_salt.o \
preauth.o \
+ preauth2.o \
princ_comp.o \
rd_cred.o \
rd_error.o \
@@ -81,6 +86,8 @@ STLIBOBJS= \
tgtname.o \
unparse.o \
valid_times.o \
+ vfy_increds.o \
+ vic_opt.o \
walk_rtree.o
OBJS= addr_comp.$(OBJEXT) \
@@ -90,6 +97,7 @@ OBJS= addr_comp.$(OBJEXT) \
bld_pr_ext.$(OBJEXT) \
bld_princ.$(OBJEXT) \
chk_trans.$(OBJEXT) \
+ chpw.$(OBJEXT) \
conv_princ.$(OBJEXT) \
copy_addrs.$(OBJEXT) \
copy_auth.$(OBJEXT) \
@@ -113,6 +121,9 @@ OBJS= addr_comp.$(OBJEXT) \
gen_subkey.$(OBJEXT) \
get_creds.$(OBJEXT) \
get_in_tkt.$(OBJEXT) \
+ gic_keytab.$(OBJEXT) \
+ gic_opt.$(OBJEXT) \
+ gic_pwd.$(OBJEXT) \
in_tkt_ktb.$(OBJEXT) \
in_tkt_pwd.$(OBJEXT) \
in_tkt_sky.$(OBJEXT) \
@@ -128,6 +139,7 @@ OBJS= addr_comp.$(OBJEXT) \
parse.$(OBJEXT) \
pr_to_salt.$(OBJEXT) \
preauth.$(OBJEXT) \
+ preauth2.$(OBJEXT) \
princ_comp.$(OBJEXT) \
rd_cred.$(OBJEXT) \
rd_error.$(OBJEXT) \
@@ -155,6 +167,8 @@ OBJS= addr_comp.$(OBJEXT) \
tgtname.$(OBJEXT) \
unparse.$(OBJEXT) \
valid_times.$(OBJEXT) \
+ vfy_increds.$(OBJEXT) \
+ vic_opt.$(OBJEXT) \
walk_rtree.$(OBJEXT)
SRCS= $(srcdir)/addr_comp.c \
@@ -165,6 +179,7 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/bld_princ.c \
$(srcdir)/brand.c \
$(srcdir)/chk_trans.c \
+ $(srcdir)/chpw.c \
$(srcdir)/conv_princ.c \
$(srcdir)/copy_addrs.c \
$(srcdir)/copy_auth.c \
@@ -188,6 +203,9 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/gen_subkey.c \
$(srcdir)/get_creds.c \
$(srcdir)/get_in_tkt.c \
+ $(srcdir)/gic_keytab.c \
+ $(srcdir)/gic_opt.c \
+ $(srcdir)/gic_pwd.c \
$(srcdir)/in_tkt_ktb.c \
$(srcdir)/in_tkt_pwd.c \
$(srcdir)/in_tkt_sky.c \
@@ -203,6 +221,7 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/parse.c \
$(srcdir)/pr_to_salt.c \
$(srcdir)/preauth.c \
+ $(srcdir)/preauth2.c \
$(srcdir)/princ_comp.c \
$(srcdir)/rd_cred.c \
$(srcdir)/rd_error.c \
@@ -230,6 +249,8 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/tgtname.c \
$(srcdir)/unparse.c \
$(srcdir)/valid_times.c \
+ $(srcdir)/vfy_increds.c \
+ $(srcdir)/vic_opt.c \
$(srcdir)/walk_rtree.c
##DOS##LIBOBJS = $(OBJS)
diff --git a/src/lib/krb5/krb/chpw.c b/src/lib/krb5/krb/chpw.c
new file mode 100644
index 0000000..3266f4f
--- /dev/null
+++ b/src/lib/krb5/krb/chpw.c
@@ -0,0 +1,221 @@
+#include <string.h>
+
+#include "k5-int.h"
+#include "krb5_err.h"
+#include "auth_con.h"
+
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_mk_chpw_req(context, auth_context, ap_req, passwd, packet)
+ krb5_context context;
+ krb5_auth_context auth_context;
+ krb5_data *ap_req;
+ char *passwd;
+ krb5_data *packet;
+{
+ krb5_error_code ret;
+ krb5_data clearpw;
+ krb5_data cipherpw;
+ krb5_replay_data replay;
+ char *ptr;
+
+ if (ret = krb5_auth_con_setflags(context, auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE))
+ return(ret);
+
+ clearpw.length = strlen(passwd);
+ clearpw.data = passwd;
+
+ if (ret = krb5_mk_priv(context, auth_context,
+ &clearpw, &cipherpw, &replay))
+ return(ret);
+
+ packet->length = 6 + ap_req->length + cipherpw.length;
+ packet->data = (char *) malloc(packet->length);
+ ptr = packet->data;
+
+ /* length */
+
+ *ptr++ = (packet->length>>8) & 0xff;
+ *ptr++ = packet->length & 0xff;
+
+ /* version == 0x0001 big-endian */
+
+ *ptr++ = 0;
+ *ptr++ = 1;
+
+ /* ap_req length, big-endian */
+
+ *ptr++ = (ap_req->length>>8) & 0xff;
+ *ptr++ = ap_req->length & 0xff;
+
+ /* ap-req data */
+
+ memcpy(ptr, ap_req->data, ap_req->length);
+ ptr += ap_req->length;
+
+ /* krb-priv of password */
+
+ memcpy(ptr, cipherpw.data, cipherpw.length);
+
+ return(0);
+}
+
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_rd_chpw_rep(context, auth_context, packet, result_code, result_data)
+ krb5_context context;
+ krb5_auth_context auth_context;
+ krb5_data *packet;
+ int *result_code;
+ krb5_data *result_data;
+{
+ char *ptr;
+ int plen, vno;
+ krb5_data ap_rep;
+ krb5_ap_rep_enc_part *ap_rep_enc;
+ krb5_error_code ret;
+ krb5_data cipherresult;
+ krb5_data clearresult;
+ krb5_error *krberror;
+ krb5_replay_data replay;
+ krb5_keyblock *tmp;
+
+ if (packet->length < 4)
+ /* either this, or the server is printing bad messages,
+ or the caller passed in garbage */
+ return(KRB5KRB_AP_ERR_MODIFIED);
+
+ ptr = packet->data;
+
+ /* verify length */
+
+ plen = (*ptr++ & 0xff);
+ plen = (plen<<8) | (*ptr++ & 0xff);
+
+ if (plen != packet->length)
+ return(KRB5KRB_AP_ERR_MODIFIED);
+
+ /* verify version number */
+
+ vno = (*ptr++ & 0xff);
+ vno = (vno<<8) | (*ptr++ & 0xff);
+
+ if (vno != 1)
+ return(KRB5KDC_ERR_BAD_PVNO);
+
+ /* read, check ap-rep length */
+
+ ap_rep.length = (*ptr++ & 0xff);
+ ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
+
+ if (ptr + ap_rep.length >= packet->data + packet->length)
+ return(KRB5KRB_AP_ERR_MODIFIED);
+
+ if (ap_rep.length) {
+ /* verify ap_rep */
+ ap_rep.data = ptr;
+ ptr += ap_rep.length;
+
+ if (ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc))
+ return(ret);
+
+ krb5_free_ap_rep_enc_part(context, ap_rep_enc);
+
+ /* extract and decrypt the result */
+
+ cipherresult.data = ptr;
+ cipherresult.length = (packet->data + packet->length) - ptr;
+
+ /* XXX there's no api to do this right. The problem is that
+ if there's a remote subkey, it will be used. This is
+ not what the spec requires */
+
+ tmp = auth_context->remote_subkey;
+ auth_context->remote_subkey = NULL;
+
+ ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
+ &replay);
+
+ auth_context->remote_subkey = tmp;
+
+ if (ret)
+ return(ret);
+ } else {
+ cipherresult.data = ptr;
+ cipherresult.length = (packet->data + packet->length) - ptr;
+
+ if (ret = krb5_rd_error(context, &cipherresult, &krberror))
+ return(ret);
+
+ clearresult = krberror->e_data;
+ }
+
+ if (clearresult.length < 2) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto cleanup;
+ }
+
+ ptr = clearresult.data;
+
+ *result_code = (*ptr++ & 0xff);
+ *result_code = (*result_code<<8) | (*ptr++ & 0xff);
+
+ if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
+ (*result_code > KRB5_KPASSWD_SOFTERROR)) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto cleanup;
+ }
+
+ /* all success replies should be authenticated/encrypted */
+
+ if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto cleanup;
+ }
+
+ result_data->length = (clearresult.data + clearresult.length) - ptr;
+
+ if (result_data->length) {
+ result_data->data = (char *) malloc(result_data->length);
+ memcpy(result_data->data, ptr, result_data->length);
+ } else {
+ result_data->data = NULL;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (ap_rep.length) {
+ krb5_xfree(clearresult.data);
+ } else {
+ krb5_free_error(context, krberror);
+ }
+
+ return(ret);
+}
+
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_chpw_result_code_string(context, result_code, code_string)
+ krb5_context context;
+ int result_code;
+ char **code_string;
+{
+ switch (result_code) {
+ case KRB5_KPASSWD_MALFORMED:
+ *code_string = "Malformed request error";
+ break;
+ case KRB5_KPASSWD_HARDERROR:
+ *code_string = "Server error";
+ break;
+ case KRB5_KPASSWD_AUTHERROR:
+ *code_string = "Authentication error";
+ break;
+ case KRB5_KPASSWD_SOFTERROR:
+ *code_string = "Password change rejected";
+ break;
+ default:
+ *code_string = "Password change failed";
+ break;
+ }
+
+ return(0);
+}
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 1314690..cbcbc72 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -24,6 +24,8 @@
* krb5_get_in_tkt()
*/
+#include <string.h>
+
#include "k5-int.h"
/*
@@ -74,12 +76,14 @@ typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((krb5_context,
* unexpected response, an error is returned.
*/
static krb5_error_code
-send_as_request(context, request, time_now, ret_err_reply, ret_as_reply)
+send_as_request(context, request, time_now, ret_err_reply, ret_as_reply,
+ master)
krb5_context context;
krb5_kdc_req *request;
krb5_timestamp *time_now;
krb5_error ** ret_err_reply;
krb5_kdc_rep ** ret_as_reply;
+ int * master;
{
krb5_kdc_rep *as_reply = 0;
krb5_error_code retval;
@@ -104,7 +108,8 @@ send_as_request(context, request, time_now, ret_err_reply, ret_as_reply)
k4_version = packet->data[0];
retval = krb5_sendto_kdc(context, packet,
- krb5_princ_realm(context, request->client), &reply);
+ krb5_princ_realm(context, request->client),
+ &reply, master);
krb5_free_data(context, packet);
if (retval)
goto cleanup;
@@ -233,7 +238,8 @@ verify_as_reply(context, time_now, request, as_reply)
|| (request->nonce != as_reply->enc_part2->nonce)
/* XXX check for extraneous flags */
/* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
- || ((request->from != 0) &&
+ || ((request->kdc_options & KDC_OPT_POSTDATED) &&
+ (request->from != 0) &&
(request->from != as_reply->enc_part2->times.starttime))
|| ((request->till != 0) &&
(as_reply->enc_part2->times.endtime > request->till))
@@ -272,14 +278,26 @@ stash_as_reply(context, time_now, request, as_reply, creds, ccache)
{
krb5_error_code retval;
krb5_data * packet;
+ krb5_principal client;
+ krb5_principal server;
+
+ client = NULL;
+ server = NULL;
+
+ if (!creds->client)
+ if (retval = krb5_copy_principal(context, as_reply->client, &client))
+ goto cleanup;
+
+ if (!creds->server)
+ if (retval = krb5_copy_principal(context, as_reply->enc_part2->server,
+ &server))
+ goto cleanup;
- /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
-
/* fill in the credentials */
if ((retval = krb5_copy_keyblock_contents(context,
as_reply->enc_part2->session,
&creds->keyblock)))
- return (retval);
+ goto cleanup;
creds->times = as_reply->enc_part2->times;
creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
@@ -299,13 +317,21 @@ stash_as_reply(context, time_now, request, as_reply, creds, ccache)
krb5_xfree(packet);
/* store it in the ccache! */
- if (ccache) {
- if ((retval = krb5_cc_store_cred(context, ccache, creds)))
- goto cleanup;
- }
+ if (ccache)
+ if ((retval = krb5_cc_store_cred(context, ccache, creds)))
+ goto cleanup;
+
+ if (!creds->client)
+ creds->client = client;
+ if (!creds->server)
+ creds->server = server;
cleanup:
if (retval) {
+ if (client)
+ krb5_free_principal(context, client);
+ if (server)
+ krb5_free_principal(context, server);
if (creds->keyblock.contents) {
memset((char *)creds->keyblock.contents, 0,
creds->keyblock.length);
@@ -326,9 +352,10 @@ cleanup:
}
static krb5_error_code
-make_preauth_list(context, ptypes, ret_list)
+make_preauth_list(context, ptypes, nptypes, ret_list)
krb5_context context;
krb5_preauthtype * ptypes;
+ int nptypes;
krb5_pa_data *** ret_list;
{
krb5_preauthtype * ptypep;
@@ -336,26 +363,36 @@ make_preauth_list(context, ptypes, ret_list)
krb5_pa_data ** preauth_to_use;
int i;
- for (i=1, ptypep = ptypes; *ptypep; ptypep++, i++)
- ;
- preauth_to_use = malloc(i * sizeof(krb5_pa_data *));
- if (preauth_to_use == NULL)
- return (ENOMEM);
- for (preauthp = preauth_to_use, ptypep = ptypes;
- *ptypep;
- preauthp++, ptypep++) {
- *preauthp = malloc(sizeof(krb5_pa_data));
- if (*preauthp == NULL) {
- krb5_free_pa_data(context, preauth_to_use);
+ if (nptypes < 0) {
+ for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
+ ;
+ }
+
+ /* allocate space for a NULL to terminate the list */
+
+ if ((preauthp =
+ (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
+ return(ENOMEM);
+
+ for (i=0; i<nptypes; i++) {
+ if ((preauthp[i] =
+ (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
+ for (; i>=0; i++)
+ free(preauthp[i]);
+ free(preauthp);
return (ENOMEM);
}
- (*preauthp)->magic = KV5M_PA_DATA;
- (*preauthp)->pa_type = *ptypep;
- (*preauthp)->length = 0;
- (*preauthp)->contents = 0;
+ preauthp[i]->magic = KV5M_PA_DATA;
+ preauthp[i]->pa_type = ptypes[i];
+ preauthp[i]->length = 0;
+ preauthp[i]->contents = 0;
}
- *preauthp = NULL;
- *ret_list = preauth_to_use;
+
+ /* fill in the terminating NULL */
+
+ preauthp[nptypes] = NULL;
+
+ *ret_list = preauthp;
return 0;
}
@@ -450,7 +487,7 @@ krb5_get_in_tkt(context, options, addrs, ktypes, ptypes, key_proc, keyseed,
err_reply = 0;
as_reply = 0;
if ((retval = send_as_request(context, &request, &time_now, &err_reply,
- &as_reply)))
+ &as_reply, NULL)))
goto cleanup;
if (err_reply) {
@@ -505,7 +542,7 @@ cleanup:
if (preauth_to_use)
krb5_free_pa_data(context, preauth_to_use);
if (decrypt_key)
- krb5_free_keyblock(context, decrypt_key);
+ krb5_free_keyblock(context, decrypt_key);
if (as_reply) {
if (ret_as_reply)
*ret_as_reply = as_reply;
@@ -514,3 +551,466 @@ cleanup:
}
return (retval);
}
+
+/* begin libdefaults parsing code. This should almost certainly move
+ somewhere else, but I don't know where the correct somewhere else
+ is yet. */
+
+/* XXX Duplicating this is annoying; try to work on a better way.*/
+static char *conf_yes[] = {
+ "y", "yes", "true", "t", "1", "on",
+ 0,
+};
+
+static char *conf_no[] = {
+ "n", "no", "false", "nil", "0", "off",
+ 0,
+};
+
+static int conf_boolean(s)
+ char *s;
+{
+ char **p;
+
+ for(p=conf_yes; *p; p++) {
+ if (!strcasecmp(*p,s))
+ return 1;
+ }
+
+ for(p=conf_no; *p; p++) {
+ if (!strcasecmp(*p,s))
+ return 0;
+ }
+
+ /* Default to "no" */
+ return 0;
+}
+
+static krb5_error_code
+krb5_appdefault_string(context, realm, option, ret_value)
+ krb5_context context;
+ const krb5_data *realm;
+ const char *option;
+ char **ret_value;
+{
+ profile_t profile;
+ const char *names[5];
+ char **nameval = NULL;
+ krb5_error_code retval;
+ char realmstr[1024];
+ char **cpp;
+
+ if (realm->length > sizeof(realmstr)-1)
+ return(EINVAL);
+
+ strncpy(realmstr, realm->data, realm->length);
+ realmstr[realm->length] = '\0';
+
+ if (!context || (context->magic != KV5M_CONTEXT))
+ return KV5M_CONTEXT;
+
+ profile = context->profile;
+
+ names[0] = "libdefaults";
+
+ /*
+ * Try number one:
+ *
+ * [appdefaults]
+ * REALM = {
+ * option = <boolean>
+ * }
+ */
+
+ names[1] = realmstr;
+ names[2] = option;
+ names[3] = 0;
+ retval = profile_get_values(profile, names, &nameval);
+ if (retval == 0 && nameval && nameval[0])
+ goto goodbye;
+
+ /*
+ * Try number two:
+ *
+ * [appdefaults]
+ * option = <boolean>
+ */
+
+ names[1] = option;
+ names[2] = 0;
+ retval = profile_get_values(profile, names, &nameval);
+ if (retval == 0 && nameval && nameval[0])
+ goto goodbye;
+
+goodbye:
+ if (!nameval)
+ return(ENOENT);
+
+ if (!nameval[0]) {
+ free(nameval);
+ return(ENOENT);
+ }
+
+ *ret_value = nameval[0];
+
+ for (cpp = &nameval[1]; *cpp; cpp++)
+ free(*cpp);
+
+ free(nameval);
+
+ return 0;
+}
+
+/* not static so verify_init_creds() can call it */
+
+krb5_error_code
+krb5_appdefault_boolean(context, realm, option, ret_value)
+ krb5_context context;
+ const char *option;
+ const krb5_data *realm;
+ int *ret_value;
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = krb5_appdefault_string(context, realm, option, &string);
+
+ if (retval)
+ return(retval);
+
+ *ret_value = conf_boolean(string);
+ free(string);
+
+ return(0);
+}
+
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds(context, creds, client, prompter, prompter_data,
+ start_time, in_tkt_service, options, gak_fct, gak_data,
+ master, as_reply)
+ krb5_context context;
+ krb5_creds *creds;
+ krb5_principal client;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+ krb5_deltat start_time;
+ char *in_tkt_service;
+ krb5_get_init_creds_opt *options;
+ krb5_gic_get_as_key_fct gak_fct;
+ void *gak_data;
+ int *master;
+ krb5_kdc_rep **as_reply;
+{
+ krb5_error_code ret;
+ krb5_kdc_req request;
+ krb5_pa_data **padata;
+ int tempint;
+ char *tempstr;
+ krb5_deltat renew_life;
+ int loopcount;
+ krb5_data salt;
+ krb5_keyblock as_key;
+ krb5_error *err_reply;
+ krb5_kdc_rep *local_as_reply;
+ krb5_timestamp time_now;
+
+ /* initialize everything which will be freed at cleanup */
+
+ request.server = NULL;
+ request.ktype = NULL;
+ request.addresses = NULL;
+ request.padata = NULL;
+ padata = NULL;
+ as_key.length = 0;
+ salt.length = 0;
+ salt.data = NULL;
+
+ /*
+ * Set up the basic request structure
+ */
+ request.magic = KV5M_KDC_REQ;
+ request.msg_type = KRB5_AS_REQ;
+
+ /* request.padata is filled in later */
+
+ request.kdc_options = 0;
+
+ /* forwardable */
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE))
+ tempint = options->forwardable;
+ else if ((ret = krb5_appdefault_boolean(context, &client->realm,
+ "forwardable", &tempint)) == 0)
+ ;
+ else
+ tempint = 0;
+ if (tempint)
+ request.kdc_options |= KDC_OPT_FORWARDABLE;
+
+ /* proxiable */
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE))
+ tempint = options->proxiable;
+ else if ((ret = krb5_appdefault_boolean(context, &client->realm,
+ "proxiable", &tempint)) == 0)
+ ;
+ else
+ tempint = 0;
+ if (tempint)
+ request.kdc_options |= KDC_OPT_PROXIABLE;
+
+ /* renewable */
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
+ renew_life = options->renew_life;
+ } else if ((ret = krb5_appdefault_string(context, &client->realm,
+ "renew_lifetime", &tempstr))
+ == 0) {
+ if (ret = krb5_string_to_deltat(tempstr, &renew_life)) {
+ free(tempstr);
+ goto cleanup;
+ }
+ } else {
+ renew_life = 0;
+ }
+ if (renew_life > 0)
+ request.kdc_options |= KDC_OPT_RENEWABLE;
+
+ /* allow_postdate */
+
+ if (start_time > 0)
+ request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED);
+
+ /* client */
+
+ request.client = client;
+
+ if (in_tkt_service) {
+ /* this is ugly, because so are the data structures involved. I'm
+ in the library, so I'm going to manipulate the data structures
+ directly, otherwise, it will be worse. */
+
+ if (ret = krb5_parse_name(context, in_tkt_service, &request.server))
+ goto cleanup;
+
+ /* stuff the client realm into the server principal.
+ realloc if necessary */
+ if (request.server->realm.length < request.client->realm.length)
+ if ((request.server->realm.data =
+ (char *) realloc(request.server->realm.data,
+ request.client->realm.length)) == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+
+ request.server->realm.length = request.client->realm.length;
+ memcpy(request.server->realm.data, request.client->realm.data,
+ request.client->realm.length);
+ } else {
+ if (ret = krb5_build_principal_ext(context, &request.server,
+ request.client->realm.length,
+ request.client->realm.data,
+ KRB5_TGS_NAME_SIZE,
+ KRB5_TGS_NAME,
+ request.client->realm.length,
+ request.client->realm.data,
+ 0))
+ goto cleanup;
+ }
+
+ if (ret = krb5_timeofday(context, &request.from))
+ goto cleanup;
+ request.from += start_time;
+
+ request.till = request.from;
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE))
+ request.till += options->tkt_life;
+ else
+ request.till += 10*60*60; /* this used to be hardcoded in kinit.c */
+
+ if (renew_life > 0) {
+ request.rtime = request.from;
+ request.rtime += renew_life;
+ } else {
+ request.rtime = 0;
+ }
+
+ /* nonce is filled in by send_as_request */
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
+ request.ktype = options->etype_list;
+ request.nktypes = options->etype_list_length;
+ } else if ((ret = krb5_get_default_in_tkt_ktypes(context,
+ &request.ktype)) == 0) {
+ for (request.nktypes = 0;
+ request.ktype[request.nktypes];
+ request.nktypes++)
+ ;
+ } else {
+ /* there isn't any useful default here. ret is set from above */
+ goto cleanup;
+ }
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST))
+ request.addresses = options->address_list;
+ else
+ if ((ret = krb5_os_localaddr(context, &request.addresses)))
+ goto cleanup;
+
+ request.authorization_data.ciphertext.length = 0;
+ request.authorization_data.ciphertext.data = 0;
+ request.unenc_authdata = 0;
+ request.second_ticket = 0;
+
+ /* set up the other state. */
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
+ if (ret = make_preauth_list(context, options->preauth_list,
+ options->preauth_list_length,
+ &padata))
+ goto cleanup;
+ }
+
+ /* the salt is allocated from somewhere, unless it is from the caller,
+ then it is a reference */
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) {
+ salt = *options->salt;
+ } else {
+ salt.length = -1;
+ salt.data = NULL;
+ }
+
+ /* now, loop processing preauth data and talking to the kdc */
+
+ for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
+ if (request.padata) {
+ krb5_free_pa_data(context, request.padata);
+ request.padata = NULL;
+ }
+
+ if (ret = krb5_do_preauth(context, &request,
+ padata, &request.padata,
+ &salt, &as_key, prompter,
+ prompter_data, gak_fct, gak_data))
+ goto cleanup;
+
+ if (padata) {
+ krb5_free_pa_data(context, padata);
+ padata = 0;
+ }
+
+ err_reply = 0;
+ local_as_reply = 0;
+ if ((ret = send_as_request(context, &request, &time_now, &err_reply,
+ &local_as_reply, master)))
+ goto cleanup;
+
+ if (err_reply) {
+ if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
+ err_reply->e_data.length > 0) {
+ ret = decode_krb5_padata_sequence(&err_reply->e_data,
+ &padata);
+ krb5_free_error(context, err_reply);
+ if (ret)
+ goto cleanup;
+ } else {
+ ret = err_reply->error + ERROR_TABLE_BASE_krb5;
+ krb5_free_error(context, err_reply);
+ goto cleanup;
+ }
+ } else if (local_as_reply) {
+ break;
+ } else {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ goto cleanup;
+ }
+ }
+
+ if (loopcount == MAX_IN_TKT_LOOPS) {
+ ret = KRB5_GET_IN_TKT_LOOP;
+ goto cleanup;
+ }
+
+ /* process any preauth data in the as_reply */
+
+ if (ret = krb5_do_preauth(context, &request,
+ local_as_reply->padata, &padata,
+ &salt, &as_key, prompter,
+ prompter_data, gak_fct, gak_data))
+ goto cleanup;
+
+ /* XXX if there's padata on output, something is wrong, but it's
+ not obviously an error */
+
+ /* XXX because etypes are handled poorly (particularly wrt SAM,
+ where the etype is fixed by the kdc), we may want to try
+ decrypt_as_reply twice. If there's an as_key available, try
+ it. If decrypting the as_rep fails, or if there isn't an
+ as_key at all yet, then use the gak_fct to get one, and try
+ again. */
+
+ if (as_key.length)
+ ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
+ NULL, &as_key, krb5_kdc_rep_decrypt_proc,
+ NULL);
+ else
+ ret = -1;
+
+ if (ret) {
+ /* if we haven't get gotten a key, get it now */
+
+ if (ret = ((*gak_fct)(context, request.client,
+ local_as_reply->enc_part.enctype,
+ prompter, prompter_data, &salt,
+ &as_key, gak_data)))
+ goto cleanup;
+
+ if (ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
+ NULL, &as_key, krb5_kdc_rep_decrypt_proc,
+ NULL))
+ goto cleanup;
+ }
+
+ if (ret = verify_as_reply(context, time_now, &request, local_as_reply))
+ goto cleanup;
+
+ /* XXX this should be inside stash_as_reply, but as long as
+ get_in_tkt is still around using that arg as an in/out, I can't
+ do that */
+ memset(creds, 0, sizeof(*creds));
+
+ if (ret = stash_as_reply(context, time_now, &request, local_as_reply,
+ creds, NULL))
+ goto cleanup;
+
+ /* success */
+
+ ret = 0;
+
+cleanup:
+ if (request.server)
+ krb5_free_principal(context, request.server);
+ if (request.ktype &&
+ (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))))
+ free(request.ktype);
+ if (request.addresses &&
+ (!(options &&
+ (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST))))
+ krb5_free_addresses(context, request.addresses);
+ if (padata)
+ krb5_free_pa_data(context, padata);
+ if (request.padata)
+ krb5_free_pa_data(context, request.padata);
+ if (as_key.length)
+ krb5_free_keyblock_contents(context, &as_key);
+ if (salt.data &&
+ (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT))))
+ krb5_xfree(salt.data);
+ if (as_reply)
+ *as_reply = local_as_reply;
+ else if (local_as_reply)
+ krb5_free_kdc_rep(context, local_as_reply);
+
+ return(ret);
+}
diff --git a/src/lib/krb5/krb/gic_keytab.c b/src/lib/krb5/krb/gic_keytab.c
new file mode 100644
index 0000000..8b6f231
--- /dev/null
+++ b/src/lib/krb5/krb/gic_keytab.c
@@ -0,0 +1,126 @@
+#include "k5-int.h"
+
+static krb5_error_code
+krb5_get_as_key_keytab(context, client, etype, prompter, prompter_data,
+ salt, as_key, gak_data)
+ krb5_context context;
+ krb5_principal client;
+ krb5_enctype etype;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+ krb5_data *salt;
+ krb5_keyblock *as_key;
+ void *gak_data;
+{
+ krb5_keytab keytab = (krb5_keytab) gak_data;
+ krb5_error_code ret;
+ krb5_keytab_entry kt_ent;
+ krb5_keyblock *kt_key;
+
+ /* if there's already a key of the correct etype, we're done.
+ if the etype is wrong, free the existing key, and make
+ a new one. */
+
+ if (as_key->length) {
+ if (as_key->enctype == etype)
+ return(0);
+
+ krb5_free_keyblock(context, as_key);
+ as_key->length = 0;
+ }
+
+ if (!valid_enctype(etype))
+ return(KRB5_PROG_ETYPE_NOSUPP);
+
+ if ((ret = krb5_kt_get_entry(context, keytab, client,
+ 0, /* don't have vno available */
+ etype, &kt_ent)))
+ return(ret);
+
+ ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key);
+
+ /* again, krb5's memory management is lame... */
+
+ *as_key = *kt_key;
+ krb5_xfree(kt_key);
+
+ (void) krb5_kt_free_entry(context, &kt_ent);
+
+ return(ret);
+}
+
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_keytab(context, creds, client, arg_keytab,
+ start_time, in_tkt_service, options)
+ krb5_context context;
+ krb5_creds *creds;
+ krb5_principal client;
+ krb5_keytab arg_keytab;
+ krb5_deltat start_time;
+ char *in_tkt_service;
+ krb5_get_init_creds_opt *options;
+{
+ krb5_error_code ret, ret2;
+ int master;
+ krb5_keytab keytab;
+
+ if (arg_keytab == NULL) {
+ if (ret = krb5_kt_default(context, &keytab))
+ return ret;
+ } else {
+ keytab = arg_keytab;
+ }
+
+ master = 0;
+
+ /* first try: get the requested tkt from any kdc */
+
+ ret = krb5_get_init_creds(context, creds, client, NULL, NULL,
+ start_time, in_tkt_service, options,
+ krb5_get_as_key_keytab, (void *) keytab,
+ &master, NULL);
+
+ /* check for success */
+
+ if (ret == 0)
+ goto cleanup;
+
+ /* If all the kdc's are unavailable fail */
+
+ if (ret == KRB5_KDC_UNREACH)
+ goto cleanup;
+
+ /* if the reply did not come from the master kdc, try again with
+ the master kdc */
+
+ if (!master) {
+ master = 1;
+
+ ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL,
+ start_time, in_tkt_service, options,
+ krb5_get_as_key_keytab, (void *) keytab,
+ &master, NULL);
+
+ if (ret2 == 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ /* if the master is unreachable, return the error from the
+ slave we were able to contact */
+
+ if (ret2 == KRB5_KDC_UNREACH)
+ goto cleanup;
+
+ ret = ret2;
+ }
+
+ /* at this point, we have a response from the master. Since we don't
+ do any prompting or changing for keytabs, that's it. */
+
+cleanup:
+ if (arg_keytab == NULL)
+ krb5_kt_close(context, keytab);
+
+ return(ret);
+}
diff --git a/src/lib/krb5/krb/gic_opt.c b/src/lib/krb5/krb/gic_opt.c
new file mode 100644
index 0000000..eb04f51
--- /dev/null
+++ b/src/lib/krb5/krb/gic_opt.c
@@ -0,0 +1,85 @@
+#include "k5-int.h"
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_init(opt)
+ krb5_get_init_creds_opt *opt;
+{
+ opt->flags = 0;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_tkt_life(opt, 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;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_renew_life(opt, 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;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_forwardable(opt, forwardable)
+ krb5_get_init_creds_opt *opt;
+ int forwardable;
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE;
+ opt->forwardable = forwardable;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_proxiable(opt, proxiable)
+ krb5_get_init_creds_opt *opt;
+ int proxiable;
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE;
+ opt->proxiable = proxiable;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_etype_list(opt, etype_list, etype_list_length)
+ 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;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_address_list(opt, addresses)
+ krb5_get_init_creds_opt *opt;
+ krb5_address **addresses;
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST;
+ opt->address_list = addresses;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_preauth_list(opt, preauth_list,
+ preauth_list_length)
+ 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 = preauth_list;
+ opt->preauth_list_length = preauth_list_length;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_salt(opt, salt)
+ krb5_get_init_creds_opt *opt;
+ krb5_data *salt;
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT;
+ opt->salt = salt;
+}
diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c
new file mode 100644
index 0000000..5ab3280
--- /dev/null
+++ b/src/lib/krb5/krb/gic_pwd.c
@@ -0,0 +1,317 @@
+#include "k5-int.h"
+#include "com_err.h"
+
+static krb5_error_code
+krb5_get_as_key_password(context, client, etype, prompter, prompter_data,
+ salt, as_key, gak_data)
+ krb5_context context;
+ krb5_principal client;
+ krb5_enctype etype;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+ krb5_data *salt;
+ krb5_keyblock *as_key;
+ void *gak_data;
+{
+ krb5_data *password;
+ krb5_error_code ret;
+ krb5_data defsalt;
+ krb5_encrypt_block eblock;
+ char *clientstr;
+ char promptstr[1024];
+ krb5_prompt prompt;
+
+ password = (krb5_data *) gak_data;
+
+ /* if there's already a key of the correct etype, we're done.
+ if the etype is wrong, free the existing key, and make
+ a new one. */
+
+ if (as_key->length) {
+ if (as_key->enctype == etype)
+ return(0);
+
+ krb5_free_keyblock_contents(context, as_key);
+ as_key->length = 0;
+ }
+
+ if (!valid_enctype(etype))
+ return(KRB5_PROG_ETYPE_NOSUPP);
+
+ krb5_use_enctype(context, &eblock, etype);
+
+ if (password->data[0] == '\0') {
+ if (prompter == NULL)
+ return(EIO);
+
+ if (ret = krb5_unparse_name(context, client, &clientstr))
+ return(ret);
+
+ strcpy(promptstr, "Password for ");
+ strncat(promptstr, clientstr, sizeof(promptstr)-strlen(promptstr)-1);
+ promptstr[sizeof(promptstr)-1] = '\0';
+
+ free(clientstr);
+
+ prompt.prompt = promptstr;
+ prompt.hidden = 1;
+ prompt.reply = password;
+
+ if (ret = ((*prompter)(context, prompter_data, NULL, 1, &prompt)))
+ return(ret);
+ }
+
+ if ((salt->length == -1) && (salt->data == NULL)) {
+ if (ret = krb5_principal2salt(context, client, &defsalt))
+ return(ret);
+
+ salt = &defsalt;
+ } else {
+ defsalt.length = 0;
+ }
+
+ ret = krb5_string_to_key(context, &eblock, as_key, password, salt);
+
+ if (defsalt.length)
+ krb5_xfree(defsalt.data);
+
+ return(ret);
+}
+
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_password(context, creds, client, password, prompter, data,
+ start_time, in_tkt_service, options)
+ krb5_context context;
+ krb5_creds *creds;
+ krb5_principal client;
+ char *password;
+ krb5_prompter_fct prompter;
+ void *data;
+ krb5_deltat start_time;
+ char *in_tkt_service;
+ krb5_get_init_creds_opt *options;
+{
+ krb5_error_code ret, ret2;
+ int master;
+ krb5_kdc_rep *as_reply;
+ int tries;
+ krb5_creds chpw_creds;
+ krb5_get_init_creds_opt chpw_opts;
+ krb5_data pw0, pw1;
+ char banner[1024], pw0array[1024], pw1array[1024];
+ krb5_prompt prompt[2];
+
+ master = 0;
+ as_reply = NULL;
+ memset(&chpw_creds, 0, sizeof(chpw_creds));
+
+ pw0.data = pw0array;
+
+ if (password) {
+ if ((pw0.length = strlen(password)) > sizeof(pw0array)) {
+ ret = EINVAL;
+ goto cleanup;
+ }
+ strcpy(pw0.data, password);
+ } else {
+ pw0.data[0] = '\0';
+ pw0.length = sizeof(pw0array);
+ }
+
+ pw1.data = pw1array;
+ pw1.data[0] = '\0';
+ pw1.length = sizeof(pw1array);
+
+ /* first try: get the requested tkt from any kdc */
+
+ ret = krb5_get_init_creds(context, creds, client, prompter, data,
+ start_time, in_tkt_service, options,
+ krb5_get_as_key_password, (void *) &pw0,
+ &master, &as_reply);
+
+ /* check for success */
+
+ if (ret == 0)
+ goto cleanup;
+
+ /* If all the kdc's are unavailable, or if the error was due to a
+ user interrupt, fail */
+
+ if ((ret == KRB5_KDC_UNREACH) ||
+ (ret == KRB5_LIBOS_PWDINTR))
+ goto cleanup;
+
+ /* if the reply did not come from the master kdc, try again with
+ the master kdc */
+
+ if (!master) {
+ master = 1;
+
+ ret2 = krb5_get_init_creds(context, creds, client, prompter, data,
+ start_time, in_tkt_service, options,
+ krb5_get_as_key_password, (void *) &pw0,
+ &master, &as_reply);
+
+ if (ret2 == 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ /* if the master is unreachable, return the error from the
+ slave we were able to contact */
+
+ if (ret2 == KRB5_KDC_UNREACH)
+ goto cleanup;
+
+ ret = ret2;
+ }
+
+ /* at this point, we have an error from the master. if the error
+ is not password expired, or if it is but there's no prompter,
+ return this error */
+
+ if ((ret != KRB5KDC_ERR_KEY_EXP) ||
+ (prompter == NULL))
+ goto cleanup;
+
+ /* ok, we have an expired password. Give the user a few chances
+ to change it */
+
+ /* use a minimal set of options */
+
+ krb5_get_init_creds_opt_init(&chpw_opts);
+ krb5_get_init_creds_opt_set_tkt_life(&chpw_opts, 5*60);
+ krb5_get_init_creds_opt_set_renew_life(&chpw_opts, 0);
+ krb5_get_init_creds_opt_set_forwardable(&chpw_opts, 0);
+ krb5_get_init_creds_opt_set_proxiable(&chpw_opts, 0);
+
+ if (ret = krb5_get_init_creds(context, &chpw_creds, client,
+ prompter, data,
+ start_time, "kadmin/changepw", &chpw_opts,
+ krb5_get_as_key_password, (void *) &pw0,
+ &master, NULL))
+ goto cleanup;
+
+ prompt[0].prompt = "Enter new password";
+ prompt[0].hidden = 1;
+ prompt[0].reply = &pw0;
+
+ prompt[1].prompt = "Enter it again";
+ prompt[1].hidden = 1;
+ prompt[1].reply = &pw1;
+
+ strcpy(banner, "Password expired. You must change it now.");
+
+ for (tries = 3; tries; tries--) {
+ pw0.length = sizeof(pw0array);
+ pw1.length = sizeof(pw1array);
+
+ if (ret = ((*prompter)(context, data, banner,
+ sizeof(prompt)/sizeof(prompt[0]), prompt)))
+ goto cleanup;
+
+ if (strcmp(pw0.data, pw1.data) != 0) {
+ ret = KRB5_LIBOS_BADPWDMATCH;
+ sprintf(banner, "%s. Please try again.", error_message(ret));
+ } else if (pw0.length == 0) {
+ ret = KRB5_CHPW_PWDNULL;
+ sprintf(banner, "%s. Please try again.", error_message(ret));
+ } else {
+ int result_code;
+ krb5_data code_string;
+ krb5_data result_string;
+
+ if (ret = krb5_change_password(context, &chpw_creds, pw0array,
+ &result_code, &code_string,
+ &result_string))
+ goto cleanup;
+
+ /* the change succeeded. go on */
+
+ if (result_code == 0) {
+ krb5_xfree(result_string.data);
+ break;
+ }
+
+ /* set this in case the retry loop falls through */
+
+ ret = KRB5_CHPW_FAIL;
+
+ if (result_code != KRB5_KPASSWD_SOFTERROR) {
+ krb5_xfree(result_string.data);
+ goto cleanup;
+ }
+
+ /* the error was soft, so try again */
+
+ /* 100 is I happen to know that no code_string will be longer
+ than 100 chars */
+
+ if (result_string.length > (sizeof(banner)-100))
+ result_string.length = sizeof(banner)-100;
+
+ sprintf(banner, "%.*s%s%.*s. Please try again.\n",
+ code_string.length, code_string.data,
+ result_string.length?": ":"",
+ result_string.length, result_string.data);
+
+ krb5_xfree(code_string.data);
+ krb5_xfree(result_string.data);
+ }
+ }
+
+ if (ret)
+ goto cleanup;
+
+ /* the password change was successful. Get an initial ticket
+ from the master. this is the last try. the return from this
+ is final. */
+
+ ret = krb5_get_init_creds(context, creds, client, prompter, data,
+ start_time, in_tkt_service, options,
+ krb5_get_as_key_password, (void *) &pw0,
+ &master, &as_reply);
+
+cleanup:
+ /* if getting the password was successful, then check to see if the
+ password is about to expire, and warn if so */
+
+ if (ret == 0) {
+ krb5_timestamp now;
+ int hours;
+
+ /* XXX 7 days should be configurable. This is all pretty ad hoc,
+ and could probably be improved if I was willing to screw around
+ with timezones, etc. */
+
+ if (prompter &&
+ (in_tkt_service &&
+ (strcmp(in_tkt_service, "kadmin/changepw") != 0)) &&
+ ((ret = krb5_timeofday(context, &now)) == 0) &&
+ as_reply->enc_part2->key_exp &&
+ ((hours = ((as_reply->enc_part2->key_exp-now)/(60*60))) <= 7*24) &&
+ (hours >= 0)) {
+ if (hours < 1)
+ sprintf(banner,
+ "Warning: Your password will expire in less than one hour.",
+ hours);
+ else if (hours <= 48)
+ sprintf(banner, "Warning: Your password will expire in %d hour%s.",
+ hours, (hours == 1)?"":"s");
+ else
+ sprintf(banner, "Warning: Your password will expire in %d days.",
+ hours/24);
+
+ /* ignore an error here */
+ (*prompter)(context, data, banner, NULL, 0);
+ }
+ }
+
+ memset(pw0array, 0, sizeof(pw0array));
+ memset(pw1array, 0, sizeof(pw1array));
+ krb5_free_cred_contents(context, &chpw_creds);
+ if (as_reply)
+ krb5_free_kdc_rep(context, as_reply);
+
+ return(ret);
+}
diff --git a/src/lib/krb5/krb/preauth.c b/src/lib/krb5/krb/preauth.c
index 586e1b9..910069d 100644
--- a/src/lib/krb5/krb/preauth.c
+++ b/src/lib/krb5/krb/preauth.c
@@ -446,8 +446,13 @@ char *handle_sam_labels(sc)
label = "Challenge for Enigma Logic mechanism";
break;
case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
+ case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
label = "Challenge for Digital Pathways mechanism";
break;
+ case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
+ case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
+ label = "Challenge for Activcard mechanism";
+ break;
case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
label = "Challenge for Enhanced S/Key mechanism";
break;
@@ -457,6 +462,9 @@ char *handle_sam_labels(sc)
case PA_SAM_TYPE_SECURID: /* Security Dynamics */
label = "Challenge for Security Dynamics mechanism";
break;
+ case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
+ label = "Challenge for Security Dynamics mechanism";
+ break;
}
prompt = "Passcode";
label_len = strlen(label);
@@ -470,10 +478,12 @@ char *handle_sam_labels(sc)
p = prompt1 = malloc(label_len + strlen(sep1) +
challenge_len + strlen(sep2) +
prompt_len+ strlen(sep3) + 1);
- strncpy(p, label, label_len); p += label_len;
- strcpy(p, sep1); p += strlen(sep1);
- strncpy(p, challenge, challenge_len); p += challenge_len;
- strcpy(p, sep2); p += strlen(sep2);
+ if (challenge_len) {
+ strncpy(p, label, label_len); p += label_len;
+ strcpy(p, sep1); p += strlen(sep1);
+ strncpy(p, challenge, challenge_len); p += challenge_len;
+ strcpy(p, sep2); p += strlen(sep2);
+ }
strncpy(p, prompt, prompt_len); p += prompt_len;
strcpy(p, sep3); /* p += strlen(sep3); */
return prompt1;
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
new file mode 100644
index 0000000..0a154f6
--- /dev/null
+++ b/src/lib/krb5/krb/preauth2.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright 1995 by the Massachusetts Institute of Technology. All
+ * Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * This file contains routines for establishing, verifying, and any other
+ * necessary functions, for utilizing the pre-authentication field of the
+ * kerberos kdc request, with various hardware/software verification devices.
+ */
+
+#include "k5-int.h"
+
+typedef krb5_error_code (*pa_function)(krb5_context,
+ krb5_kdc_req *request,
+ krb5_pa_data *in_padata,
+ krb5_pa_data **out_padata,
+ krb5_data *salt,
+ krb5_keyblock *as_key,
+ krb5_prompter_fct prompter_fct,
+ void *prompter_data,
+ krb5_gic_get_as_key_fct gak_fct,
+ void *gak_data);
+
+typedef struct _pa_types_t {
+ krb5_preauthtype type;
+ pa_function fct;
+ int flags;
+} pa_types_t;
+
+#define PA_REAL 0x0001
+#define PA_INFO 0x0002
+
+static
+krb5_error_code pa_salt(krb5_context context,
+ krb5_kdc_req *request,
+ krb5_pa_data *in_padata,
+ krb5_pa_data **out_padata,
+ krb5_data *salt,
+ krb5_keyblock *as_key,
+ krb5_prompter_fct prompter, void *prompter_data,
+ krb5_gic_get_as_key_fct gak_fct, void *gak_data)
+{
+ krb5_error_code ret;
+ krb5_data tmp;
+
+ /* screw the abstraction. If there was a *reasonable* copy_data,
+ I'd use it. But I'm inside the library, which is the twilight
+ zone of source code, so I can do anything. */
+
+ tmp.length = in_padata->length;
+ if (tmp.length) {
+ if ((tmp.data = malloc(tmp.length)) == NULL)
+ return ENOMEM;
+ memcpy(tmp.data, in_padata->contents, tmp.length);
+ } else {
+ tmp.data = NULL;
+ }
+
+ *salt = tmp;
+
+ /* assume that no other salt was allocated */
+
+ if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
+ salt->length = -1;
+
+ return(0);
+}
+
+static
+krb5_error_code pa_enc_timestamp(krb5_context context,
+ krb5_kdc_req *request,
+ krb5_pa_data *in_padata,
+ krb5_pa_data **out_padata,
+ krb5_data *salt,
+ krb5_keyblock *as_key,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_gic_get_as_key_fct gak_fct,
+ void *gak_data)
+{
+ krb5_error_code ret;
+ krb5_pa_enc_ts pa_enc;
+ krb5_data *tmp;
+ krb5_enc_data enc_data;
+ krb5_pa_data *pa;
+
+ /* if we haven't yet gotten a key, get it now. */
+
+ if (ret = ((*gak_fct)(context, request->client,
+ request->ktype[0], prompter, prompter_data,
+ salt, as_key, gak_data)))
+ return(ret);
+
+ /* now get the time of day, and encrypt it accordingly */
+
+ if (ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec))
+ return(ret);
+
+ if (ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp))
+ return(ret);
+
+ ret = krb5_encrypt_data(context, as_key, 0, tmp, &enc_data);
+
+ krb5_free_data(context, tmp);
+
+ if (ret)
+ return(ret);
+
+ ret = encode_krb5_enc_data(&enc_data, &tmp);
+
+ krb5_xfree(enc_data.ciphertext.data);
+
+ if (ret)
+ return(ret);
+
+ if ((pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
+ krb5_free_data(context, tmp);
+ return(ENOMEM);
+ }
+
+ pa->magic = KV5M_PA_DATA;
+ pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP;
+ pa->length = tmp->length;
+ pa->contents = (krb5_octet *) tmp->data;
+
+ *out_padata = pa;
+
+ krb5_xfree(tmp);
+
+ return(0);
+}
+
+static
+char *sam_challenge_banner(sam_type)
+ krb5_int32 sam_type;
+{
+ char *label;
+
+ switch (sam_type) {
+ case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
+ label = "Challenge for Enigma Logic mechanism";
+ break;
+ case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
+ case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
+ label = "Challenge for Digital Pathways mechanism";
+ break;
+ case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
+ case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
+ label = "Challenge for Activcard mechanism";
+ break;
+ case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
+ label = "Challenge for Enhanced S/Key mechanism";
+ break;
+ case PA_SAM_TYPE_SKEY: /* Traditional S/Key */
+ label = "Challenge for Traditional S/Key mechanism";
+ break;
+ case PA_SAM_TYPE_SECURID: /* Security Dynamics */
+ label = "Challenge for Security Dynamics mechanism";
+ break;
+ case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
+ label = "Challenge for Security Dynamics mechanism";
+ break;
+ default:
+ label = "Challenge from authentication server";
+ break;
+ }
+
+ return(label);
+}
+
+/* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
+
+#define SAMDATA(kdata, str, maxsize) \
+ (kdata.length)? \
+ ((((kdata.length)<=(maxsize))?(kdata.length):(maxsize))): \
+ strlen(str), \
+ (kdata.length)?(kdata.data):(str)
+
+/* XXX Danger! This code is not in sync with the kerberos-password-02
+ draft. This draft cannot be implemented as written. This code is
+ compatible with earlier versions of mit krb5 and cygnus kerbnet. */
+
+static
+krb5_error_code pa_sam(krb5_context context,
+ krb5_kdc_req *request,
+ krb5_pa_data *in_padata,
+ krb5_pa_data **out_padata,
+ krb5_data *salt,
+ krb5_keyblock *as_key,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_gic_get_as_key_fct gak_fct,
+ void *gak_data)
+{
+ krb5_error_code ret;
+ krb5_data tmpsam;
+ char banner[100], prompt[100], response[100];
+ krb5_data response_data;
+ krb5_prompt kprompt;
+ krb5_encrypt_block eblock;
+ krb5_data defsalt;
+ krb5_sam_challenge *sam_challenge = 0;
+ krb5_sam_response sam_response;
+ /* these two get encrypted and stuffed in to sam_response */
+ krb5_enc_sam_response_enc enc_sam_response_enc;
+ krb5_keyblock * sam_use_key = 0;
+ krb5_data * scratch;
+ krb5_pa_data * pa;
+
+ tmpsam.length = in_padata->length;
+ tmpsam.data = (char *) in_padata->contents;
+ if (ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge))
+ return(ret);
+
+ if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
+ krb5_xfree(sam_challenge);
+ return(KRB5_SAM_UNSUPPORTED);
+ }
+
+ sprintf(banner, "%.*s",
+ SAMDATA(sam_challenge->sam_challenge_label,
+ sam_challenge_banner(sam_challenge->sam_type),
+ sizeof(banner)-1));
+
+ /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */
+ sprintf(prompt, "%s%.*s%s%.*s",
+ sam_challenge->sam_challenge.length?"Challenge is [":"",
+ SAMDATA(sam_challenge->sam_challenge, "", 20),
+ sam_challenge->sam_challenge.length?"], ":"",
+ SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));
+
+ response_data.data = response;
+ response_data.length = sizeof(response);
+
+ kprompt.prompt = prompt;
+ kprompt.hidden = sam_challenge->sam_challenge.length?0:1;
+ kprompt.reply = &response_data;
+
+ if (ret = ((*prompter)(context, prompter_data, banner, 1, &kprompt))) {
+ krb5_xfree(sam_challenge);
+ return(ret);
+ }
+
+ enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
+ if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
+ enc_sam_response_enc.sam_passcode = response_data;
+ } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
+ if (sam_challenge->sam_nonce == 0) {
+ if (ret = krb5_us_timeofday(context,
+ &enc_sam_response_enc.sam_timestamp,
+ &enc_sam_response_enc.sam_usec)) {
+ krb5_xfree(sam_challenge);
+ return(ret);
+ }
+
+ sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
+ }
+
+ /* process the key as password */
+
+ if (as_key->length) {
+ krb5_free_keyblock_contents(context, as_key);
+ as_key->length = 0;
+ }
+
+ /* XXX the server uses this fixed enctype, so we will, too. */
+
+ if (!valid_enctype(ENCTYPE_DES_CBC_MD5))
+ return(KRB5_PROG_ETYPE_NOSUPP);
+
+ krb5_use_enctype(context, &eblock, ENCTYPE_DES_CBC_MD5);
+
+#if 0
+ if ((salt->length == -1) && (salt->data == NULL)) {
+ if (ret = krb5_principal2salt(context, request->client,
+ &defsalt)) {
+ krb5_xfree(sam_challenge);
+ return(ret);
+ }
+
+ salt = &defsalt;
+ } else {
+ defsalt.length = 0;
+ }
+#else
+ defsalt.length = 0;
+ salt = NULL;
+#endif
+
+ ret = krb5_string_to_key(context, &eblock, as_key,
+ &response_data, salt);
+ if (defsalt.length)
+ krb5_xfree(defsalt.data);
+
+ if (ret) {
+ krb5_xfree(sam_challenge);
+ return(ret);
+ }
+
+ enc_sam_response_enc.sam_passcode.length = 0;
+ }
+
+ /* copy things from the challenge */
+ sam_response.sam_nonce = sam_challenge->sam_nonce;
+ sam_response.sam_flags = sam_challenge->sam_flags;
+ sam_response.sam_track_id = sam_challenge->sam_track_id;
+ sam_response.sam_type = sam_challenge->sam_type;
+ sam_response.magic = KV5M_SAM_RESPONSE;
+
+ krb5_xfree(sam_challenge);
+
+ /* encode the encoded part of the response */
+ if (ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
+ &scratch))
+ return(ret);
+
+ ret = krb5_encrypt_data(context, as_key, 0, scratch,
+ &sam_response.sam_enc_nonce_or_ts);
+
+ krb5_free_data(context, scratch);
+
+ if (ret)
+ return(ret);
+
+ /* sam_enc_key is reserved for future use */
+ sam_response.sam_enc_key.ciphertext.length = 0;
+
+ if ((pa = malloc(sizeof(krb5_pa_data))) == NULL)
+ return(ENOMEM);
+
+ if (ret = encode_krb5_sam_response(&sam_response, &scratch)) {
+ free(pa);
+ return(ret);
+ }
+
+ pa->magic = KV5M_PA_DATA;
+ pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
+ pa->length = scratch->length;
+ pa->contents = (krb5_octet *) scratch->data;
+
+ *out_padata = pa;
+
+ return(0);
+}
+
+pa_types_t pa_types[] = {
+ {
+ KRB5_PADATA_PW_SALT,
+ pa_salt,
+ PA_INFO,
+ },
+ {
+ KRB5_PADATA_AFS3_SALT,
+ pa_salt,
+ PA_INFO,
+ },
+ {
+ KRB5_PADATA_ENC_TIMESTAMP,
+ pa_enc_timestamp,
+ PA_REAL,
+ },
+ {
+ KRB5_PADATA_SAM_CHALLENGE,
+ pa_sam,
+ PA_REAL,
+ },
+ {
+ -1,
+ NULL,
+ 0,
+ },
+};
+
+krb5_error_code
+krb5_do_preauth(krb5_context context,
+ krb5_kdc_req *request,
+ krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
+ krb5_data *salt,
+ krb5_keyblock *as_key,
+ krb5_prompter_fct prompter, void *prompter_data,
+ krb5_gic_get_as_key_fct gak_fct, void *gak_data)
+{
+ int h, i, j, out_pa_list_size;
+ krb5_pa_data *out_pa, **out_pa_list;
+ krb5_error_code ret;
+ static int paorder[] = { PA_INFO, PA_REAL };
+ int realdone;
+
+ if (in_padata == NULL) {
+ *out_padata = NULL;
+ return(0);
+ }
+
+ out_pa_list = NULL;
+ out_pa_list_size = 0;
+
+ /* first do all the informational preauths, then the first real one */
+
+ for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
+ realdone = 0;
+ for (i=0; in_padata[i] && !realdone; i++) {
+ for (j=0; pa_types[j].type >= 0; j++) {
+ if ((in_padata[i]->pa_type == pa_types[j].type) &&
+ (pa_types[j].flags & paorder[h])) {
+ out_pa = NULL;
+
+ if (ret = ((*pa_types[j].fct)(context, request,
+ in_padata[i], &out_pa,
+ salt, as_key,
+ prompter, prompter_data,
+ gak_fct, gak_data))) {
+ if (out_pa_list) {
+ out_pa_list[out_pa_list_size++] = NULL;
+ krb5_free_pa_data(context, out_pa_list);
+ }
+ return(ret);
+ }
+
+ if (out_pa) {
+ if (out_pa_list == NULL) {
+ if ((out_pa_list =
+ (krb5_pa_data **)
+ malloc(2*sizeof(krb5_pa_data *)))
+ == NULL)
+ return(ENOMEM);
+ } else {
+ if ((out_pa_list =
+ (krb5_pa_data **)
+ realloc(out_pa_list,
+ (out_pa_list_size+2)*
+ sizeof(krb5_pa_data *)))
+ == NULL)
+ /* XXX this will leak the pointers which
+ have already been allocated. oh well. */
+ return(ENOMEM);
+ }
+
+ out_pa_list[out_pa_list_size++] = out_pa;
+ }
+ if (h == PA_REAL)
+ realdone = 1;
+ }
+ }
+ }
+ }
+
+ if (out_pa_list)
+ out_pa_list[out_pa_list_size++] = NULL;
+
+ *out_padata = out_pa_list;
+
+ return(0);
+}
diff --git a/src/lib/krb5/krb/send_tgs.c b/src/lib/krb5/krb/send_tgs.c
index 3247c8e..b06ef2b 100644
--- a/src/lib/krb5/krb/send_tgs.c
+++ b/src/lib/krb5/krb/send_tgs.c
@@ -337,7 +337,7 @@ krb5_send_tgs(context, kdcoptions, timestruct, ktypes, sname, addrs,
/* now send request & get response from KDC */
retval = krb5_sendto_kdc(context, scratch,
krb5_princ_realm(context, sname),
- &rep->response);
+ &rep->response, NULL);
krb5_free_data(context, scratch);
if (retval == 0) {
diff --git a/src/lib/krb5/krb/vfy_increds.c b/src/lib/krb5/krb/vfy_increds.c
new file mode 100644
index 0000000..bb8ea34
--- /dev/null
+++ b/src/lib/krb5/krb/vfy_increds.c
@@ -0,0 +1,238 @@
+#include "k5-int.h"
+
+static krb5_error_code
+krb5_cc_copy_creds_except(context, incc, outcc, princ)
+ krb5_context context;
+ krb5_ccache incc;
+ krb5_ccache outcc;
+ krb5_principal princ;
+{
+ krb5_error_code code;
+ krb5_flags flags;
+ krb5_cc_cursor cur;
+ krb5_creds creds;
+
+ flags = 0; /* turns off OPENCLOSE mode */
+ if ((code = krb5_cc_set_flags(context, incc, flags)))
+ return(code);
+ if ((code = krb5_cc_set_flags(context, outcc, flags)))
+ return(code);
+
+ if ((code = krb5_cc_start_seq_get(context, incc, &cur)))
+ goto cleanup;
+
+ while (!(code = krb5_cc_next_cred(context, incc, &cur, &creds))) {
+ if (krb5_principal_compare(context, princ, creds.server))
+ continue;
+
+ code = krb5_cc_store_cred(context, outcc, &creds);
+ krb5_free_cred_contents(context, &creds);
+ if (code)
+ goto cleanup;
+ }
+
+ if (code != KRB5_CC_END)
+ goto cleanup;
+
+ code = 0;
+
+cleanup:
+ flags = KRB5_TC_OPENCLOSE;
+
+ if (code)
+ krb5_cc_set_flags(context, incc, flags);
+ else
+ code = krb5_cc_set_flags(context, incc, flags);
+
+ if (code)
+ krb5_cc_set_flags(context, outcc, flags);
+ else
+ code = krb5_cc_set_flags(context, outcc, flags);
+
+ return(code);
+}
+
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_verify_init_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal server_arg,
+ krb5_keytab keytab_arg,
+ krb5_ccache *ccache_arg,
+ krb5_verify_init_creds_opt *options)
+{
+ krb5_error_code ret;
+ krb5_principal server;
+ krb5_keytab keytab;
+ krb5_ccache ccache;
+ krb5_keytab_entry kte;
+ krb5_creds in_creds, *out_creds;
+ krb5_auth_context authcon;
+ krb5_data ap_req;
+ int keytab_key_exists, rd_req_succeeds, nofail;
+
+ keytab_key_exists = 0;
+ rd_req_succeeds = 0;
+ nofail = 0;
+
+ /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */
+
+ server = NULL;
+ keytab = NULL;
+ ccache = NULL;
+ out_creds = NULL;
+ authcon = NULL;
+ ap_req.data = NULL;
+
+ if (server_arg) {
+ server = server_arg;
+ } else {
+ if (ret = krb5_sname_to_principal(context, NULL, NULL,
+ KRB5_NT_SRV_HST, &server))
+ goto cleanup;
+ }
+
+ /* first, check if the server is in the keytab. If not, there's
+ no reason to continue. rd_req does all this, but there's
+ no way to know that a given error is caused by a missing
+ keytab or key, and not by some other problem. */
+
+ if (keytab_arg) {
+ keytab = keytab_arg;
+ } else {
+ if (ret = krb5_kt_default(context, &keytab))
+ goto cleanup;
+ }
+
+ if (ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))
+ goto cleanup;
+
+ krb5_kt_free_entry(context, &kte);
+ keytab_key_exists = 1;
+
+ /* If the creds are for the server principal, we're set, just do
+ a mk_req. Otherwise, do a get_credentials first. */
+
+ if (krb5_principal_compare(context, server, creds->server)) {
+ /* make an ap_req */
+ if (ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds,
+ &ap_req))
+ goto cleanup;
+ } else {
+ /* this is unclean, but it's the easiest way without ripping the
+ library into very small pieces. store the client's initial cred
+ in a memory ccache, then call the library. Later, we'll copy
+ everything except the initial cred into the ccache we return to
+ the user. A clean implementation would involve library
+ internals with a coherent idea of "in" and "out". */
+
+ /* insert the initial cred into the ccache */
+
+ if (ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache))
+ goto cleanup;
+
+ if (ret = krb5_cc_initialize(context, ccache, creds->client))
+ goto cleanup;
+
+ if (ret = krb5_cc_store_cred(context, ccache, creds))
+ goto cleanup;
+
+ /* set up for get_creds */
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.client = creds->client;
+ in_creds.server = server;
+ if (ret = krb5_timeofday(context, &in_creds.times.endtime))
+ goto cleanup;
+ in_creds.times.endtime += 5*60;
+
+ if (ret = krb5_get_credentials(context, 0, ccache, &in_creds,
+ &out_creds))
+ goto cleanup;
+
+ /* make an ap_req */
+ if (ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds,
+ &ap_req))
+ goto cleanup;
+ }
+
+ /* wipe the auth context for mk_req */
+ if (authcon) {
+ krb5_auth_con_free(context, authcon);
+ authcon = NULL;
+ }
+
+ /* verify the ap_req */
+
+ if (ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab,
+ NULL, NULL))
+ goto cleanup;
+
+ rd_req_succeeds = 1;
+
+cleanup:
+ /* I could test the error case first, but then there would be a
+ chance that the verification would succeed when there was
+ actually a significant failure (some transient condition could
+ make rd_req fail, and this would not be a problem if nofail was
+ not set */
+
+ if (!keytab_key_exists) {
+ krb5_error_code ret2;
+
+ if (options &&
+ (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL))
+ nofail = options->ap_req_nofail;
+ else if ((ret2 = krb5_appdefault_boolean(context, &creds->client->realm,
+ "verify_ap_req_nofail",
+ &nofail))
+ == 0)
+ ;
+ else
+ nofail = 0;
+ }
+
+ if ((keytab_key_exists && rd_req_succeeds) ||
+ (!keytab_key_exists && !nofail)) {
+ ret = 0;
+
+ if (ccache_arg && ccache) {
+ if (*ccache_arg == NULL) {
+ krb5_ccache retcc;
+
+ retcc = NULL;
+
+ if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) ||
+ (ret = krb5_cc_initialize(context, retcc, creds->client)) ||
+ (ret = krb5_cc_copy_creds_except(context, ccache, retcc,
+ creds->server))) {
+ if (retcc)
+ krb5_cc_destroy(context, retcc);
+ } else {
+ *ccache_arg = retcc;
+ }
+ } else {
+ /* if this returns an error, then that's the return
+ from this function */
+ ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg,
+ server);
+ }
+ }
+ }
+
+ if (!server_arg)
+ krb5_free_principal(context, server);
+ if (!keytab_arg)
+ krb5_kt_close(context, keytab);
+ if (ccache)
+ krb5_cc_destroy(context, ccache);
+ if (out_creds)
+ krb5_free_creds(context, out_creds);
+ if (authcon)
+ krb5_auth_con_free(context, authcon);
+ if (ap_req.data)
+ krb5_xfree(ap_req.data);
+
+ return(ret);
+}
+
+
+
diff --git a/src/lib/krb5/krb/vic_opt.c b/src/lib/krb5/krb/vic_opt.c
new file mode 100644
index 0000000..d28d709
--- /dev/null
+++ b/src/lib/krb5/krb/vic_opt.c
@@ -0,0 +1,17 @@
+#include "k5-int.h"
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_verify_init_creds_opt_init(opt)
+ krb5_verify_init_creds_opt *opt;
+{
+ opt->flags = 0;
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_verify_init_creds_opt_set_ap_req_nofail(opt, ap_req_nofail)
+ krb5_verify_init_creds_opt *opt;
+ int ap_req_nofail;
+{
+ opt->flags |= KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL;
+ opt->ap_req_nofail = ap_req_nofail;
+}