* Makefile.in: Add files chpw.c, gic_*, preauth2.c, vfy_increds.c,
authorTom Yu <tlyu@mit.edu>
Sat, 6 Dec 1997 08:00:17 +0000 (08:00 +0000)
committerTom Yu <tlyu@mit.edu>
Sat, 6 Dec 1997 08:00:17 +0000 (08:00 +0000)
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

12 files changed:
src/lib/krb5/krb/ChangeLog
src/lib/krb5/krb/Makefile.in
src/lib/krb5/krb/chpw.c [new file with mode: 0644]
src/lib/krb5/krb/get_in_tkt.c
src/lib/krb5/krb/gic_keytab.c [new file with mode: 0644]
src/lib/krb5/krb/gic_opt.c [new file with mode: 0644]
src/lib/krb5/krb/gic_pwd.c [new file with mode: 0644]
src/lib/krb5/krb/preauth.c
src/lib/krb5/krb/preauth2.c [new file with mode: 0644]
src/lib/krb5/krb/send_tgs.c
src/lib/krb5/krb/vfy_increds.c [new file with mode: 0644]
src/lib/krb5/krb/vic_opt.c [new file with mode: 0644]

index 043f0f748cc1dc829e147b5dcc4efa9cf36aa01c..8c2d5e2dabfc22ea19811f05fde35e57604115fa 100644 (file)
@@ -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
index 8d08fcea3e9cd318d2176c3b69feafa0389f6c21..87f93ea19bedc17bb4b7e88059da6c7e2cdd39ec 100644 (file)
@@ -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 (file)
index 0000000..3266f4f
--- /dev/null
@@ -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);
+}
index 13146901692ea33a60e80842a95331272dad05b0..cbcbc724611d2b250776f6df3e0f2bd91ad4a29b 100644 (file)
@@ -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 (file)
index 0000000..8b6f231
--- /dev/null
@@ -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 (file)
index 0000000..eb04f51
--- /dev/null
@@ -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 (file)
index 0000000..5ab3280
--- /dev/null
@@ -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);
+}
index 586e1b94d07413812709b02c541796ca89df91ec..910069d1ce16eb43055a2048a18e601edb3683be 100644 (file)
@@ -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 (file)
index 0000000..0a154f6
--- /dev/null
@@ -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);
+}
index 3247c8eb52c2b90b21d26eff98967d624307053c..b06ef2bfc737d5483ef22ae335c66d270ad50d33 100644 (file)
@@ -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 (file)
index 0000000..bb8ea34
--- /dev/null
@@ -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 (file)
index 0000000..d28d709
--- /dev/null
@@ -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;
+}