Implement s4u extensions
authorGreg Hudson <ghudson@mit.edu>
Sun, 13 Sep 2009 02:52:23 +0000 (02:52 +0000)
committerGreg Hudson <ghudson@mit.edu>
Sun, 13 Sep 2009 02:52:23 +0000 (02:52 +0000)
Merge Luke's users/lhoward/s4u branch to trunk.  Implements S4U2Self
and S4U2Proxy extensions.

ticket: 6563

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@22736 dc483132-0cff-0310-8789-dd5450dbe970

71 files changed:
src/clients/kvno/kvno.M
src/clients/kvno/kvno.c
src/include/k5-int.h
src/include/kdb.h
src/include/kdb_ext.h
src/include/krb5/krb5.hin
src/kadmin/cli/kadmin.c
src/kdc/do_tgs_req.c
src/kdc/kdc_authdata.c
src/kdc/kdc_preauth.c
src/kdc/kdc_util.c
src/kdc/kdc_util.h
src/lib/crypto/krb/enc_provider/Makefile.in [new file with mode: 0644]
src/lib/crypto/krb/enc_provider/aes.c [new file with mode: 0644]
src/lib/crypto/krb/enc_provider/deps [new file with mode: 0644]
src/lib/crypto/krb/enc_provider/des.c [new file with mode: 0644]
src/lib/crypto/krb/enc_provider/des3.c [new file with mode: 0644]
src/lib/crypto/krb/enc_provider/enc_provider.h [new file with mode: 0644]
src/lib/crypto/krb/enc_provider/rc4.c [new file with mode: 0644]
src/lib/gssapi/generic/gssapi_ext.h
src/lib/gssapi/krb5/Makefile.in
src/lib/gssapi/krb5/accept_sec_context.c
src/lib/gssapi/krb5/acquire_cred.c
src/lib/gssapi/krb5/gssapiP_krb5.h
src/lib/gssapi/krb5/gssapi_krb5.c
src/lib/gssapi/krb5/import_name.c
src/lib/gssapi/krb5/init_sec_context.c
src/lib/gssapi/krb5/krb5_gss_glue.c
src/lib/gssapi/krb5/s4u_gss_glue.c [new file with mode: 0644]
src/lib/gssapi/krb5/val_cred.c
src/lib/gssapi/libgssapi_krb5.exports
src/lib/gssapi/mechglue/Makefile.in
src/lib/gssapi/mechglue/g_accept_sec_context.c
src/lib/gssapi/mechglue/g_acquire_cred.c
src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_glue.c
src/lib/gssapi/mechglue/g_initialize.c
src/lib/gssapi/mechglue/g_set_context_option.c
src/lib/gssapi/mechglue/mglueP.h
src/lib/gssapi/spnego/gssapiP_spnego.h
src/lib/gssapi/spnego/spnego_mech.c
src/lib/kadm5/str_conv.c
src/lib/krb5/asn.1/asn1_k_decode.c
src/lib/krb5/asn.1/asn1_k_decode.h
src/lib/krb5/asn.1/asn1_k_encode.c
src/lib/krb5/asn.1/krb5_decode.c
src/lib/krb5/krb/Makefile.in
src/lib/krb5/krb/gc_frm_kdc.c
src/lib/krb5/krb/gc_via_tkt.c
src/lib/krb5/krb/get_creds.c
src/lib/krb5/krb/get_in_tkt.c
src/lib/krb5/krb/int-proto.h
src/lib/krb5/krb/kfree.c
src/lib/krb5/krb/preauth2.c
src/lib/krb5/krb/s4u_creds.c [new file with mode: 0644]
src/lib/krb5/krb/send_tgs.c
src/lib/krb5/krb/srv_dec_tkt.c
src/lib/krb5/libkrb5.exports
src/lib/krb5/os/sendto_kdc.c
src/slave/kproplog.c
src/tests/asn.1/krb5_decode_leak.c
src/tests/asn.1/krb5_decode_test.c
src/tests/asn.1/krb5_encode_test.c
src/tests/asn.1/ktest.c
src/tests/asn.1/ktest.h
src/tests/asn.1/ktest_equal.c
src/tests/asn.1/ktest_equal.h
src/tests/asn.1/reference_encode.out
src/tests/asn.1/trval_reference.out
src/tests/gssapi/Makefile.in
src/tests/gssapi/t_s4u.c [new file with mode: 0644]

index b7e4d46a0da91b52af7e61d8cc25676951e03723..37b0bcbd53bfa8a4c0f139eb6f12d8da45eee276 100644 (file)
@@ -51,6 +51,13 @@ suppress printing
 .B \-h
 prints a usage statement and exits
 .TP
+.B \-P
+specifies that the
+.B service1 service2 ...
+arguments are to be treated as services for which credentials should
+be acquired using constrained delegation. This option is only valid
+when used in conjunction with protocol transition.
+.TP
 .B \-S sname
 specifies that krb5_sname_to_principal() will be used to build
 principal names.  If this flag is specified, the
@@ -59,6 +66,13 @@ arguments are interpreted as hostnames (rather than principal names),
 and
 .B sname
 is interpreted as the service name.
+.TP
+.B \-U for_user
+specifies that protocol transition (S4U2Self) is to be used to acquire
+a ticket on behalf of
+.B for_user.
+If constrained delegation is not requested, the service name
+must match the credentials cache client principal.
 .SH ENVIRONMENT
 .B Kvno
 uses the following environment variable:
index b98b85d30420b50a817d276f17dab13501b9c6f1..58702525f6127b9abdb8e97f43edc3482fc6ef47 100644 (file)
@@ -39,8 +39,9 @@ static char *prog;
 
 static void xusage()
 {
-    fprintf(stderr, "usage: %s [-C] [-u] [-c ccache] [-e etype] [-k keytab] [-S sname] service1 service2 ...\n",
-            prog);
+    fprintf(stderr, "usage: %s [-C] [-u] [-c ccache] [-e etype]\n", prog);
+    fprintf(stderr, "\t[-k keytab] [-S sname] [-U for_user [-P]]\n");
+    fprintf(stderr, "\tservice1 service2 ...\n");
     exit(1);
 }
 
@@ -48,7 +49,8 @@ int quiet = 0;
 
 static void do_v5_kvno (int argc, char *argv[], 
                         char *ccachestr, char *etypestr, char *keytab_name,
-                       char *sname, int canon, int unknown);
+                       char *sname, int canon, int unknown,
+                       char *for_user, int proxy);
 
 #include <com_err.h>
 static void extended_com_err_fn (const char *, errcode_t, const char *,
@@ -58,8 +60,8 @@ int main(int argc, char *argv[])
 {
     int option;
     char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL;
-    char *sname = NULL;
-    int canon = 0, unknown = 0;
+    char *sname = NULL, *for_user = NULL;
+    int canon = 0, unknown = 0, proxy = 0;
 
 
     set_com_err_hook (extended_com_err_fn);
@@ -67,7 +69,7 @@ int main(int argc, char *argv[])
     prog = strrchr(argv[0], '/');
     prog = prog ? (prog + 1) : argv[0];
 
-    while ((option = getopt(argc, argv, "uCc:e:hk:qS:")) != -1) {
+    while ((option = getopt(argc, argv, "uCc:e:hk:qPS:U:")) != -1) {
        switch (option) {
        case 'C':
            canon = 1;
@@ -87,6 +89,9 @@ int main(int argc, char *argv[])
        case 'q':
            quiet = 1;
            break;
+       case 'P':
+           proxy = 1; /* S4U2Proxy - constrained delegation */
+           break;
        case 'S':
            sname = optarg;
             if (unknown == 1){ 
@@ -101,21 +106,37 @@ int main(int argc, char *argv[])
                xusage();
             }
             break;
+       case 'U':
+           for_user = optarg; /* S4U2Self - protocol transition */
+           break;
        default:
            xusage();
            break;
        }
     }
 
+    if (proxy) {
+       if (keytab_name == NULL) {
+           fprintf(stderr, "Option -P (constrained delegation) "
+                           "requires keytab to be specified\n");
+           xusage();
+       } else if (for_user == NULL) {
+           fprintf(stderr, "Option -P (constrained delegation) requires "
+                           "option -U (protocol transition)\n");
+           xusage();
+       }
+    }
+
     if ((argc - optind) < 1)
        xusage();
 
        do_v5_kvno(argc - optind, argv + optind,
-                  ccachestr, etypestr, keytab_name, sname, canon, unknown);
+                  ccachestr, etypestr, keytab_name, sname,
+                  canon, unknown, for_user, proxy);
     return 0;
 }
 
-#include <krb5.h>
+#include <k5-int.h>
 static krb5_context context;
 static void extended_com_err_fn (const char *myprog, errcode_t code,
                                 const char *fmt, va_list args)
@@ -130,17 +151,18 @@ static void extended_com_err_fn (const char *myprog, errcode_t code,
 
 static void do_v5_kvno (int count, char *names[], 
                         char * ccachestr, char *etypestr, char *keytab_name,
-                       char *sname, int canon, int unknown)
+                       char *sname, int canon, int unknown, char *for_user,
+                       int proxy)
 {
     krb5_error_code ret;
     int i, errors;
     krb5_enctype etype;
     krb5_ccache ccache;
     krb5_principal me;
-    krb5_creds in_creds, *out_creds;
-    krb5_ticket *ticket;
-    char *princ;
+    krb5_creds in_creds;
     krb5_keytab keytab = NULL;
+    krb5_principal for_user_princ = NULL;
+    krb5_flags options;
 
     ret = krb5_init_context(&context);
     if (ret) {
@@ -175,6 +197,16 @@ static void do_v5_kvno (int count, char *names[],
        }
     }
 
+    if (for_user) {
+       ret = krb5_parse_name_flags(context, for_user,
+                                   KRB5_PRINCIPAL_PARSE_ENTERPRISE,
+                                   &for_user_princ);
+       if (ret) {
+           com_err(prog, ret, "while parsing principal name %s", for_user);
+           exit(1);
+       }
+    }
+
     ret = krb5_cc_get_principal(context, ccache, &me);
     if (ret) {
        com_err(prog, ret, "while getting client principal name");
@@ -183,91 +215,131 @@ static void do_v5_kvno (int count, char *names[],
 
     errors = 0;
 
+    options = 0;
+    if (canon)
+       options |= KRB5_GC_CANONICALIZE;
+
     for (i = 0; i < count; i++) {
-       memset(&in_creds, 0, sizeof(in_creds));
+       krb5_principal server = NULL;
+       krb5_ticket *ticket = NULL;
+       krb5_creds *out_creds = NULL;
+       char *princ = NULL;
 
-       in_creds.client = me;
+       memset(&in_creds, 0, sizeof(in_creds));
 
        if (sname != NULL) {
            ret = krb5_sname_to_principal(context, names[i],
                                          sname, KRB5_NT_SRV_HST,
-                                         &in_creds.server);
+                                         &server);
        } else {
-           ret = krb5_parse_name(context, names[i], &in_creds.server);
+           ret = krb5_parse_name(context, names[i], &server);
        }
        if (ret) {
            if (!quiet)
                com_err(prog, ret, "while parsing principal name %s", names[i]);
-           errors++;
-           continue;
+           goto error;
        }
         if (unknown == 1) {
-            krb5_princ_type(context, in_creds.server) = KRB5_NT_UNKNOWN;
+            krb5_princ_type(context, server) = KRB5_NT_UNKNOWN;
         }
 
-       ret = krb5_unparse_name(context, in_creds.server, &princ);
+       ret = krb5_unparse_name(context, server, &princ);
        if (ret) {
            com_err(prog, ret,
                    "while formatting parsed principal name for '%s'",
                    names[i]);
-           errors++;
-           continue;
+           goto error;
        }
 
        in_creds.keyblock.enctype = etype;
 
-       ret = krb5_get_credentials(context, canon ? KRB5_GC_CANONICALIZE : 0,
-                                  ccache, &in_creds, &out_creds);
+       if (for_user) {
+           if (!proxy &&
+               !krb5_principal_compare(context, me, server)) {
+               com_err(prog, EINVAL,
+                       "client and server principal names must match");
+               goto error;
+           }
 
-       krb5_free_principal(context, in_creds.server);
+           in_creds.client = for_user_princ;
+           in_creds.server = me;
+
+           ret = krb5_get_credentials_for_user(context, options, ccache,
+                                               &in_creds, NULL, &out_creds);
+       } else {
+           in_creds.client = me;
+           in_creds.server = server;
+           ret = krb5_get_credentials(context, options, ccache,
+                                      &in_creds, &out_creds);
+       }
 
        if (ret) {
            com_err(prog, ret, "while getting credentials for %s", princ);
-
-           krb5_free_unparsed_name(context, princ);
-
-           errors++;
-           continue;
+           goto error;
        }
 
        /* we need a native ticket */
        ret = krb5_decode_ticket(&out_creds->ticket, &ticket);
        if (ret) {
            com_err(prog, ret, "while decoding ticket for %s", princ);
-           krb5_free_creds(context, out_creds);
-           krb5_free_unparsed_name(context, princ);
-
-           errors++;
-           continue;
+           goto error;
        }
-           
+
        if (keytab) {
            ret = krb5_server_decrypt_ticket_keytab(context, keytab, ticket);
            if (ret) {
-               if (!quiet)
-                   printf("%s: kvno = %d, keytab entry invalid", princ, ticket->enc_part.kvno);
+               if (!quiet) {
+                   fprintf(stderr, "%s: kvno = %d, keytab entry invalid\n",
+                           princ, ticket->enc_part.kvno);
+               }
                com_err(prog, ret, "while decrypting ticket for %s", princ);
-               krb5_free_ticket(context, ticket);
-               krb5_free_creds(context, out_creds);
-               krb5_free_unparsed_name(context, princ);
-
-               errors++;
-               continue;
+               goto error;
            }
            if (!quiet)
-               printf("%s: kvno = %d, keytab entry valid\n", princ, ticket->enc_part.kvno);
+               printf("%s: kvno = %d, keytab entry valid\n",
+                      princ, ticket->enc_part.kvno);
+           if (proxy) {
+               krb5_free_creds(context, out_creds);
+               out_creds = NULL;
+
+               in_creds.client = ticket->enc_part2->client;
+               in_creds.server = server;
+
+               ret = krb5_get_credentials_for_proxy(context,
+                                                    KRB5_GC_CANONICALIZE,
+                                                    ccache,
+                                                    &in_creds,
+                                                    ticket,
+                                                    &out_creds);
+               if (ret) {
+                   com_err(prog, ret,
+                           "%s: constrained delegation failed", princ);
+                   goto error;
+               }
+           }
        } else {
            if (!quiet)
                printf("%s: kvno = %d\n", princ, ticket->enc_part.kvno);
        }
 
-       krb5_free_creds(context, out_creds);
-       krb5_free_unparsed_name(context, princ);
+       continue;
+
+error:
+       if (server != NULL)
+           krb5_free_principal(context, server);
+       if (ticket != NULL)
+           krb5_free_ticket(context, ticket);
+       if (out_creds != NULL)
+           krb5_free_creds(context, out_creds);
+       if (princ != NULL)
+           krb5_free_unparsed_name(context, princ);
+       errors++;
     }
 
     if (keytab)
        krb5_kt_close(context, keytab);
     krb5_free_principal(context, me);
+    krb5_free_principal(context, for_user_princ);
     krb5_cc_close(context, ccache);
     krb5_free_context(context);
 
index 90b6d9cf700911ae5866935defbe3b1b79fccbda..dbe5223569b9a1a9cff202968262967c022828d2 100644 (file)
@@ -966,6 +966,21 @@ typedef struct _krb5_pa_for_user {
     krb5_data          auth_package;
 } krb5_pa_for_user;
 
+typedef struct _krb5_s4u_userid {
+    krb5_int32         nonce;
+    krb5_principal     user;
+    krb5_data          subject_cert;
+    krb5_flags         options;
+} krb5_s4u_userid;
+
+#define KRB5_S4U_OPTS_CHECK_LOGON_HOURS                0x40000000 /* check logon hour restrictions */
+#define KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE      0x20000000 /* sign with usage 27 instead of 26 */
+
+typedef struct _krb5_pa_s4u_x509_user {
+    krb5_s4u_userid    user_id;
+    krb5_checksum      cksum;
+} krb5_pa_s4u_x509_user;
+
 enum {
   KRB5_FAST_ARMOR_AP_REQUEST = 0x1
 };
@@ -1295,6 +1310,10 @@ void KRB5_CALLCONV krb5_free_pa_enc_ts
        (krb5_context, krb5_pa_enc_ts *);
 void KRB5_CALLCONV krb5_free_pa_for_user
        (krb5_context, krb5_pa_for_user * );
+void KRB5_CALLCONV krb5_free_s4u_userid_contents
+       (krb5_context, krb5_s4u_userid * );
+void KRB5_CALLCONV krb5_free_pa_s4u_x509_user
+       (krb5_context, krb5_pa_s4u_x509_user * );
 void KRB5_CALLCONV krb5_free_pa_svr_referral_data
        (krb5_context, krb5_pa_svr_referral_data * );
 void KRB5_CALLCONV krb5_free_pa_server_referral_data
@@ -1609,6 +1628,12 @@ krb5_error_code encode_krb5_setpw_req
 krb5_error_code encode_krb5_pa_for_user
        (const krb5_pa_for_user * , krb5_data **);
 
+krb5_error_code encode_krb5_s4u_userid
+       (const krb5_s4u_userid * , krb5_data **);
+
+krb5_error_code encode_krb5_pa_s4u_x509_user
+       (const krb5_pa_s4u_x509_user * , krb5_data **);
+
 krb5_error_code encode_krb5_pa_svr_referral_data
        (const krb5_pa_svr_referral_data * , krb5_data **);
 
@@ -1778,6 +1803,9 @@ krb5_error_code decode_krb5_setpw_req
 krb5_error_code decode_krb5_pa_for_user
        (const krb5_data *, krb5_pa_for_user **);
 
+krb5_error_code decode_krb5_pa_s4u_x509_user
+       (const krb5_data *, krb5_pa_s4u_x509_user **);
+
 krb5_error_code decode_krb5_pa_svr_referral_data
        (const krb5_data *, krb5_pa_svr_referral_data **);
 
@@ -2606,6 +2634,11 @@ krb5_error_code krb5int_send_tgs
                krb5_pa_data * const *,
                const krb5_data *,
                krb5_creds *,
+               krb5_error_code (*gcvt_fct)(krb5_context,
+                                           krb5_keyblock *,
+                                           krb5_kdc_req *,
+                                           void *),
+               void *gcvt_data,
                krb5_response * , krb5_keyblock **subkey);
                 /* The subkey field is an output parameter; if a
                 * tgs-rep is received then the subkey will be filled
@@ -2796,6 +2829,21 @@ krb5int_pac_sign(krb5_context context,
                 const krb5_keyblock *privsvr_key,
                 krb5_data *data);
 
+krb5_error_code KRB5_CALLCONV
+krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
+                             krb5_ccache ccache,
+                             krb5_creds *in_creds,
+                             krb5_data *cert,
+                             krb5_creds **out_creds);
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_credentials_for_proxy(krb5_context context,
+                              krb5_flags options,
+                              krb5_ccache ccache,
+                              krb5_creds *in_creds,
+                              krb5_ticket *evidence_tkt,
+                              krb5_creds **out_creds);
+
 krb5_error_code krb5int_parse_enctype_list(krb5_context context, char *profstr,
                                           krb5_enctype *default_list,
                                           krb5_enctype **result);
index ea81cfeef156cefe934b814445b0e38e0983d117..8c0cd247a2991f73fc84ff850830d98e65889550 100644 (file)
@@ -96,6 +96,8 @@
 #define KRB5_KDB_SUPPORT_DESMD5         0x00004000
 #define        KRB5_KDB_NEW_PRINC              0x00008000
 #define KRB5_KDB_OK_AS_DELEGATE                0x00100000
+#define KRB5_KDB_OK_TO_AUTH_AS_DELEGATE        0x00200000 /* S4U2Self OK */
+#define KRB5_KDB_NO_AUTH_DATA_REQUIRED 0x00400000
 
 /* Creation flags */
 #define KRB5_KDB_CREATE_BTREE          0x00000001
index 59323e23228522d0b0c023e89e2fec5ec88769a2..348be512733ff7d2f4b562ecd4de8255dca9053a 100644 (file)
 #ifndef KRB5_KDB5_EXT__
 #define KRB5_KDB5_EXT__
 
-/* Allowed to use protocol transition */
-#define KRB5_KDB_OK_TO_AUTH_AS_DELEGATE        0x00200000
-/* Service does not require authorization data */
-#define KRB5_KDB_NO_AUTH_DATA_REQUIRED 0x00400000
 /* Private flag used to indicate principal is local TGS */
 #define KRB5_KDB_TICKET_GRANTING_SERVICE       0x01000000
 /* Private flag used to indicate xrealm relationship  is non-transitive */
index bf8c29c59ede05493cc441d5d3eaa809939cfd71..8111c5bb6271ce96ff8f40024fcfae03e304a025 100644 (file)
@@ -631,6 +631,11 @@ krb5_error_code KRB5_CALLCONV
 
 /* Defined in KDC referrals draft */
 #define KRB5_KEYUSAGE_PA_REFERRAL              26 /* XXX note conflict with above */
+
+/* Defined in [MS-SFU] */
+#define KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST 26 /* XXX note conflict with above */
+#define KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY   27 /* XXX note conflict with above */
+
 /* define in draft-ietf-krb-wg-preauth-framework*/
 #define KRB5_KEYUSAGE_FAST_REQ_CHKSUM 50
 #define KRB5_KEYUSAGE_FAST_ENC 51
@@ -1566,6 +1571,10 @@ void KRB5_CALLCONV krb5_free_tgt_creds
 #define        KRB5_GC_USER_USER       1       /* want user-user ticket */
 #define        KRB5_GC_CACHED          2       /* want cached ticket only */
 #define        KRB5_GC_CANONICALIZE    4       /* set canonicalize KDC option */
+#define        KRB5_GC_NO_STORE        8       /* do not store in credentials cache */
+#define        KRB5_GC_FORWARDABLE             16  /* acquire forwardable tickets */
+#define        KRB5_GC_NO_TRANSIT_CHECK        32  /* disable transited check */
+#define        KRB5_GC_CONSTRAINED_DELEGATION  64  /* constrained delegation */
 
 krb5_error_code KRB5_CALLCONV krb5_get_credentials
        (krb5_context,
index 814ace35cc34d120ff1427d09eedc2828ca95686..513e716bbea6e6d9072b416b9f4ce9fbf7fc4981 100644 (file)
@@ -72,7 +72,9 @@ static struct pflag flags[] = {
 {"allow_svr", 9,       KRB5_KDB_DISALLOW_SVR, 1},
 {"password_changing_service",  25,     KRB5_KDB_PWCHANGE_SERVICE,      0 },
 {"support_desmd5",     14,     KRB5_KDB_SUPPORT_DESMD5,        0 },
-{"ok_as_delegate",     14,     KRB5_KDB_OK_AS_DELEGATE,        0 }
+{"ok_as_delegate",     14,     KRB5_KDB_OK_AS_DELEGATE,        0 },
+{"ok_to_auth_as_delegate", 22, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, 0 },
+{"no_auth_data_required", 21,  KRB5_KDB_NO_AUTH_DATA_REQUIRED, 0},
 };
 
 static char *prflags[] = {
@@ -97,6 +99,8 @@ static char *prflags[] = {
     "UNKNOWN_0x00040000",      /* 0x00040000 */
     "UNKNOWN_0x00080000",      /* 0x00080000 */
     "OK_AS_DELEGATE",          /* 0x00100000 */
+    "OK_TO_AUTH_AS_DELEGATE",  /* 0x00200000 */
+    "NO_AUTH_DATA_REQUIRED",   /* 0x00400000 */
 };
 
 char *getenv();
@@ -1123,7 +1127,7 @@ kadmin_addprinc_usage(func)
            "\t\tallow_postdated allow_forwardable allow_tgs_req allow_renewable\n",
            "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n",
            "\t\trequires_hwauth needchange allow_svr password_changing_service\n"
-           "\t\tok_as_delegate\n"
+           "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"
            "\nwhere,\n\t[-x db_princ_args]* - any number of database specific arguments.\n"
            "\t\t\tLook at each database documentation for supported arguments\n");
 }
@@ -1140,7 +1144,7 @@ kadmin_modprinc_usage(func)
            "\t\tallow_postdated allow_forwardable allow_tgs_req allow_renewable\n",
            "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n",
            "\t\trequires_hwauth needchange allow_svr password_changing_service\n"
-           "\t\tok_as_delegate\n"
+           "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"
            "\nwhere,\n\t[-x db_princ_args]* - any number of database specific arguments.\n"
            "\t\t\tLook at each database documentation for supported arguments\n"
        );
index a99dc35ba19b7373c74d1642c7fb6299f9802a4a..37a69e10d9c7d98bb94d41c054c6e06772ff1e09 100644 (file)
@@ -117,7 +117,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
     krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */
     krb5_db_entry client, krbtgt;
     int c_nprincs = 0, k_nprincs = 0;
-    krb5_pa_for_user *for_user = NULL;           /* protocol transition request */
+    krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */
     krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
     unsigned int c_flags = 0, s_flags = 0;       /* client/server KDB flags */
     char *s4u_name = NULL;
@@ -131,7 +131,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
     krb5_data scratch;
 
     session_key.contents = NULL;
-    
+
     retval = decode_krb5_tgs_req(pkt, &request);
     if (retval)
         return retval;
@@ -292,12 +292,20 @@ tgt_again:
         !krb5_principal_compare(kdc_context, tgs_server, server.princ);
 
     /* Check for protocol transition */
-    errcode = kdc_process_s4u2self_req(kdc_context, request, header_enc_tkt->client,
-                                       &server, header_enc_tkt->session, kdc_time,
-                                       &for_user, &client, &c_nprincs, &status);
+    errcode = kdc_process_s4u2self_req(kdc_context,
+                                      request,
+                                      header_enc_tkt->client,
+                                       &server,
+                                      subkey,
+                                      header_enc_tkt->session,
+                                      kdc_time,
+                                       &s4u_x509_user,
+                                      &client,
+                                      &c_nprincs,
+                                      &status);
     if (errcode)
         goto cleanup;
-    if (for_user != NULL)
+    if (s4u_x509_user != NULL)
         setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
 
     /*
@@ -438,19 +446,32 @@ tgt_again:
     /* processing of any of these flags.  For example, some */
     /* realms may refuse to issue renewable tickets         */
 
-    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
+    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) {
         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
-    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
-        if (!krb5_is_tgs_principal(server.princ) &&
-            is_local_principal(server.princ)) {
-            if (isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
-                setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
-            else
+
+        if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
+            /*
+             * If S4U2Self principal is not forwardable, then mark ticket as
+             * unforwardable. This behaviour matches Windows, but it is
+             * different to the MIT AS-REQ path, which returns an error
+             * (KDC_ERR_POLICY) if forwardable tickets cannot be issued.
+             *
+             * Consider this block the S4U2Self equivalent to
+             * validate_forwardable().
+             */
+            if (c_nprincs &&
+                isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
+                clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
+            /*
+             * OK_TO_AUTH_AS_DELEGATE must be set on the service requesting
+             * S4U2Self in order for forwardable tickets to be returned.
+             */
+            else if (!is_referral &&
+                !isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
                 clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
         }
-        if (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
-            clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
     }
+
     if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
 
@@ -560,7 +581,7 @@ tgt_again:
         enc_tkt_reply.times.starttime = 0;
 
     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
-        errcode = krb5_unparse_name(kdc_context, for_user->user, &s4u_name);
+        errcode = krb5_unparse_name(kdc_context, s4u_x509_user->user_id.user, &s4u_name);
     } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
         errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name);
     } else {
@@ -670,8 +691,8 @@ tgt_again:
     enc_tkt_reply.authorization_data = NULL;
 
     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
-        is_local_principal(header_enc_tkt->client))
-        enc_tkt_reply.client = for_user->user;
+        !isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM))
+        enc_tkt_reply.client = s4u_x509_user->user_id.user;
     else
         enc_tkt_reply.client = header_enc_tkt->client;
 
@@ -685,7 +706,8 @@ tgt_again:
                               &encrypting_key, /* U2U or server key */
                               pkt,
                               request,
-                              for_user ? for_user->user : NULL,
+                              s4u_x509_user ?
+                               s4u_x509_user->user_id.user : NULL,
                               header_enc_tkt,
                               &enc_tkt_reply);
     if (errcode) {
@@ -845,6 +867,20 @@ tgt_again:
     /* Start assembling the response */
     reply.msg_type = KRB5_TGS_REP;
     reply.padata = 0;/* always */
+    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
+        find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER) != NULL) {
+        errcode = kdc_make_s4u2self_rep(kdc_context,
+                                        subkey,
+                                        header_ticket->enc_part2->session,
+                                        s4u_x509_user,
+                                        &reply,
+                                        &reply_encpart);
+        if (errcode) {
+            status = "KDC_RETURN_S4U2SELF_PADATA";
+            goto cleanup;
+        }
+    }
+
     reply.client = enc_tkt_reply.client;
     reply.enc_part.kvno = 0;/* We are using the session key */
     reply.ticket = &ticket_reply;
@@ -958,14 +994,18 @@ cleanup:
         krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs);
     if (c_nprincs)
         krb5_db_free_principal(kdc_context, &client, c_nprincs);
-    if (for_user != NULL)
-        krb5_free_pa_for_user(kdc_context, for_user);
+    if (s4u_x509_user != NULL)
+        krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user);
     if (kdc_issued_auth_data != NULL)
         krb5_free_authdata(kdc_context, kdc_issued_auth_data);
     if (s4u_name != NULL)
         free(s4u_name);
     if (subkey != NULL)
         krb5_free_keyblock(kdc_context, subkey);
+    if (reply.padata)
+        krb5_free_pa_data(kdc_context, reply.padata);
+    if (reply_encpart.enc_padata)
+        krb5_free_pa_data(kdc_context, reply_encpart.enc_padata);
 
     return retval;
 }
index 43ea0869ac09af3667eba69de7ce0447d48273f1..504d3fbddc144a7372924ef99b41949178373649 100644 (file)
@@ -544,9 +544,18 @@ handle_tgt_authdata (krb5_context context,
     }
 
     if (ad_nprincs != 0) {
+       /*
+        * This code was submitted by Novell; however there is no
+        * mention in [MS-SFU] of needing to examine the authorization
+        * data to clear the forwardable flag. My understanding is that
+        * the state of the forwardable flag is propagated through the
+        * cross-realm TGTs.
+        */
+#if 0
        if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
            isflagset(ad_entry.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
            clear(enc_tkt_reply->flags, TKT_FLG_FORWARDABLE);
+#endif
 
        krb5_db_free_principal(context, &ad_entry, ad_nprincs);
 
index cc7ae34ed4ce2530dee9f25f4d6ae065985940c9..2149fd1ac2061b50ae998b489130e56322b7260c 100644 (file)
@@ -1348,25 +1348,6 @@ cleanup:
     return (retval);
 }
 
-static krb5_boolean
-enctype_requires_etype_info_2(krb5_enctype enctype)
-{
-    switch(enctype) {
-    case ENCTYPE_DES_CBC_CRC:
-    case ENCTYPE_DES_CBC_MD4:
-    case ENCTYPE_DES_CBC_MD5:
-    case ENCTYPE_DES3_CBC_SHA1:
-    case ENCTYPE_DES3_CBC_RAW:
-    case ENCTYPE_ARCFOUR_HMAC:
-    case ENCTYPE_ARCFOUR_HMAC_EXP :
-       return 0;
-    default:
-       if (krb5_c_valid_enctype(enctype))
-           return 1;
-       else return 0;
-    }
-}
-
 static krb5_boolean
 request_contains_enctype (krb5_context context,  const krb5_kdc_req *request,
                          krb5_enctype enctype)
index 88ef110629c0a2a1fa9496ac0e7b6c5e2ea2a64b..6ac5289538cf4e8afb22e4e9c81a68d05c2c1ffb 100644 (file)
@@ -223,7 +223,7 @@ comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
 krb5_pa_data *
 find_pa_data(krb5_pa_data **padata, krb5_preauthtype pa_type)
 {
-return krb5int_find_pa_data(kdc_context, padata, pa_type);
+    return krb5int_find_pa_data(kdc_context, padata, pa_type);
 }
 
 krb5_error_code 
@@ -371,7 +371,8 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
     }
 
     /* make sure the client is of proper lineage (see above) */
-    if (foreign_server && !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
+    if (foreign_server &&
+       !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
        if (is_local_principal((*ticket)->enc_part2->client)) {
            /* someone in a foreign realm claiming to be local */
            krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
@@ -926,7 +927,8 @@ fail:
  * as a com_err error number!
  */
 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
-KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
+                           KDC_OPT_VALIDATE | KDC_OPT_RENEW | \
+                           KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)
 int
 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
                    krb5_db_entry server, krb5_timestamp kdc_time,
@@ -998,17 +1000,9 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
      *    preauthentication data is absent in the request.
      *
      * Hence, this check most be done after the check for preauth
-     * data, and is now performed by validate_forwardable().
+     * data, and is now performed by validate_forwardable() (the
+     * contents of which were previously below).
      */
-#if 0
-    /* Client and server must allow forwardable tickets */
-    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
-       (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
-        isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
-       *status = "FORWARDABLE NOT ALLOWED";
-       return(KDC_ERR_POLICY);
-    }
-#endif
     
     /* Client and server must allow renewable tickets */
     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
@@ -1793,7 +1787,7 @@ sign_db_authdata (krb5_context context,
 }
 
 static krb5_error_code
-verify_s4u2self_checksum(krb5_context context,
+verify_for_user_checksum(krb5_context context,
                         krb5_keyblock *key,
                         krb5_pa_for_user *req)
 {
@@ -1852,7 +1846,7 @@ verify_s4u2self_checksum(krb5_context context,
                                  &valid);
 
     if (code == 0 && valid == FALSE)
-       code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+       code = KRB5KRB_AP_ERR_MODIFIED;
 
     free(data.data);
 
@@ -1860,55 +1854,246 @@ verify_s4u2self_checksum(krb5_context context,
 }
 
 /*
- * Protocol transition validation code based on AS-REQ
- * validation code
+ * Legacy protocol transition (Windows 2003 and above)
  */
-static int
-validate_s4u2self_request(krb5_kdc_req *request,
-                         const krb5_db_entry *client,
-                         krb5_timestamp kdc_time,
-                         const char **status)
+static krb5_error_code
+kdc_process_for_user(krb5_context context,
+                    krb5_pa_data *pa_data,
+                    krb5_keyblock *tgs_session,
+                    krb5_pa_s4u_x509_user **s4u_x509_user,
+                    const char **status)
 {
-    int                                errcode;
-    krb5_db_entry              server = { 0 };
-    /* The client must not be expired */
-    if (client->expiration && client->expiration < kdc_time) {
-       *status = "CLIENT EXPIRED";
-       return KDC_ERR_NAME_EXP;
+    krb5_error_code            code;
+    krb5_pa_for_user           *for_user;
+    krb5_data                  req_data;
+
+    req_data.length = pa_data->length;
+    req_data.data = (char *)pa_data->contents;
+
+    code = decode_krb5_pa_for_user(&req_data, &for_user);
+    if (code)
+       return code;
+
+    code = verify_for_user_checksum(context, tgs_session, for_user);
+    if (code) {
+       *status = "INVALID_S4U2SELF_CHECKSUM";
+       krb5_free_pa_for_user(kdc_context, for_user);
+       return code;
     }
 
-    /* The client's password must not be expired, unless the server is
-      a KRB5_KDC_PWCHANGE_SERVICE. */
-    if (client->pw_expiration && client->pw_expiration < kdc_time) {
-       *status = "CLIENT KEY EXPIRED";
-       return KDC_ERR_KEY_EXP;
+    *s4u_x509_user = calloc(1, sizeof(krb5_pa_s4u_x509_user));
+    if (*s4u_x509_user == NULL) {
+       krb5_free_pa_for_user(kdc_context, for_user);
+       return ENOMEM;
     }
 
+    (*s4u_x509_user)->user_id.user = for_user->user;
+    for_user->user = NULL;
+    krb5_free_pa_for_user(context, for_user);
+
+    return 0;
+}
+
+static krb5_error_code
+verify_s4u_x509_user_checksum(krb5_context context,
+                             krb5_keyblock *key,
+                             krb5_data *req_data,
+                             krb5_int32 kdc_req_nonce,
+                             krb5_pa_s4u_x509_user *req)
+{
+    krb5_error_code            code;
+    krb5_data                  scratch;
+    krb5_boolean               valid = FALSE;
+
+    if (enctype_requires_etype_info_2(key->enctype) &&
+       !krb5_c_is_keyed_cksum(req->cksum.checksum_type))
+       return KRB5KRB_AP_ERR_INAPP_CKSUM;
+
+    if (req->user_id.nonce != kdc_req_nonce)
+       return KRB5KRB_AP_ERR_MODIFIED;
+
     /*
-     * If the client requires password changing, then return an
-     * error; S4U2Self cannot be used to change a password.
+     * Verify checksum over the encoded userid. If that fails,
+     * re-encode, and verify that. This is similar to the
+     * behaviour in kdc_process_tgs_req().
      */
-    if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE)) {
-       *status = "REQUIRED PWCHANGE";
-       return KDC_ERR_KEY_EXP;
+    if (fetch_asn1_field((unsigned char *)req_data->data, 1, 0, &scratch) < 0)
+       return ASN1_PARSE_ERROR;
+
+    code = krb5_c_verify_checksum(context,
+                                 key,
+                                 KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
+                                 &scratch,
+                                 &req->cksum,
+                                 &valid);
+    if (code != 0)
+       return code;
+
+    if (valid == FALSE) {
+       krb5_data *data;
+
+       code = encode_krb5_s4u_userid(&req->user_id, &data);
+       if (code != 0)
+           return code;
+
+       code = krb5_c_verify_checksum(context,
+                                     key,
+                                     KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
+                                     data,
+                                     &req->cksum,
+                                     &valid);
+
+       krb5_free_data(context, data);
+
+       if (code != 0)
+           return code;
     }
 
-    /* Check to see if client is locked out */
-    if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
-       *status = "CLIENT LOCKED OUT";
-       return KDC_ERR_C_PRINCIPAL_UNKNOWN;
+    return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
+}
+
+/*
+ * New protocol transition request (Windows 2008 and above)
+ */
+static krb5_error_code
+kdc_process_s4u_x509_user(krb5_context context,
+                         krb5_kdc_req *request,
+                         krb5_pa_data *pa_data,
+                         krb5_keyblock *tgs_subkey,
+                         krb5_keyblock *tgs_session,
+                         krb5_pa_s4u_x509_user **s4u_x509_user,
+                         const char **status)
+{
+    krb5_error_code            code;
+    krb5_data                  req_data;
+
+    req_data.length = pa_data->length;
+    req_data.data = (char *)pa_data->contents;
+
+    code = decode_krb5_pa_s4u_x509_user(&req_data, s4u_x509_user);
+    if (code)
+       return code;
+
+    code = verify_s4u_x509_user_checksum(context,
+                                        tgs_subkey ? tgs_subkey :
+                                           tgs_session,
+                                        &req_data,
+                                        request->nonce, *s4u_x509_user);
+
+    if (code) {
+       *status = "INVALID_S4U2SELF_CHECKSUM";
+       krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
+       *s4u_x509_user = NULL;
+       return code;
     }
 
+    if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 ||
+       (*s4u_x509_user)->user_id.subject_cert.length != 0) {
+       *status = "INVALID_S4U2SELF_REQUEST";
+       krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
+       *s4u_x509_user = NULL;
+       return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+    }
+
+    return 0;
+}
+
+krb5_error_code
+kdc_make_s4u2self_rep(krb5_context context,
+                     krb5_keyblock *tgs_subkey,
+                     krb5_keyblock *tgs_session,
+                     krb5_pa_s4u_x509_user *req_s4u_user,
+                     krb5_kdc_rep *reply,
+                     krb5_enc_kdc_rep_part *reply_encpart)
+{
+    krb5_error_code            code;
+    krb5_data                  *data = NULL;
+    krb5_pa_s4u_x509_user      rep_s4u_user;
+    krb5_pa_data               padata;
+    krb5_enctype               enctype;
+    krb5_keyusage              usage;
+
+    memset(&rep_s4u_user, 0, sizeof(rep_s4u_user));
+
+    rep_s4u_user.user_id.nonce   = req_s4u_user->user_id.nonce;
+    rep_s4u_user.user_id.user    = req_s4u_user->user_id.user;
+    rep_s4u_user.user_id.options =
+       req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;
+
+    code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &data);
+    if (code != 0)
+        goto cleanup;
+
+    if (req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE)
+        usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY;
+    else
+        usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST;
+
+    code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type,
+                               tgs_subkey != NULL ? tgs_subkey : tgs_session,
+                                usage, data,
+                                &rep_s4u_user.cksum);
+    if (code != 0)
+        goto cleanup;
+
+    krb5_free_data(context, data);
+    data = NULL;
+
+    code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &data);
+    if (code != 0)
+        goto cleanup;
+
+    padata.magic = KV5M_PA_DATA;
+    padata.pa_type = KRB5_PADATA_S4U_X509_USER;
+    padata.length = data->length;
+    padata.contents = (krb5_octet *)data->data;
+
+    code = add_pa_data_element(context, &padata, &reply->padata, FALSE);
+    if (code != 0)
+       goto cleanup;
+
+    free(data);
+    data = NULL;
+
+    if (tgs_subkey != NULL)
+       enctype = tgs_subkey->enctype;
+    else
+       enctype = tgs_session->enctype;
+
     /*
-     * Check against local policy
+     * Owing to a bug in Windows, unkeyed checksums were used for older
+     * enctypes, including rc4-hmac. A forthcoming workaround for this
+     * includes the checksum bytes in the encrypted padata.
      */
-    errcode = against_local_policy_as(request, *client, server,
-                                     kdc_time, status); 
-    if (errcode)
-       return errcode;
+    if ((req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) &&
+       enctype_requires_etype_info_2(enctype) == FALSE) {
+       padata.length = req_s4u_user->cksum.length +
+                       rep_s4u_user.cksum.length;
+       padata.contents = malloc(padata.length);
+       if (padata.contents == NULL) {
+           code = ENOMEM;
+           goto cleanup;
+       }
 
-    return 0;
+       memcpy(padata.contents,
+              req_s4u_user->cksum.contents,
+              req_s4u_user->cksum.length);
+       memcpy(&padata.contents[req_s4u_user->cksum.length],
+              rep_s4u_user.cksum.contents,
+              rep_s4u_user.cksum.length);
+
+       code = add_pa_data_element(context,&padata,
+                                  &reply_encpart->enc_padata, FALSE);
+       if (code != 0)
+           goto cleanup;
+    }
+
+cleanup:
+    if (rep_s4u_user.cksum.contents != NULL)
+        krb5_free_checksum_contents(context, &rep_s4u_user.cksum);
+    krb5_free_data(context, data);
+
+    return code;
 }
 
 /*
@@ -1919,92 +2104,116 @@ kdc_process_s4u2self_req(krb5_context context,
                         krb5_kdc_req *request,
                         krb5_const_principal client_princ,
                         const krb5_db_entry *server,
-                        krb5_keyblock *subkey,
+                        krb5_keyblock *tgs_subkey,
+                        krb5_keyblock *tgs_session,
                         krb5_timestamp kdc_time,
-                        krb5_pa_for_user **for_user,
+                        krb5_pa_s4u_x509_user **s4u_x509_user,
                         krb5_db_entry *princ,
                         int *nprincs,
                         const char **status)
 {
     krb5_error_code            code;
-    krb5_pa_data               **pa_data;
-    krb5_data                  req_data;
+    krb5_pa_data               *pa_data;
     krb5_boolean               more;
+    int                                flags;
 
     *nprincs = 0;
     memset(princ, 0, sizeof(*princ));
 
-    if (request->padata == NULL) {
-       return 0;
-    }
-
-    for (pa_data = request->padata; *pa_data != NULL; pa_data++) {
-       if ((*pa_data)->pa_type == KRB5_PADATA_FOR_USER)
-           break;
-    }
-    if (*pa_data == NULL) {
-       return 0;
+    pa_data = find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER);
+    if (pa_data != NULL) {
+       code = kdc_process_s4u_x509_user(context,
+                                        request,
+                                        pa_data,
+                                        tgs_subkey,
+                                        tgs_session,
+                                        s4u_x509_user,
+                                        status);
+       if (code != 0)
+           return code;
+    } else {
+       pa_data = find_pa_data(request->padata, KRB5_PADATA_FOR_USER);
+       if (pa_data != NULL) {
+           code = kdc_process_for_user(context,
+                                       pa_data,
+                                       tgs_session,
+                                       s4u_x509_user,
+                                       status);
+           if (code != 0)
+               return code;
+       } else
+           return 0;
     }
 
-#if 0
     /*
-     * Ignore request if the server principal is a TGS, not so much
-     * to avoid unconstrained tickets being issued (as that would
-     * require knowing the TGS key anyway) but so that we do not
-     * block the server referral path.
+     * We need to compare the client name in the TGT with the requested
+     * server name. Supporting server name aliases without assuming a
+     * global name service makes this difficult to do.
+     *
+     * The comparison below handles the following cases (note that the
+     * term "principal name" below excludes the realm).
+     *
+     * (1) The requested service is a host-based service with two name
+     *     components, in which case we assume the principal name to
+     *     contain sufficient qualifying information. The realm is
+     *     ignored for the purpose of comparison.
+     *
+     * (2) The requested service name is an enterprise principal name:
+     *     the service principal name is compared with the unparsed
+     *     form of the client name (including its realm).
+     *
+     * (3) The requested service is some other name type: an exact
+     *     match is required.
+     *
+     * An alternative would be to look up the server once again with
+     * FLAG_CANONICALIZE | FLAG_CLIENT_REFERRALS_ONLY set, do an exact
+     * match between the returned name and client_princ. However, this
+     * assumes that the client set FLAG_CANONICALIZE when requesting
+     * the TGT and that we have a global name service.
      */
-    if (krb5_is_tgs_principal(server->princ)) {
-       return 0;
-    }
-#endif
-
-    *status = "PROCESS_S4U2SELF_REQUEST";
-
-    req_data.length = (*pa_data)->length;
-    req_data.data = (char *)(*pa_data)->contents;
-
-    code = decode_krb5_pa_for_user(&req_data, for_user);
-    if (code) {
-       return code;
-    }
-
-    if (krb5_princ_type(context, (*for_user)->user) !=
-       KRB5_NT_ENTERPRISE_PRINCIPAL) {
-       *status = "INVALID_S4U2SELF_REQUEST";
-       return KRB5KDC_ERR_POLICY;
+    flags = 0;
+    switch (krb5_princ_type(kdc_context, request->server)) {
+    case KRB5_NT_SRV_HST:                  /* (1) */
+       if (krb5_princ_size(kdc_context, request->server) == 2)
+           flags |= KRB5_PRINCIPAL_COMPARE_IGNORE_REALM;
+       break;
+    case KRB5_NT_ENTERPRISE_PRINCIPAL:     /* (2) */
+       flags |= KRB5_PRINCIPAL_COMPARE_ENTERPRISE;
+       break;
+    default:                               /* (3) */
+       break;
     }
 
-    code = verify_s4u2self_checksum(context, subkey, *for_user);
-    if (code) {
-       *status = "INVALID_S4U2SELF_CHECKSUM";
-       krb5_free_pa_for_user(kdc_context, *for_user);
-       *for_user = NULL;
-       return code;
-    }
-    if (!krb5_principal_compare_flags(context, request->server, client_princ,
-                                     KRB5_PRINCIPAL_COMPARE_ENTERPRISE)) {
+    if (!krb5_principal_compare_flags(context,
+                                     request->server,
+                                     client_princ,
+                                     flags)) {
        *status = "INVALID_S4U2SELF_REQUEST";
-       return KRB5KDC_ERR_POLICY;
+       return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error code */
     }
 
     /*
      * Protocol transition is mutually exclusive with renew/forward/etc
-     * as well as user-to-user and constrained delegation.
+     * as well as user-to-user and constrained delegation. This check
+     * is also made in validate_as_request().
      *
      * We can assert from this check that the header ticket was a TGT, as
      * that is validated previously in validate_tgs_request().
      */
-    if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
+    if (request->kdc_options & AS_INVALID_OPTIONS) {
+       *status = "INVALID AS OPTIONS";
        return KRB5KDC_ERR_BADOPTION;
     }
 
     /*
      * Do not attempt to lookup principals in foreign realms.
      */
-    if (is_local_principal((*for_user)->user)) {
+    if (is_local_principal((*s4u_x509_user)->user_id.user)) {
+       krb5_db_entry no_server;
+
        *nprincs = 1;
        code = krb5_db_get_principal_ext(kdc_context,
-                                        (*for_user)->user,
+                                        (*s4u_x509_user)->user_id.user,
                                         KRB5_KDB_FLAG_INCLUDE_PAC,
                                         princ, nprincs, &more);
        if (code) {
@@ -2021,14 +2230,15 @@ kdc_process_s4u2self_req(krb5_context context,
            return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
        }
 
-       code = validate_s4u2self_request(request, princ, kdc_time, status);
+       memset(&no_server, 0, sizeof(no_server));
+
+       code = validate_as_request(request, *princ,
+                                  no_server, kdc_time, status);
        if (code) {
            return code;
        }
     }
 
-    *status = NULL;
-
     return 0;
 }
 
@@ -2049,7 +2259,7 @@ check_allowed_to_delegate_to(krb5_context context,
 
     /* Must be in same realm */
     if (!krb5_realm_compare(context, server->princ, proxy)) {
-       return KRB5KDC_ERR_BADOPTION;
+       return KRB5KDC_ERR_POLICY;
     }
 
     req.server = server;
@@ -2345,3 +2555,63 @@ log_tgs_alt_tgt(krb5_principal p)
     /* OpenSolaris: audit_krb5kdc_tgs_req_alt_tgt(...) */
 }
 
+krb5_boolean
+enctype_requires_etype_info_2(krb5_enctype enctype)
+{
+    switch(enctype) {
+    case ENCTYPE_DES_CBC_CRC:
+    case ENCTYPE_DES_CBC_MD4:
+    case ENCTYPE_DES_CBC_MD5:
+    case ENCTYPE_DES3_CBC_SHA1:
+    case ENCTYPE_DES3_CBC_RAW:
+    case ENCTYPE_ARCFOUR_HMAC:
+    case ENCTYPE_ARCFOUR_HMAC_EXP :
+       return 0;
+    default:
+       return krb5_c_valid_enctype(enctype);
+    }
+}
+
+/* XXX where are the generic helper routines for this? */
+krb5_error_code
+add_pa_data_element(krb5_context context,
+                   krb5_pa_data *padata,
+                   krb5_pa_data ***inout_padata,
+                   krb5_boolean copy)
+{
+    int                                i;
+    krb5_pa_data               **p;
+
+    if (*inout_padata != NULL) {
+       for (i = 0; (*inout_padata)[i] != NULL; i++)
+           ;
+    } else
+       i = 0;
+
+    p = realloc(*inout_padata, (i + 2) * sizeof(krb5_pa_data *));
+    if (p == NULL)
+       return ENOMEM;
+
+    *inout_padata = p;
+
+    p[i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
+    if (p[i] == NULL)
+       return ENOMEM;
+    *(p[i]) = *padata;
+
+    p[i + 1] = NULL;
+
+    if (copy) {
+       p[i]->contents = (krb5_octet *)malloc(padata->length);
+       if (p[i]->contents == NULL) {
+           free(p[i]);
+           p[i] = NULL;
+           return ENOMEM;
+       }
+
+       memcpy(p[i]->contents, padata->contents, padata->length);
+    }
+
+    return 0;
+}
+
index 0604426045e5b5e426b2736b340329692807e84b..cb8fb5f7afa8bcf88f57bd6ac1de1e079cf03d89 100644 (file)
@@ -150,6 +150,8 @@ int against_local_policy_tgs (krb5_kdc_req *, krb5_db_entry,
                                        krb5_ticket *, const char **);
 
 /* kdc_preauth.c */
+krb5_boolean enctype_requires_etype_info_2(krb5_enctype enctype);
+
 const char * missing_required_preauth
     (krb5_db_entry *client, krb5_db_entry *server,
               krb5_enc_tkt_part *enc_tkt_reply);
@@ -177,6 +179,12 @@ krb5_error_code free_padata_context
 krb5_pa_data *find_pa_data
     (krb5_pa_data **padata, krb5_preauthtype pa_type);
 
+krb5_error_code add_pa_data_element
+    (krb5_context context,
+                   krb5_pa_data *padata,
+                   krb5_pa_data ***out_padata,
+                   krb5_boolean copy);
+
 /* kdc_authdata.c */
 krb5_error_code load_authdata_plugins(krb5_context context);
 krb5_error_code unload_authdata_plugins(krb5_context context);
@@ -239,13 +247,22 @@ krb5_error_code kdc_process_s4u2self_req
                krb5_kdc_req *request,
                krb5_const_principal client_princ,
                const krb5_db_entry *server,
-               krb5_keyblock *subkey,
+               krb5_keyblock *tgs_subkey,
+               krb5_keyblock *tgs_session,
                krb5_timestamp kdc_time,
-               krb5_pa_for_user **s4u2_req,
+               krb5_pa_s4u_x509_user **s4u2self_req,
                krb5_db_entry *princ,
                int *nprincs,
                const char **status);
 
+krb5_error_code kdc_make_s4u2self_rep
+       (krb5_context context,
+               krb5_keyblock *tgs_subkey,
+               krb5_keyblock *tgs_session,
+               krb5_pa_s4u_x509_user *req_s4u_user,
+               krb5_kdc_rep *reply,
+               krb5_enc_kdc_rep_part *reply_encpart);
+
 krb5_error_code kdc_process_s4u2proxy_req
        (krb5_context context,
                krb5_kdc_req *request,
diff --git a/src/lib/crypto/krb/enc_provider/Makefile.in b/src/lib/crypto/krb/enc_provider/Makefile.in
new file mode 100644 (file)
index 0000000..2eedf1d
--- /dev/null
@@ -0,0 +1,41 @@
+thisconfigdir=../../../..
+myfulldir=lib/crypto/krb/enc_provider
+mydir=lib/crypto/krb/enc_provider
+BUILDTOP=$(REL)..$(S)..$(S)..$(S)..
+LOCALINCLUDES = -I$(srcdir)/../../@CRYPTO_IMPL@/des -I$(srcdir)/../../@CRYPTO_IMPL@/arcfour    \
+               -I$(srcdir)/../../@CRYPTO_IMPL@/aes -I$(srcdir)/.. -I$(srcdir)/../../@CRYPTO_IMPL@ 
+DEFS=
+
+##DOS##BUILDTOP = ..\..\..\..
+##DOS##PREFIXDIR=enc_provider
+##DOS##OBJFILE=..\$(OUTPRE)enc_prov.lst
+
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+
+STLIBOBJS= des.o des3.o rc4.o aes.o 
+
+OBJS= \
+       $(OUTPRE)des.$(OBJEXT) \
+       $(OUTPRE)des3.$(OBJEXT) \
+       $(OUTPRE)aes.$(OBJEXT) \
+       $(OUTPRE)rc4.$(OBJEXT)
+
+SRCS= \
+       $(srcdir)/des.c \
+       $(srcdir)/des3.c \
+       $(srcdir)/aes.c \
+       $(srcdir)/rc4.c
+
+##DOS##LIBOBJS = $(OBJS)
+
+all-unix:: all-libobjs
+
+includes:: depend
+
+depend:: $(SRCS)
+
+clean-unix:: clean-libobjs
+
+@libobj_frag@
+
diff --git a/src/lib/crypto/krb/enc_provider/aes.c b/src/lib/crypto/krb/enc_provider/aes.c
new file mode 100644 (file)
index 0000000..060d119
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * lib/crypto/enc_provider/aes.c
+ *
+ * Copyright (C) 2003, 2007, 2008 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.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ */
+
+#include "k5-int.h"
+#include "enc_provider.h"
+#include "aes.h"
+#include "../aead.h"
+
+#if 0
+aes_rval aes_blk_len(unsigned int blen, aes_ctx cx[1]);
+aes_rval aes_enc_key(const unsigned char in_key[], unsigned int klen, aes_ctx cx[1]);
+aes_rval aes_enc_blk(const unsigned char in_blk[], unsigned char out_blk[], const aes_ctx cx[1]);
+aes_rval aes_dec_key(const unsigned char in_key[], unsigned int klen, aes_ctx cx[1]);
+aes_rval aes_dec_blk(const unsigned char in_blk[], unsigned char out_blk[], const aes_ctx cx[1]);
+#endif
+
+#define CHECK_SIZES 0
+
+#if 0
+static void printd (const char *descr, krb5_data *d) {
+    int i, j;
+    const int r = 16;
+
+    printf("%s:", descr);
+
+    for (i = 0; i < d->length; i += r) {
+       printf("\n  %04x: ", i);
+       for (j = i; j < i + r && j < d->length; j++)
+           printf(" %02x", 0xff & d->data[j]);
+#ifdef SHOW_TEXT
+       for (; j < i + r; j++)
+           printf("   ");
+       printf("   ");
+       for (j = i; j < i + r && j < d->length; j++) {
+           int c = 0xff & d->data[j];
+           printf("%c", isprint(c) ? c : '.');
+       }
+#endif
+    }
+    printf("\n");
+}
+#endif
+
+static inline void enc(char *out, const char *in, aes_ctx *ctx)
+{
+    if (aes_enc_blk((const unsigned char *)in, (unsigned char *)out, ctx)
+       != aes_good)
+       abort();
+}
+static inline void dec(char *out, const char *in, aes_ctx *ctx)
+{
+    if (aes_dec_blk((const unsigned char *)in, (unsigned char *)out, ctx)
+       != aes_good)
+       abort();
+}
+
+static void xorblock(char *out, const char *in)
+{
+    int z;
+    for (z = 0; z < BLOCK_SIZE; z++)
+       out[z] ^= in[z];
+}
+
+krb5_error_code
+krb5int_aes_encrypt(const krb5_keyblock *key, const krb5_data *ivec,
+                   const krb5_data *input, krb5_data *output)
+{
+    aes_ctx ctx;
+    char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
+    int nblocks = 0, blockno;
+
+/*    CHECK_SIZES; */
+
+    if (aes_enc_key(key->contents, key->length, &ctx) != aes_good)
+       abort();
+
+    if (ivec)
+       memcpy(tmp, ivec->data, BLOCK_SIZE);
+    else
+       memset(tmp, 0, BLOCK_SIZE);
+
+    nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+    if (nblocks == 1) {
+       /* XXX Used for DK function.  */
+       enc(output->data, input->data, &ctx);
+    } else {
+       unsigned int nleft;
+
+       for (blockno = 0; blockno < nblocks - 2; blockno++) {
+           xorblock(tmp, input->data + blockno * BLOCK_SIZE);
+           enc(tmp2, tmp, &ctx);
+           memcpy(output->data + blockno * BLOCK_SIZE, tmp2, BLOCK_SIZE);
+
+           /* Set up for next block.  */
+           memcpy(tmp, tmp2, BLOCK_SIZE);
+       }
+       /* Do final CTS step for last two blocks (the second of which
+          may or may not be incomplete).  */
+       xorblock(tmp, input->data + (nblocks - 2) * BLOCK_SIZE);
+       enc(tmp2, tmp, &ctx);
+       nleft = input->length - (nblocks - 1) * BLOCK_SIZE;
+       memcpy(output->data + (nblocks - 1) * BLOCK_SIZE, tmp2, nleft);
+       memcpy(tmp, tmp2, BLOCK_SIZE);
+
+       memset(tmp3, 0, sizeof(tmp3));
+       memcpy(tmp3, input->data + (nblocks - 1) * BLOCK_SIZE, nleft);
+       xorblock(tmp, tmp3);
+       enc(tmp2, tmp, &ctx);
+       memcpy(output->data + (nblocks - 2) * BLOCK_SIZE, tmp2, BLOCK_SIZE);
+       if (ivec)
+           memcpy(ivec->data, tmp2, BLOCK_SIZE);
+    }
+
+    return 0;
+}
+
+krb5_error_code
+krb5int_aes_decrypt(const krb5_keyblock *key, const krb5_data *ivec,
+                   const krb5_data *input, krb5_data *output)
+{
+    aes_ctx ctx;
+    char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
+    int nblocks = 0, blockno;
+
+    CHECK_SIZES;
+
+    if (aes_dec_key(key->contents, key->length, &ctx) != aes_good)
+       abort();
+
+    if (ivec)
+       memcpy(tmp, ivec->data, BLOCK_SIZE);
+    else
+       memset(tmp, 0, BLOCK_SIZE);
+
+    nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+    if (nblocks == 1) {
+       if (input->length < BLOCK_SIZE)
+           abort();
+       dec(output->data, input->data, &ctx);
+    } else {
+
+       for (blockno = 0; blockno < nblocks - 2; blockno++) {
+           dec(tmp2, input->data + blockno * BLOCK_SIZE, &ctx);
+           xorblock(tmp2, tmp);
+           memcpy(output->data + blockno * BLOCK_SIZE, tmp2, BLOCK_SIZE);
+           memcpy(tmp, input->data + blockno * BLOCK_SIZE, BLOCK_SIZE);
+       }
+       /* Do last two blocks, the second of which (next-to-last block
+          of plaintext) may be incomplete.  */
+       dec(tmp2, input->data + (nblocks - 2) * BLOCK_SIZE, &ctx);
+       /* Set tmp3 to last ciphertext block, padded.  */
+       memset(tmp3, 0, sizeof(tmp3));
+       memcpy(tmp3, input->data + (nblocks - 1) * BLOCK_SIZE,
+              input->length - (nblocks - 1) * BLOCK_SIZE);
+       /* Set tmp2 to last (possibly partial) plaintext block, and
+          save it.  */
+       xorblock(tmp2, tmp3);
+       memcpy(output->data + (nblocks - 1) * BLOCK_SIZE, tmp2,
+              input->length - (nblocks - 1) * BLOCK_SIZE);
+       /* Maybe keep the trailing part, and copy in the last
+          ciphertext block.  */
+       memcpy(tmp2, tmp3, input->length - (nblocks - 1) * BLOCK_SIZE);
+       /* Decrypt, to get next to last plaintext block xor previous
+          ciphertext.  */
+       dec(tmp3, tmp2, &ctx);
+       xorblock(tmp3, tmp);
+       memcpy(output->data + (nblocks - 2) * BLOCK_SIZE, tmp3, BLOCK_SIZE);
+       if (ivec)
+           memcpy(ivec->data, input->data + (nblocks - 2) * BLOCK_SIZE,
+                  BLOCK_SIZE);
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+krb5int_aes_encrypt_iov(const krb5_keyblock *key,
+                       const krb5_data *ivec,
+                       krb5_crypto_iov *data,
+                       size_t num_data)
+{
+    aes_ctx ctx;
+    char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE];
+    int nblocks = 0, blockno;
+    size_t input_length, i;
+
+    if (aes_enc_key(key->contents, key->length, &ctx) != aes_good)
+       abort();
+
+    if (ivec != NULL)
+       memcpy(tmp, ivec->data, BLOCK_SIZE);
+    else
+       memset(tmp, 0, BLOCK_SIZE);
+
+    for (i = 0, input_length = 0; i < num_data; i++) {
+       krb5_crypto_iov *iov = &data[i];
+
+       if (ENCRYPT_IOV(iov))
+           input_length += iov->data.length;
+    }
+
+    nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+    assert(nblocks > 1);
+
+    {
+       char blockN2[BLOCK_SIZE];   /* second last */
+       char blockN1[BLOCK_SIZE];   /* last block */
+       struct iov_block_state input_pos, output_pos;
+
+       IOV_BLOCK_STATE_INIT(&input_pos);
+       IOV_BLOCK_STATE_INIT(&output_pos);
+
+       for (blockno = 0; blockno < nblocks - 2; blockno++) {
+           char blockN[BLOCK_SIZE];
+
+           krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos);
+           xorblock(tmp, blockN);
+           enc(tmp2, tmp, &ctx);
+           krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos);
+
+           /* Set up for next block.  */
+           memcpy(tmp, tmp2, BLOCK_SIZE);
+       }
+
+       /* Do final CTS step for last two blocks (the second of which
+          may or may not be incomplete).  */
+
+       /* First, get the last two blocks */
+       memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
+       krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos);
+       krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos);
+
+       /* Encrypt second last block */
+       xorblock(tmp, blockN2);
+       enc(tmp2, tmp, &ctx);
+       memcpy(blockN2, tmp2, BLOCK_SIZE); /* blockN2 now contains first block */
+       memcpy(tmp, tmp2, BLOCK_SIZE);
+
+       /* Encrypt last block */
+       xorblock(tmp, blockN1);
+       enc(tmp2, tmp, &ctx);
+       memcpy(blockN1, tmp2, BLOCK_SIZE);
+
+       /* Put the last two blocks back into the iovec (reverse order) */
+       krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos);
+       krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos);
+
+       if (ivec != NULL)
+           memcpy(ivec->data, blockN1, BLOCK_SIZE);
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+krb5int_aes_decrypt_iov(const krb5_keyblock *key,
+                       const krb5_data *ivec,
+                       krb5_crypto_iov *data,
+                       size_t num_data)
+{
+    aes_ctx ctx;
+    char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
+    int nblocks = 0, blockno;
+    unsigned int i;
+    size_t input_length;
+
+    CHECK_SIZES;
+
+    if (aes_dec_key(key->contents, key->length, &ctx) != aes_good)
+       abort();
+
+    if (ivec != NULL)
+       memcpy(tmp, ivec->data, BLOCK_SIZE);
+    else
+       memset(tmp, 0, BLOCK_SIZE);
+
+    for (i = 0, input_length = 0; i < num_data; i++) {
+       krb5_crypto_iov *iov = &data[i];
+
+       if (ENCRYPT_IOV(iov))
+           input_length += iov->data.length;
+    }
+
+    nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+    assert(nblocks > 1);
+
+    {
+       char blockN2[BLOCK_SIZE];   /* second last */
+       char blockN1[BLOCK_SIZE];   /* last block */
+       struct iov_block_state input_pos, output_pos;
+
+       IOV_BLOCK_STATE_INIT(&input_pos);
+       IOV_BLOCK_STATE_INIT(&output_pos);
+
+       for (blockno = 0; blockno < nblocks - 2; blockno++) {
+           char blockN[BLOCK_SIZE];
+
+           krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos);
+           dec(tmp2, blockN, &ctx);
+           xorblock(tmp2, tmp);
+           krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos);
+           memcpy(tmp, blockN, BLOCK_SIZE);
+       }
+
+       /* Do last two blocks, the second of which (next-to-last block
+          of plaintext) may be incomplete.  */
+
+       /* First, get the last two encrypted blocks */
+       memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
+       krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos);
+       krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos);
+
+       /* Decrypt second last block */
+       dec(tmp2, blockN2, &ctx);
+       /* Set tmp2 to last (possibly partial) plaintext block, and
+          save it.  */
+       xorblock(tmp2, blockN1);
+       memcpy(blockN2, tmp2, BLOCK_SIZE);
+
+       /* Maybe keep the trailing part, and copy in the last
+          ciphertext block.  */
+       input_length %= BLOCK_SIZE;
+       memcpy(tmp2, blockN1, input_length ? input_length : BLOCK_SIZE);
+       dec(tmp3, tmp2, &ctx);
+       xorblock(tmp3, tmp);
+       /* Copy out ivec first before we clobber blockN1 with plaintext */
+       if (ivec != NULL)
+           memcpy(ivec->data, blockN1, BLOCK_SIZE);
+       memcpy(blockN1, tmp3, BLOCK_SIZE);
+
+       /* Put the last two blocks back into the iovec */
+       krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos);
+       krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos);
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+k5_aes_make_key(const krb5_data *randombits, krb5_keyblock *key)
+{
+    if (key->length != 16 && key->length != 32)
+       return(KRB5_BAD_KEYSIZE);
+    if (randombits->length != key->length)
+       return(KRB5_CRYPTO_INTERNAL);
+
+    key->magic = KV5M_KEYBLOCK;
+
+    memcpy(key->contents, randombits->data, randombits->length);
+    return(0);
+}
+
+static krb5_error_code
+krb5int_aes_init_state (const krb5_keyblock *key, krb5_keyusage usage,
+                       krb5_data *state)
+{
+    state->length = 16;
+    state->data = (void *) malloc(16);
+    if (state->data == NULL)
+       return ENOMEM;
+    memset(state->data, 0, state->length);
+    return 0;
+}
+
+const struct krb5_enc_provider krb5int_enc_aes128 = {
+    16,
+    16, 16,
+    krb5int_aes_encrypt,
+    krb5int_aes_decrypt,
+    k5_aes_make_key,
+    krb5int_aes_init_state,
+    krb5int_default_free_state,
+    krb5int_aes_encrypt_iov,
+    krb5int_aes_decrypt_iov
+};
+
+const struct krb5_enc_provider krb5int_enc_aes256 = {
+    16,
+    32, 32,
+    krb5int_aes_encrypt,
+    krb5int_aes_decrypt,
+    k5_aes_make_key,
+    krb5int_aes_init_state,
+    krb5int_default_free_state,
+    krb5int_aes_encrypt_iov,
+    krb5int_aes_decrypt_iov
+};
+
diff --git a/src/lib/crypto/krb/enc_provider/deps b/src/lib/crypto/krb/enc_provider/deps
new file mode 100644 (file)
index 0000000..0649762
--- /dev/null
@@ -0,0 +1,49 @@
+# 
+# Generated makefile dependencies follow.
+#
+des.so des.po $(OUTPRE)des.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
+  $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \
+  $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  $(srcdir)/../../builtin/des/des_int.h $(srcdir)/../aead.h \
+  $(srcdir)/../cksumtypes.h des.c enc_provider.h
+des3.so des3.po $(OUTPRE)des3.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
+  $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \
+  $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  $(srcdir)/../../builtin/des/des_int.h $(srcdir)/../aead.h \
+  $(srcdir)/../cksumtypes.h des3.c
+aes.so aes.po $(OUTPRE)aes.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
+  $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \
+  $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  $(srcdir)/../../builtin/aes/aes.h $(srcdir)/../../builtin/aes/uitypes.h \
+  $(srcdir)/../aead.h $(srcdir)/../cksumtypes.h aes.c \
+  enc_provider.h
+rc4.so rc4.po $(OUTPRE)rc4.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
+  $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \
+  $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  $(srcdir)/../../builtin/arcfour/arcfour-int.h $(srcdir)/../../builtin/arcfour/arcfour.h \
+  $(srcdir)/../aead.h $(srcdir)/../cksumtypes.h enc_provider.h \
+  rc4.c
diff --git a/src/lib/crypto/krb/enc_provider/des.c b/src/lib/crypto/krb/enc_provider/des.c
new file mode 100644 (file)
index 0000000..547f6b9
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ * 
+ * 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 FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "k5-int.h"
+#include "des_int.h"
+#include "enc_provider.h"
+#include "aead.h"
+
+static krb5_error_code
+k5_des_docrypt(const krb5_keyblock *key, const krb5_data *ivec,
+              const krb5_data *input, krb5_data *output, int enc)
+{
+    mit_des_key_schedule schedule;
+
+    /* key->enctype was checked by the caller */
+
+    if (key->length != 8)
+       return(KRB5_BAD_KEYSIZE);
+    if ((input->length%8) != 0)
+       return(KRB5_BAD_MSIZE);
+    if (ivec && (ivec->length != 8))
+       return(KRB5_BAD_MSIZE);
+    if (input->length != output->length)
+       return(KRB5_BAD_MSIZE);
+
+    switch (mit_des_key_sched(key->contents, schedule)) {
+    case -1:
+       return(KRB5DES_BAD_KEYPAR);
+    case -2:
+       return(KRB5DES_WEAK_KEY);
+    }
+
+    /* this has a return value, but the code always returns zero */
+
+    mit_des_cbc_encrypt((krb5_pointer) input->data,
+                       (krb5_pointer) output->data, input->length,
+                       schedule,
+                       (ivec
+                        ? (const unsigned char *) ivec->data
+                        : (const unsigned char *) mit_des_zeroblock),
+                       enc);
+
+    memset(schedule, 0, sizeof(schedule));
+
+    return(0);
+}
+
+static krb5_error_code
+k5_des_encrypt(const krb5_keyblock *key, const krb5_data *ivec,
+              const krb5_data *input, krb5_data *output)
+{
+    return(k5_des_docrypt(key, ivec, input, output, 1));
+}
+
+static krb5_error_code
+k5_des_decrypt(const krb5_keyblock *key, const krb5_data *ivec,
+              const krb5_data *input, krb5_data *output)
+{
+    return(k5_des_docrypt(key, ivec, input, output, 0));
+}
+
+static krb5_error_code
+k5_des_make_key(const krb5_data *randombits, krb5_keyblock *key)
+{
+    if (key->length != 8)
+       return(KRB5_BAD_KEYSIZE);
+    if (randombits->length != 7)
+       return(KRB5_CRYPTO_INTERNAL);
+
+    key->magic = KV5M_KEYBLOCK;
+    key->length = 8;
+
+    /* take the seven bytes, move them around into the top 7 bits of the
+       8 key bytes, then compute the parity bits */
+
+    memcpy(key->contents, randombits->data, randombits->length);
+    key->contents[7] = (((key->contents[0]&1)<<1) | ((key->contents[1]&1)<<2) |
+                       ((key->contents[2]&1)<<3) | ((key->contents[3]&1)<<4) |
+                       ((key->contents[4]&1)<<5) | ((key->contents[5]&1)<<6) |
+                       ((key->contents[6]&1)<<7));
+
+    mit_des_fixup_key_parity(key->contents);
+
+    return(0);
+}
+
+static krb5_error_code
+k5_des_docrypt_iov(const krb5_keyblock *key, const krb5_data *ivec,
+                  krb5_crypto_iov *data, size_t num_data, int enc)
+{
+    mit_des_key_schedule schedule;
+    size_t input_length = 0;
+    unsigned int i;
+
+    /* key->enctype was checked by the caller */
+
+    if (key->length != 8)
+       return(KRB5_BAD_KEYSIZE);
+
+    for (i = 0; i < num_data; i++) {
+       const krb5_crypto_iov *iov = &data[i];
+
+       if (ENCRYPT_DATA_IOV(iov))
+           input_length += iov->data.length;
+    }
+
+    if ((input_length % 8) != 0)
+       return(KRB5_BAD_MSIZE);
+    if (ivec && (ivec->length != 8))
+       return(KRB5_BAD_MSIZE);
+
+    switch (mit_des_key_sched(key->contents, schedule)) {
+    case -1:
+       return(KRB5DES_BAD_KEYPAR);
+    case -2:
+       return(KRB5DES_WEAK_KEY);
+    }
+
+    /* this has a return value, but the code always returns zero */
+    if (enc)
+       krb5int_des_cbc_encrypt_iov(data, num_data, schedule, ivec ? ivec->data : NULL);
+    else
+       krb5int_des_cbc_decrypt_iov(data, num_data, schedule, ivec ? ivec->data : NULL);
+
+    memset(schedule, 0, sizeof(schedule));
+
+    return(0);
+}
+
+static krb5_error_code
+k5_des_encrypt_iov(const krb5_keyblock *key,
+                   const krb5_data *ivec,
+                   krb5_crypto_iov *data,
+                   size_t num_data)
+{
+    return k5_des_docrypt_iov(key, ivec, data, num_data, 1);
+}
+
+static krb5_error_code
+k5_des_decrypt_iov(const krb5_keyblock *key,
+                  const krb5_data *ivec,
+                  krb5_crypto_iov *data,
+                  size_t num_data)
+{
+    return k5_des_docrypt_iov(key, ivec, data, num_data, 0);
+}
+
+const struct krb5_enc_provider krb5int_enc_des = {
+    8,
+    7, 8,
+    k5_des_encrypt,
+    k5_des_decrypt,
+    k5_des_make_key,
+    krb5int_des_init_state,
+    krb5int_default_free_state,
+    k5_des_encrypt_iov,
+    k5_des_decrypt_iov
+};
diff --git a/src/lib/crypto/krb/enc_provider/des3.c b/src/lib/crypto/krb/enc_provider/des3.c
new file mode 100644 (file)
index 0000000..412c994
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ * 
+ * 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 FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "k5-int.h"
+#include "des_int.h"
+#include "../aead.h"
+
+static krb5_error_code
+validate_and_schedule(const krb5_keyblock *key, const krb5_data *ivec,
+                     const krb5_data *input, const krb5_data *output,
+                     mit_des3_key_schedule *schedule)
+{
+    /* key->enctype was checked by the caller */
+
+    if (key->length != 24)
+       return(KRB5_BAD_KEYSIZE);
+    if ((input->length%8) != 0)
+       return(KRB5_BAD_MSIZE);
+    if (ivec && (ivec->length != 8))
+       return(KRB5_BAD_MSIZE);
+    if (input->length != output->length)
+       return(KRB5_BAD_MSIZE);
+
+    switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents,
+                              *schedule)) {
+    case -1:
+       return(KRB5DES_BAD_KEYPAR);
+    case -2:
+       return(KRB5DES_WEAK_KEY);
+    }
+    return 0;
+}
+
+static krb5_error_code
+validate_and_schedule_iov(const krb5_keyblock *key, const krb5_data *ivec,
+                         const krb5_crypto_iov *data, size_t num_data,
+                         mit_des3_key_schedule *schedule)
+{
+    size_t i, input_length;
+
+    for (i = 0, input_length = 0; i < num_data; i++) {
+       const krb5_crypto_iov *iov = &data[i];
+
+       if (ENCRYPT_IOV(iov))
+           input_length += iov->data.length;
+    }
+
+    if (key->length != 24)
+       return(KRB5_BAD_KEYSIZE);
+    if ((input_length%8) != 0)
+       return(KRB5_BAD_MSIZE);
+    if (ivec && (ivec->length != 8))
+       return(KRB5_BAD_MSIZE);
+
+    switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents,
+                              *schedule)) {
+    case -1:
+       return(KRB5DES_BAD_KEYPAR);
+    case -2:
+       return(KRB5DES_WEAK_KEY);
+    }
+    return 0;
+}
+
+static krb5_error_code
+k5_des3_encrypt(const krb5_keyblock *key, const krb5_data *ivec,
+               const krb5_data *input, krb5_data *output)
+{
+    mit_des3_key_schedule schedule;
+    krb5_error_code err;
+
+    err = validate_and_schedule(key, ivec, input, output, &schedule);
+    if (err)
+       return err;
+
+    /* this has a return value, but the code always returns zero */
+    krb5int_des3_cbc_encrypt((krb5_pointer) input->data,
+                            (krb5_pointer) output->data, input->length,
+                            schedule[0], schedule[1], schedule[2],
+                            ivec?(const unsigned char *) ivec->data:(const unsigned char *)mit_des_zeroblock);
+
+    zap(schedule, sizeof(schedule));
+
+    return(0);
+}
+
+static krb5_error_code
+k5_des3_decrypt(const krb5_keyblock *key, const krb5_data *ivec,
+               const krb5_data *input, krb5_data *output)
+{
+    mit_des3_key_schedule schedule;
+    krb5_error_code err;
+
+    err = validate_and_schedule(key, ivec, input, output, &schedule);
+    if (err)
+       return err;
+
+    /* this has a return value, but the code always returns zero */
+    krb5int_des3_cbc_decrypt((krb5_pointer) input->data,
+                            (krb5_pointer) output->data, input->length,
+                            schedule[0], schedule[1], schedule[2],
+                            ivec?(const unsigned char *) ivec->data:(const unsigned char *)mit_des_zeroblock);
+
+    zap(schedule, sizeof(schedule));
+
+    return(0);
+}
+
+static krb5_error_code
+k5_des3_make_key(const krb5_data *randombits, krb5_keyblock *key)
+{
+    int i;
+
+    if (key->length != 24)
+       return(KRB5_BAD_KEYSIZE);
+    if (randombits->length != 21)
+       return(KRB5_CRYPTO_INTERNAL);
+
+    key->magic = KV5M_KEYBLOCK;
+    key->length = 24;
+
+    /* take the seven bytes, move them around into the top 7 bits of the
+       8 key bytes, then compute the parity bits.  Do this three times. */
+
+    for (i=0; i<3; i++) {
+       memcpy(key->contents+i*8, randombits->data+i*7, 7);
+       key->contents[i*8+7] = (((key->contents[i*8]&1)<<1) |
+                               ((key->contents[i*8+1]&1)<<2) |
+                               ((key->contents[i*8+2]&1)<<3) |
+                               ((key->contents[i*8+3]&1)<<4) |
+                               ((key->contents[i*8+4]&1)<<5) |
+                               ((key->contents[i*8+5]&1)<<6) |
+                               ((key->contents[i*8+6]&1)<<7));
+
+       mit_des_fixup_key_parity(key->contents+i*8);
+    }
+
+    return(0);
+}
+
+static krb5_error_code
+k5_des3_encrypt_iov(const krb5_keyblock *key,
+                   const krb5_data *ivec,
+                   krb5_crypto_iov *data,
+                   size_t num_data)
+{
+    mit_des3_key_schedule schedule;
+    krb5_error_code err;
+
+    err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule);
+    if (err)
+       return err;
+
+    /* this has a return value, but the code always returns zero */
+    krb5int_des3_cbc_encrypt_iov(data, num_data,
+                            schedule[0], schedule[1], schedule[2],
+                            ivec != NULL ? (unsigned char *) ivec->data : NULL);
+
+    zap(schedule, sizeof(schedule));
+
+    return(0);
+}
+
+static krb5_error_code
+k5_des3_decrypt_iov(const krb5_keyblock *key,
+                   const krb5_data *ivec,
+                   krb5_crypto_iov *data,
+                   size_t num_data)
+{
+    mit_des3_key_schedule schedule;
+    krb5_error_code err;
+
+    err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule);
+    if (err)
+       return err;
+
+    /* this has a return value, but the code always returns zero */
+    krb5int_des3_cbc_decrypt_iov(data, num_data,
+                                schedule[0], schedule[1], schedule[2],
+                                ivec != NULL ? (unsigned char *) ivec->data : NULL);
+
+    zap(schedule, sizeof(schedule));
+
+    return(0);
+}
+
+const struct krb5_enc_provider krb5int_enc_des3 = {
+    8,
+    21, 24,
+    k5_des3_encrypt,
+    k5_des3_decrypt,
+    k5_des3_make_key,
+    krb5int_des_init_state,
+    krb5int_default_free_state,
+    k5_des3_encrypt_iov,
+    k5_des3_decrypt_iov
+};
+
diff --git a/src/lib/crypto/krb/enc_provider/enc_provider.h b/src/lib/crypto/krb/enc_provider/enc_provider.h
new file mode 100644 (file)
index 0000000..92022b3
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ * 
+ * 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 FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "k5-int.h"
+
+extern const struct krb5_enc_provider krb5int_enc_des;
+extern const struct krb5_enc_provider krb5int_enc_des3;
+extern const struct krb5_enc_provider krb5int_enc_arcfour;
+extern const struct krb5_enc_provider krb5int_enc_aes128;
+extern const struct krb5_enc_provider krb5int_enc_aes256;
+extern const struct krb5_enc_provider krb5int_enc_aes128_ctr;
+extern const struct krb5_enc_provider krb5int_enc_aes256_ctr;
+
diff --git a/src/lib/crypto/krb/enc_provider/rc4.c b/src/lib/crypto/krb/enc_provider/rc4.c
new file mode 100644 (file)
index 0000000..b950a60
--- /dev/null
@@ -0,0 +1,271 @@
+/* arcfour.c 
+ *
+ * Copyright (c) 2000 by Computer Science Laboratory,
+ *                       Rensselaer Polytechnic Institute
+ *
+ * #include STD_DISCLAIMER
+ */
+
+#include "k5-int.h"
+#include "arcfour-int.h"
+#include "enc_provider.h"
+#include "../aead.h"
+/* gets the next byte from the PRNG */
+#if ((__GNUC__ >= 2) )
+static __inline__ unsigned int k5_arcfour_byte(ArcfourContext *);
+#else
+static unsigned int k5_arcfour_byte(ArcfourContext *);
+#endif /* gcc inlines*/
+
+/* Initializes the context and sets the key. */
+static krb5_error_code k5_arcfour_init(ArcfourContext *ctx, const unsigned char *key, 
+                 unsigned int keylen);
+
+/* Encrypts/decrypts data. */
+static void k5_arcfour_crypt(ArcfourContext *ctx, unsigned char *dest, 
+                    const unsigned char *src, unsigned int len);
+
+/* Interface layer to kerb5 crypto layer */
+static krb5_error_code
+k5_arcfour_docrypt(const krb5_keyblock *, const krb5_data *,
+                  const krb5_data *, krb5_data *);
+
+/* from a random bitstrem, construct a key */
+static krb5_error_code
+k5_arcfour_make_key(const krb5_data *, krb5_keyblock *);
+
+static const unsigned char arcfour_weakkey1[] = {0x00, 0x00, 0xfd};
+static const unsigned char arcfour_weakkey2[] = {0x03, 0xfd, 0xfc};
+static const struct {
+    size_t length;
+    const unsigned char *data;
+} arcfour_weakkeys[] = {
+    { sizeof (arcfour_weakkey1), arcfour_weakkey1},
+    { sizeof (arcfour_weakkey2), arcfour_weakkey2},
+};
+
+static inline unsigned int k5_arcfour_byte(ArcfourContext * ctx)
+{
+  unsigned int x;
+  unsigned int y;
+  unsigned int sx, sy;
+  unsigned char *state;
+
+  state = ctx->state;
+  x = (ctx->x + 1) & 0xff;
+  sx = state[x];
+  y = (sx + ctx->y) & 0xff;
+  sy = state[y];
+  ctx->x = x;
+  ctx->y = y;
+  state[y] = sx;
+  state[x] = sy;
+  return state[(sx + sy) & 0xff];
+}
+
+static void k5_arcfour_crypt(ArcfourContext *ctx, unsigned char *dest, 
+                    const unsigned char *src, unsigned int len)
+{
+  unsigned int i;
+  for (i = 0; i < len; i++)
+    dest[i] = src[i] ^ k5_arcfour_byte(ctx);
+}
+
+
+static krb5_error_code
+k5_arcfour_init(ArcfourContext *ctx, const unsigned char *key, 
+                 unsigned int key_len)
+{
+  unsigned int t, u;
+  unsigned int keyindex;
+  unsigned int stateindex;
+  unsigned char* state;
+  unsigned int counter;
+
+  if (key_len != 16)
+    return KRB5_BAD_MSIZE;     /*this is probably not the correct error code
+                                to return */
+  for (counter=0;
+       counter < sizeof(arcfour_weakkeys)/sizeof(arcfour_weakkeys[0]);
+       counter++)
+      if (!memcmp(key, arcfour_weakkeys[counter].data,
+                 arcfour_weakkeys[counter].length))
+         return KRB5DES_WEAK_KEY; /* most certainly not the correct error */
+
+  state = &ctx->state[0];
+  ctx->x = 0;
+  ctx->y = 0;
+  for (counter = 0; counter < 256; counter++)
+    state[counter] = counter;
+  keyindex = 0;
+  stateindex = 0;
+  for (counter = 0; counter < 256; counter++)
+    {
+      t = state[counter];
+      stateindex = (stateindex + key[keyindex] + t) & 0xff;
+      u = state[stateindex];
+      state[stateindex] = t;
+      state[counter] = u;
+      if (++keyindex >= key_len)
+       keyindex = 0;
+    }
+  return 0;
+}
+
+
+/* The workhorse of the arcfour system, this impliments the cipher */
+static krb5_error_code
+k5_arcfour_docrypt(const krb5_keyblock *key, const krb5_data *state,
+              const krb5_data *input, krb5_data *output)
+{
+  ArcfourContext *arcfour_ctx;
+  ArcFourCipherState *cipher_state;
+  int ret;
+
+  if (key->length != 16)
+    return(KRB5_BAD_KEYSIZE);
+  if (state && (state->length != sizeof (ArcFourCipherState)))
+    return(KRB5_BAD_MSIZE);
+  if (input->length != output->length)
+    return(KRB5_BAD_MSIZE);
+
+  if (state) {
+    cipher_state = (ArcFourCipherState *) state->data;
+    arcfour_ctx=&cipher_state->ctx;
+    if (cipher_state->initialized == 0) {
+      if ((ret=k5_arcfour_init(arcfour_ctx, key->contents, key->length))) {
+       return ret;
+      }
+      cipher_state->initialized = 1;
+    }
+    k5_arcfour_crypt(arcfour_ctx, (unsigned char *) output->data, (const unsigned char *) input->data, input->length);
+  }
+  else {
+    arcfour_ctx=malloc(sizeof (ArcfourContext));
+    if (arcfour_ctx == NULL)
+      return ENOMEM;
+    if ((ret=k5_arcfour_init(arcfour_ctx, key->contents, key->length))) {
+      free(arcfour_ctx);
+      return (ret);
+    }
+    k5_arcfour_crypt(arcfour_ctx, (unsigned char * ) output->data,
+                    (const unsigned char * ) input->data, input->length);
+    memset(arcfour_ctx, 0, sizeof (ArcfourContext));
+    free(arcfour_ctx);
+  }
+  
+  return 0;
+}
+
+/* In-place encryption */
+static krb5_error_code
+k5_arcfour_docrypt_iov(const krb5_keyblock *key,
+                      const krb5_data *state,
+                      krb5_crypto_iov *data,
+                      size_t num_data)
+{
+    ArcfourContext *arcfour_ctx = NULL;
+    ArcFourCipherState *cipher_state = NULL;
+    krb5_error_code ret;
+    size_t i;
+
+    if (key->length != 16)
+       return KRB5_BAD_KEYSIZE;
+    if (state != NULL && (state->length != sizeof(ArcFourCipherState)))
+       return KRB5_BAD_MSIZE;
+
+    if (state != NULL) {
+       cipher_state = (ArcFourCipherState *)state->data;
+       arcfour_ctx = &cipher_state->ctx;
+       if (cipher_state->initialized == 0) {
+           ret = k5_arcfour_init(arcfour_ctx, key->contents, key->length);
+           if (ret != 0)
+               return ret;
+
+           cipher_state->initialized = 1;
+       }
+    } else {
+       arcfour_ctx = (ArcfourContext *)malloc(sizeof(ArcfourContext));
+       if (arcfour_ctx == NULL)
+           return ENOMEM;
+
+       ret = k5_arcfour_init(arcfour_ctx, key->contents, key->length);
+       if (ret != 0) {
+           free(arcfour_ctx);
+           return ret;
+       }
+    }
+
+    for (i = 0; i < num_data; i++) {
+       krb5_crypto_iov *iov = &data[i];
+
+       if (ENCRYPT_IOV(iov))
+           k5_arcfour_crypt(arcfour_ctx, (unsigned char *)iov->data.data,
+                            (const unsigned char *)iov->data.data, iov->data.length);
+    }
+
+    if (state == NULL) {
+       memset(arcfour_ctx, 0, sizeof(ArcfourContext));
+       free(arcfour_ctx);
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+k5_arcfour_make_key(const krb5_data *randombits, krb5_keyblock *key)
+{
+    if (key->length != 16)
+       return(KRB5_BAD_KEYSIZE);
+    if (randombits->length != 16)
+       return(KRB5_CRYPTO_INTERNAL);
+
+    key->magic = KV5M_KEYBLOCK;
+    key->length = 16;
+
+    memcpy(key->contents, randombits->data, randombits->length);
+
+    return(0);
+}
+
+static krb5_error_code
+k5_arcfour_init_state (const krb5_keyblock *key,
+                      krb5_keyusage keyusage, krb5_data *new_state)
+{
+  /* Note that we can't actually set up the state here  because the key
+   * will change  between now and when encrypt is called
+   * because  it is data dependent.  Yeah, this has strange
+   * properties. --SDH
+   */
+  new_state->length = sizeof (ArcFourCipherState);
+  new_state->data = malloc (new_state->length);
+  if (new_state->data) {
+    memset (new_state->data, 0 , new_state->length);
+    /* That will set initialized to zero*/
+  }else {
+    return (ENOMEM);
+  }
+  return 0;
+}
+
+/* Since the arcfour cipher is identical going forwards and backwards, 
+   we just call "docrypt" directly
+*/
+const struct krb5_enc_provider krb5int_enc_arcfour = {
+    /* This seems to work... although I am not sure what the
+       implications are in other places in the kerberos library */
+    1,
+    /* Keysize is arbitrary in arcfour, but the constraints of the
+       system, and to attempt to work with the MSFT system forces us
+       to 16byte/128bit.  Since there is no parity in the key, the
+       byte and length are the same.  */
+    16, 16,
+    k5_arcfour_docrypt,
+    k5_arcfour_docrypt,
+    k5_arcfour_make_key,
+    k5_arcfour_init_state, /*xxx not implemented yet*/
+    krb5int_default_free_state,
+    k5_arcfour_docrypt_iov,
+    k5_arcfour_docrypt_iov
+};
+
index 40f5ab80930cda6b5af06b9214eedb9dab780204..ce115639b153de3725cac452c538866945ad8d7e 100644 (file)
@@ -254,6 +254,37 @@ OM_uint32 KRB5_CALLCONV gss_release_iov_buffer
     gss_iov_buffer_desc *, /* iov */
     int);              /* iov_count */
 
+
+/*
+ * Protocol transition
+ */
+OM_uint32 KRB5_CALLCONV
+gss_acquire_cred_impersonate_name(
+    OM_uint32 *,           /* minor_status */
+    const gss_cred_id_t,    /* impersonator_cred_handle */
+    const gss_name_t,      /* desired_name */
+    OM_uint32,             /* time_req */
+    const gss_OID_set,     /* desired_mechs */
+    gss_cred_usage_t,      /* cred_usage */
+    gss_cred_id_t *,       /* output_cred_handle */
+    gss_OID_set *,         /* actual_mechs */
+    OM_uint32 *);          /* time_rec */
+
+OM_uint32 KRB5_CALLCONV
+gss_add_cred_impersonate_name(
+    OM_uint32 *,           /* minor_status */
+    gss_cred_id_t,         /* input_cred_handle */
+    const gss_cred_id_t,    /* impersonator_cred_handle */
+    const gss_name_t,      /* desired_name */
+    const gss_OID,         /* desired_mech */
+    gss_cred_usage_t,      /* cred_usage */
+    OM_uint32,             /* initiator_time_req */
+    OM_uint32,             /* acceptor_time_req */
+    gss_cred_id_t *,       /* output_cred_handle */
+    gss_OID_set *,         /* actual_mechs */
+    OM_uint32 *,           /* initiator_time_rec */
+    OM_uint32 *);          /* acceptor_time_rec */
+
 #ifdef __cplusplus
 }
 #endif
index 2ee9e1d9c5b655c7f6285d2cb4d75c7378467e15..645b91b11279911cf3c072c25ab57a427b85c8f9 100644 (file)
@@ -73,6 +73,7 @@ SRCS = \
        $(srcdir)/rel_cred.c \
        $(srcdir)/rel_oid.c \
        $(srcdir)/rel_name.c \
+       $(srcdir)/s4u_gss_glue.c \
        $(srcdir)/seal.c \
        $(srcdir)/set_allowable_enctypes.c \
        $(srcdir)/ser_sctx.c \
@@ -123,6 +124,7 @@ OBJS = \
        $(OUTPRE)rel_cred.$(OBJEXT) \
        $(OUTPRE)rel_oid.$(OBJEXT) \
        $(OUTPRE)rel_name.$(OBJEXT) \
+       $(OUTPRE)s4u_gss_glue.$(OBJEXT) \
        $(OUTPRE)seal.$(OBJEXT) \
        $(OUTPRE)set_allowable_enctypes.$(OBJEXT) \
        $(OUTPRE)ser_sctx.$(OBJEXT) \
@@ -176,6 +178,7 @@ STLIBOBJS = \
        rel_cred.o \
        rel_oid.o \
        rel_name.o \
+       s4u_gss_glue.o \
        seal.o \
        set_allowable_enctypes.o \
        ser_sctx.o \
index dd17c044b66946637355329ac24855a8dd5ccec1..d340db7e72647144c092f16ebe77b6cbdc9942ce 100644 (file)
 
 #ifndef LEAN_CLIENT
 
+static OM_uint32
+create_constrained_deleg_creds(OM_uint32 *minor_status,
+                               krb5_gss_cred_id_t verifier_cred_handle,
+                               krb5_ticket *ticket,
+                               krb5_gss_cred_id_t *out_cred,
+                               krb5_context context)
+{
+    OM_uint32 major_status;
+    krb5_creds krb_creds;
+    krb5_data *data;
+    krb5_error_code code;
+
+    assert(out_cred != NULL);
+    assert(verifier_cred_handle->usage == GSS_C_BOTH);
+
+    memset(&krb_creds, 0, sizeof(krb_creds));
+    krb_creds.client = ticket->enc_part2->client;
+    krb_creds.server = ticket->server;
+    krb_creds.keyblock = *(ticket->enc_part2->session);
+    krb_creds.ticket_flags = ticket->enc_part2->flags;
+    krb_creds.times = ticket->enc_part2->times;
+    krb_creds.magic = KV5M_CREDS;
+    krb_creds.authdata = NULL;
+
+    code = encode_krb5_ticket(ticket, &data);
+    if (code) {
+        *minor_status = code;
+        return GSS_S_FAILURE;
+    }
+
+    krb_creds.ticket = *data;
+
+    major_status = kg_compose_deleg_cred(minor_status,
+                                         verifier_cred_handle,
+                                         &krb_creds,
+                                         GSS_C_INDEFINITE,
+                                         GSS_C_NO_OID_SET,
+                                         out_cred,
+                                         NULL,
+                                         NULL,
+                                         context);
+
+    krb5_free_data(context, data);
+
+    return major_status;
+}
+
 /* Decode, decrypt and store the forwarded creds in the local ccache. */
 static krb5_error_code
 rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
@@ -866,6 +913,23 @@ kg_accept_krb5(minor_status, context_handle,
     ctx->krb_times = ticket->enc_part2->times; /* struct copy */
     ctx->krb_flags = ticket->enc_part2->flags;
 
+    if (delegated_cred_handle != NULL &&
+        deleg_cred == NULL && /* no unconstrained delegation */
+        cred->usage == GSS_C_BOTH &&
+        (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
+        /*
+         * Now, we always fabricate a delegated credentials handle
+         * containing the service ticket to ourselves, which can be
+         * used for S4U2Proxy.
+         */
+        major_status = create_constrained_deleg_creds(minor_status, cred,
+                                                      ticket, &deleg_cred,
+                                                      context);
+        if (GSS_ERROR(major_status))
+            goto fail;
+        ctx->gss_flags |= GSS_C_DELEG_FLAG;
+    }
+
     krb5_free_ticket(context, ticket); /* Done with ticket */
 
     {
@@ -1055,8 +1119,8 @@ kg_accept_krb5(minor_status, context_handle,
     if (src_name)
         *src_name = (gss_name_t) name;
 
-    if (delegated_cred_handle && deleg_cred) {
-        if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
+    if (delegated_cred_handle) {
+       if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
             major_status = GSS_S_FAILURE;
             code = G_VALIDATE_FAILED;
             goto fail;
index 48471b4f4c00b9b4f05f0cd90f863a81f407cbe8..4427ed763bf47266c66ddc338a07f91ca726ffaf 100644 (file)
@@ -532,8 +532,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
 
     cred->usage = cred_usage;
     cred->princ = NULL;
-    cred->prerfc_mech = req_old;
-    cred->rfc_mech = req_new;
+    cred->prerfc_mech = (req_old != 0);
+    cred->rfc_mech = (req_new != 0);
 
 #ifndef LEAN_CLIENT
     cred->keytab = NULL;
@@ -759,3 +759,4 @@ gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
    *minor_status = 0;
    return GSS_S_COMPLETE;
 }
+
index a1073f3447305564967a3e9bce7b50d1616bade2..48da8780717287640c63545f4c493cd48da27bc3 100644 (file)
@@ -162,8 +162,9 @@ typedef struct _krb5_gss_cred_id_rec {
     /* name/type of credential */
     gss_cred_usage_t usage;
     krb5_principal princ;        /* this is not interned as a gss_name_t */
-    int prerfc_mech;
-    int rfc_mech;
+    unsigned int prerfc_mech : 1;
+    unsigned int rfc_mech : 1;
+    unsigned int proxy_cred : 1;
 
     /* keytab (accept) data */
     krb5_keytab keytab;
@@ -466,6 +467,29 @@ krb5_boolean kg_integ_only_iov(gss_iov_buffer_desc *iov, int iov_count);
 
 krb5_error_code kg_allocate_iov(gss_iov_buffer_t iov, size_t size);
 
+krb5_error_code
+krb5_to_gss_cred(krb5_context context,
+                 krb5_creds *creds,
+                 krb5_gss_cred_id_t *out_cred);
+
+OM_uint32
+kg_new_connection(
+    OM_uint32 *minor_status,
+    krb5_gss_cred_id_t cred,
+    gss_ctx_id_t *context_handle,
+    gss_name_t target_name,
+    gss_OID mech_type,
+    OM_uint32 req_flags,
+    OM_uint32 time_req,
+    gss_channel_bindings_t input_chan_bindings,
+    gss_buffer_t input_token,
+    gss_OID *actual_mech_type,
+    gss_buffer_t output_token,
+    OM_uint32 *ret_flags,
+    OM_uint32 *time_rec,
+    krb5_context context,
+    int default_mech);
+
 /** declarations of internal name mechanism functions **/
 
 OM_uint32 krb5_gss_acquire_cred
@@ -766,6 +790,17 @@ OM_uint32 krb5_gss_validate_cred
  gss_cred_id_t               /* cred */
 );
 
+OM_uint32 krb5_gss_acquire_cred_impersonate_name(
+    OM_uint32 *,            /* minor_status */
+    const gss_cred_id_t,    /* impersonator_cred_handle */
+    const gss_name_t,       /* desired_name */
+    OM_uint32,              /* time_req */
+    const gss_OID_set,      /* desired_mechs */
+    gss_cred_usage_t,       /* cred_usage */
+    gss_cred_id_t *,        /* output_cred_handle */
+    gss_OID_set *,          /* actual_mechs */
+    OM_uint32 *);           /* time_rec */
+
 OM_uint32
 krb5_gss_validate_cred_1(OM_uint32 * /* minor_status */,
                          gss_cred_id_t /* cred_handle */,
@@ -790,6 +825,19 @@ OM_uint32 gss_krb5int_unseal_token_v3(krb5_context *contextptr,
 
 int gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc);
 
+/* s4u_gss_glue.c */
+OM_uint32
+kg_compose_deleg_cred(OM_uint32 *minor_status,
+                      krb5_gss_cred_id_t impersonator_cred,
+                      krb5_creds *subject_creds,
+                      OM_uint32 time_req,
+                      const gss_OID_set desired_mechs,
+                      krb5_gss_cred_id_t *output_cred,
+                      gss_OID_set *actual_mechs,
+                      OM_uint32 *time_rec,
+                      krb5_context context);
+
+
 /*
  * These take unglued krb5-mech-specific contexts.
  */
index a20e59dfb4c4f7f7441e79b3052fcc8d012ea22c..519abb860e01363fd540796838bd4f6b7bac77d4 100644 (file)
@@ -140,6 +140,7 @@ const gss_OID_desc krb5_gss_oid_array[] = {
 
     /* gss_nt_krb5_principal.  Object identifier for a krb5_principal. Do not use. */
     {10, "\052\206\110\206\367\022\001\002\002\002"},
+
     { 0, 0 }
 };
 
@@ -447,13 +448,11 @@ krb5_gss_inquire_cred_by_oid(OM_uint32 *minor_status,
 /*
  * gss_set_sec_context_option() methods
  */
-#if 0
 static struct {
     gss_OID_desc oid;
     OM_uint32 (*func)(OM_uint32 *, gss_ctx_id_t *, const gss_OID, const gss_buffer_t);
 } krb5_gss_set_sec_context_option_ops[] = {
 };
-#endif
 
 static OM_uint32
 krb5_gss_set_sec_context_option (OM_uint32 *minor_status,
@@ -481,12 +480,8 @@ krb5_gss_set_sec_context_option (OM_uint32 *minor_status,
             return GSS_S_NO_CONTEXT;
 
         ctx = (krb5_gss_ctx_id_rec *) context_handle;
-
-        if (!ctx->established)
-            return GSS_S_NO_CONTEXT;
     }
 
-#if 0
     for (i = 0; i < sizeof(krb5_gss_set_sec_context_option_ops)/
                     sizeof(krb5_gss_set_sec_context_option_ops[0]); i++) {
         if (g_OID_prefix_equal(desired_object, &krb5_gss_set_sec_context_option_ops[i].oid)) {
@@ -496,7 +491,6 @@ krb5_gss_set_sec_context_option (OM_uint32 *minor_status,
                                                                   value);
         }
     }
-#endif
 
     *minor_status = EINVAL;
 
@@ -521,7 +515,7 @@ static struct {
     {
         {GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, GSS_KRB5_SET_CRED_RCACHE_OID},
         gss_krb5int_set_cred_rcache
-    }
+    },
 };
 
 static OM_uint32
@@ -587,7 +581,7 @@ static struct {
     {
         {GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH, GSS_KRB5_USE_KDC_CONTEXT_OID},
         krb5int_gss_use_kdc_context
-    }
+    },
 };
 
 static OM_uint32
@@ -683,6 +677,8 @@ static struct gss_config krb5_mechanism = {
     krb5_gss_unwrap_iov,
     krb5_gss_wrap_iov_length,
     NULL,               /* complete_auth_token */
+    krb5_gss_acquire_cred_impersonate_name,
+    NULL,               /* krb5_gss_add_cred_impersonate_name */
 };
 
 
index 440d36222b184cbb567b2f4ba47172f0467e5def..6879c766faa000e13ea4739eff7ae3531ec014f5 100644 (file)
@@ -56,8 +56,7 @@ krb5_gss_import_name(minor_status, input_name_buffer,
     krb5_context context;
     krb5_principal princ;
     krb5_error_code code;
-    unsigned char *cp, *end;
-    char *stringrep, *tmp, *tmp2;
+    char *stringrep, *tmp, *tmp2, *cp;
     OM_uint32    length;
 #ifndef NO_PASSWORD
     struct passwd *pw;
@@ -156,12 +155,7 @@ krb5_gss_import_name(minor_status, input_name_buffer,
             goto do_getpwuid;
 #endif
         } else if (g_OID_equal(input_name_type, gss_nt_exported_name)) {
-#define BOUNDS_CHECK(cp, end, n) do { if ((end) - (cp) < (n)) \
-                    goto fail_name; } while (0)
-            cp = (unsigned char *)tmp;
-            end = cp + input_name_buffer->length;
-
-            BOUNDS_CHECK(cp, end, 4);
+            cp = tmp;
             if (*cp++ != 0x04)
                 goto fail_name;
             if (*cp++ != 0x01)
@@ -169,28 +163,20 @@ krb5_gss_import_name(minor_status, input_name_buffer,
             if (*cp++ != 0x00)
                 goto fail_name;
             length = *cp++;
-            if (length != (ssize_t)gss_mech_krb5->length+2)
+            if (length != gss_mech_krb5->length+2)
                 goto fail_name;
-
-            BOUNDS_CHECK(cp, end, 2);
             if (*cp++ != 0x06)
                 goto fail_name;
             length = *cp++;
             if (length != gss_mech_krb5->length)
                 goto fail_name;
-
-            BOUNDS_CHECK(cp, end, length);
             if (memcmp(cp, gss_mech_krb5->elements, length) != 0)
                 goto fail_name;
             cp += length;
-
-            BOUNDS_CHECK(cp, end, 4);
             length = *cp++;
             length = (length << 8) | *cp++;
             length = (length << 8) | *cp++;
             length = (length << 8) | *cp++;
-
-            BOUNDS_CHECK(cp, end, length);
             tmp2 = malloc(length+1);
             if (tmp2 == NULL) {
                 xfree(tmp);
@@ -198,7 +184,7 @@ krb5_gss_import_name(minor_status, input_name_buffer,
                 krb5_free_context(context);
                 return GSS_S_FAILURE;
             }
-            strncpy(tmp2, (char *)cp, length);
+            strncpy(tmp2, cp, length);
             tmp2[length] = 0;
 
             stringrep = tmp2;
index 5559fadbc739d9aa299016d966393ab042114123..0bb4fde02843b122d1e6846aa9da8c02e50e29b2 100644 (file)
@@ -128,25 +128,69 @@ static krb5_error_code get_credentials(context, cred, server, now,
     krb5_creds **out_creds;
 {
     krb5_error_code     code;
-    krb5_creds          in_creds;
+    krb5_creds          in_creds, evidence_creds;
+    krb5_flags          flags = 0;
+    krb5_principal      cc_princ = NULL;
 
     k5_mutex_assert_locked(&cred->lock);
     memset(&in_creds, 0, sizeof(krb5_creds));
+    memset(&evidence_creds, 0, sizeof(krb5_creds));
     in_creds.client = in_creds.server = NULL;
 
-    if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
+    if ((code = krb5_cc_get_principal(context, cred->ccache, &cc_princ)))
         goto cleanup;
-    if ((code = krb5_copy_principal(context, server, &in_creds.server)))
-        goto cleanup;
-    in_creds.times.endtime = endtime;
 
-    in_creds.keyblock.enctype = 0;
+    /*
+     * Do constrained delegation if we have proxy credentials and
+     * we're not trying to get a ticket to ourselves (in which case
+     * we can just use the S4U2Self or evidence ticket directly).
+     */
+    if (cred->proxy_cred &&
+        !krb5_principal_compare(context, cc_princ, server)) {
+        krb5_creds mcreds;
+
+        flags |= KRB5_GC_CANONICALIZE |
+                 KRB5_GC_NO_STORE |
+                 KRB5_GC_CONSTRAINED_DELEGATION;
+
+        memset(&mcreds, 0, sizeof(mcreds));
 
-    code = krb5_get_credentials(context, 0, cred->ccache,
+        mcreds.magic = KV5M_CREDS;
+        mcreds.times.endtime = cred->tgt_expire;
+        mcreds.server = cc_princ;
+        mcreds.client = cred->princ;
+
+        code = krb5_cc_retrieve_cred(context, cred->ccache,
+                                     KRB5_TC_MATCH_TIMES, &mcreds,
+                                     &evidence_creds);
+        if (code)
+            goto cleanup;
+
+        assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE);
+
+        in_creds.client = cc_princ;
+        in_creds.second_ticket = evidence_creds.ticket;
+    } else {
+        in_creds.client = cred->princ;
+    }
+
+    in_creds.server = server;
+    in_creds.times.endtime = endtime;
+
+    code = krb5_get_credentials(context, flags, cred->ccache,
                                 &in_creds, out_creds);
     if (code)
         goto cleanup;
 
+    if (flags & KRB5_GC_CONSTRAINED_DELEGATION) {
+        if (!krb5_principal_compare(context, cred->princ,
+                                    (*out_creds)->client)) {
+            /* server did not support constrained delegation */
+            code = KRB5_KDCREP_MODIFIED;
+            goto cleanup;
+        }
+    }
+
     /*
      * Enforce a stricter limit (without timeskew forgiveness at the
      * boundaries) because accept_sec_context code is also similarly
@@ -159,10 +203,10 @@ static krb5_error_code get_credentials(context, cred, server, now,
     }
 
 cleanup:
-    if (in_creds.client)
-        krb5_free_principal(context, in_creds.client);
-    if (in_creds.server)
-        krb5_free_principal(context, in_creds.server);
+    if (cc_princ)
+        krb5_free_principal(context, cc_princ);
+    krb5_free_cred_contents(context, &evidence_creds);
+
     return code;
 }
 struct gss_checksum_data {
@@ -390,8 +434,8 @@ cleanup:
  *
  * Do the grunt work of setting up a new context.
  */
-static OM_uint32
-new_connection(
+OM_uint32
+kg_new_connection(
     OM_uint32 *minor_status,
     krb5_gss_cred_id_t cred,
     gss_ctx_id_t *context_handle,
@@ -931,12 +975,12 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
 
     /*SUPPRESS 29*/
     if (*context_handle == GSS_C_NO_CONTEXT) {
-        major_status = new_connection(minor_status, cred, context_handle,
-                                      target_name, mech_type, req_flags,
-                                      time_req, input_chan_bindings,
-                                      input_token, actual_mech_type,
-                                      output_token, ret_flags, time_rec,
-                                      context, default_mech);
+        major_status = kg_new_connection(minor_status, cred, context_handle,
+                                        target_name, mech_type, req_flags,
+                                        time_req, input_chan_bindings,
+                                        input_token, actual_mech_type,
+                                        output_token, ret_flags, time_rec,
+                                        context, default_mech);
         k5_mutex_unlock(&cred->lock);
         if (*context_handle == GSS_C_NO_CONTEXT) {
             save_error_info (*minor_status, context);
index f9bf03016f5d4643652e586ac8de55a6f6b1d46b..0345501221fe6aa18cc13cc6f3f836beccd15649 100644 (file)
@@ -416,3 +416,4 @@ gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
 
     return GSS_S_COMPLETE;
 }
+
diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c
new file mode 100644 (file)
index 0000000..8e2d690
--- /dev/null
@@ -0,0 +1,346 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009  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.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ *
+ */
+#include "k5-int.h"
+#include "gssapiP_krb5.h"
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <assert.h>
+
+static OM_uint32
+kg_set_desired_mechs(OM_uint32 *minor_status,
+                     const gss_OID_set desired_mechs,
+                     krb5_gss_cred_id_t cred)
+{
+    unsigned int i;
+
+    if (desired_mechs == GSS_C_NULL_OID_SET) {
+        cred->prerfc_mech = 1;
+        cred->rfc_mech = 1;
+    } else {
+        cred->prerfc_mech = 0;
+        cred->rfc_mech = 0;
+
+        for (i = 0; i < desired_mechs->count; i++) {
+            if (g_OID_equal(gss_mech_krb5_old, &desired_mechs->elements[i]))
+                cred->prerfc_mech = 1;
+            else if (g_OID_equal(gss_mech_krb5, &desired_mechs->elements[i]))
+                cred->rfc_mech = 1;
+        }
+
+        if (!cred->prerfc_mech && !cred->rfc_mech) {
+            *minor_status = 0;
+            return GSS_S_BAD_MECH;
+        }
+    }
+
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+kg_return_mechs(OM_uint32 *minor_status,
+                krb5_gss_cred_id_t cred,
+                gss_OID_set *actual_mechs)
+{
+    OM_uint32 major_status, minor;
+    gss_OID_set mechs;
+
+    if (actual_mechs == NULL)
+        return GSS_S_COMPLETE;
+
+    major_status = generic_gss_create_empty_oid_set(minor_status, &mechs);
+    if (GSS_ERROR(major_status))
+        return major_status;
+
+    if (cred->prerfc_mech) {
+        major_status = generic_gss_add_oid_set_member(minor_status,
+                                                      gss_mech_krb5_old,
+                                                      &mechs);
+        if (GSS_ERROR(major_status)) {
+            generic_gss_release_oid_set(&minor, &mechs);
+            return major_status;
+        }
+    }
+    if (cred->rfc_mech) {
+        major_status = generic_gss_add_oid_set_member(minor_status,
+                                                      gss_mech_krb5,
+                                                      &mechs);
+        if (GSS_ERROR(major_status)) {
+            generic_gss_release_oid_set(&minor, &mechs);
+            return major_status;
+        }
+    }
+
+    *actual_mechs = mechs;
+
+    return GSS_S_COMPLETE;
+}
+
+static int
+kg_is_initiator_cred(krb5_gss_cred_id_t cred)
+{
+    return (cred->usage == GSS_C_INITIATE || cred->usage == GSS_C_BOTH) &&
+           (cred->ccache != NULL);
+}
+
+static OM_uint32
+kg_impersonate_name(OM_uint32 *minor_status,
+                    const krb5_gss_cred_id_t impersonator_cred,
+                    const krb5_principal user,
+                    OM_uint32 time_req,
+                    const gss_OID_set desired_mechs,
+                    krb5_gss_cred_id_t *output_cred,
+                    gss_OID_set *actual_mechs,
+                    OM_uint32 *time_rec,
+                    krb5_context context)
+{
+    OM_uint32 major_status;
+    krb5_error_code code;
+    krb5_creds in_creds, *out_creds = NULL;
+
+    memset(&in_creds, 0, sizeof(in_creds));
+    memset(&out_creds, 0, sizeof(out_creds));
+
+    in_creds.client = user;
+    in_creds.server = impersonator_cred->princ;
+
+    if (impersonator_cred->req_enctypes != NULL)
+        in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0];
+
+    code = krb5_get_credentials_for_user(context,
+                                         KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE,
+                                         impersonator_cred->ccache,
+                                         &in_creds,
+                                         NULL, &out_creds);
+    if (code != 0) {
+        *minor_status = code;
+        return GSS_S_FAILURE;
+    }
+
+    major_status = kg_compose_deleg_cred(minor_status,
+                                         impersonator_cred,
+                                         out_creds,
+                                         time_req,
+                                         desired_mechs,
+                                         output_cred,
+                                         actual_mechs,
+                                         time_rec,
+                                         context);
+
+    krb5_free_creds(context, out_creds);
+
+    return major_status;
+}
+
+OM_uint32
+krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
+                                       const gss_cred_id_t impersonator_cred_handle,
+                                       const gss_name_t desired_name,
+                                       OM_uint32 time_req,
+                                       const gss_OID_set desired_mechs,
+                                       gss_cred_usage_t cred_usage,
+                                       gss_cred_id_t *output_cred_handle,
+                                       gss_OID_set *actual_mechs,
+                                       OM_uint32 *time_rec)
+{
+    OM_uint32 major_status;
+    krb5_error_code code;
+    krb5_gss_cred_id_t cred;
+    krb5_context context;
+
+    if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL)
+        return GSS_S_CALL_INACCESSIBLE_READ;
+
+    if (desired_name == GSS_C_NO_NAME)
+        return GSS_S_CALL_INACCESSIBLE_READ;
+
+    if (output_cred_handle == NULL)
+        return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+    if (cred_usage != GSS_C_INITIATE) {
+        *minor_status = (OM_uint32)G_BAD_USAGE;
+        return GSS_S_FAILURE;
+    }
+
+    *output_cred_handle = GSS_C_NO_CREDENTIAL;
+    if (actual_mechs != NULL)
+        *actual_mechs = GSS_C_NO_OID_SET;
+    if (time_rec != NULL)
+        *time_rec = 0;
+
+    code = krb5_gss_init_context(&context);
+    if (code != 0) {
+        *minor_status = code;
+        return GSS_S_FAILURE;
+    }
+
+    major_status = krb5_gss_validate_cred_1(minor_status,
+                                            impersonator_cred_handle,
+                                            context);
+    if (GSS_ERROR(major_status)) {
+        krb5_free_context(context);
+        return major_status;
+    }
+
+    major_status = kg_impersonate_name(minor_status,
+                                       (krb5_gss_cred_id_t)impersonator_cred_handle,
+                                       (krb5_principal)desired_name,
+                                       time_req,
+                                       desired_mechs,
+                                       &cred,
+                                       actual_mechs,
+                                       time_rec,
+                                       context);
+
+    *output_cred_handle = (gss_cred_id_t)cred;
+
+    k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock);
+    krb5_free_context(context);
+
+    return major_status;
+
+}
+
+OM_uint32
+kg_compose_deleg_cred(OM_uint32 *minor_status,
+                      krb5_gss_cred_id_t impersonator_cred,
+                      krb5_creds *subject_creds,
+                      OM_uint32 time_req,
+                      const gss_OID_set desired_mechs,
+                      krb5_gss_cred_id_t *output_cred,
+                      gss_OID_set *actual_mechs,
+                      OM_uint32 *time_rec,
+                      krb5_context context)
+{
+    OM_uint32 major_status;
+    krb5_error_code code;
+    krb5_gss_cred_id_t cred = NULL;
+
+    k5_mutex_assert_locked(&impersonator_cred->lock);
+
+    if (!kg_is_initiator_cred(impersonator_cred) ||
+        impersonator_cred->princ == NULL ||
+        impersonator_cred->proxy_cred) {
+        code = G_BAD_USAGE;
+        goto cleanup;
+    }
+
+    assert(subject_creds != NULL);
+    assert(subject_creds->client != NULL);
+
+    cred = xmalloc(sizeof(*cred));
+    if (cred == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+    memset(cred, 0, sizeof(*cred));
+
+    code = k5_mutex_init(&cred->lock);
+    if (code != 0)
+        goto cleanup;
+
+    /*
+     * Only return a "proxy" credential for use with constrained
+     * delegation if the subject credentials are forwardable.
+     * Submitting non-forwardable credentials to the KDC for use
+     * with constrained delegation will only return an error.
+     */
+    cred->usage = GSS_C_INITIATE;
+    cred->proxy_cred = !!(subject_creds->ticket_flags & TKT_FLG_FORWARDABLE);
+
+    major_status = kg_set_desired_mechs(minor_status, desired_mechs, cred);
+    if (GSS_ERROR(major_status))
+        goto cleanup;
+
+    cred->tgt_expire = impersonator_cred->tgt_expire;
+
+    code = krb5_copy_principal(context, subject_creds->client, &cred->princ);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_cc_initialize(context, cred->ccache,
+                              cred->proxy_cred ? impersonator_cred->princ :
+                                    (krb5_principal)subject_creds->client);
+    if (code != 0)
+        goto cleanup;
+
+    if (cred->proxy_cred) {
+        /* Impersonator's TGT will be necessary for S4U2Proxy */
+        code = krb5_cc_copy_creds(context, impersonator_cred->ccache,
+                                  cred->ccache);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    code = krb5_cc_store_cred(context, cred->ccache, subject_creds);
+    if (code != 0)
+        goto cleanup;
+
+    if (time_rec != NULL) {
+        krb5_timestamp now;
+
+        code = krb5_timeofday(context, &now);
+        if (code != 0)
+            goto cleanup;
+
+        *time_rec = cred->tgt_expire - now;
+    }
+
+    major_status = kg_return_mechs(minor_status, cred, actual_mechs);
+    if (GSS_ERROR(major_status))
+        goto cleanup;
+
+    if (!kg_save_cred_id((gss_cred_id_t)cred)) {
+        code = G_VALIDATE_FAILED;
+        goto cleanup;
+    }
+
+    major_status = GSS_S_COMPLETE;
+    *minor_status = 0;
+    *output_cred = cred;
+
+cleanup:
+    if (code != 0) {
+        *minor_status = code;
+        major_status = GSS_S_FAILURE;
+    }
+
+    if (GSS_ERROR(major_status) && cred != NULL) {
+        k5_mutex_destroy(&cred->lock);
+        if (cred->ccache != NULL)
+            krb5_cc_destroy(context, cred->ccache);
+        if (cred->princ != NULL)
+            krb5_free_principal(context, cred->princ);
+        xfree(cred);
+    }
+
+    return major_status;
+}
+
index dd82d5341c50821061b9c8a2e7fc15758f2bdf8e..43b1f695d3b738380662583d548cd44c15176c4b 100644 (file)
@@ -58,7 +58,8 @@ krb5_gss_validate_cred_1(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
             *minor_status = code;
             return(GSS_S_DEFECTIVE_CREDENTIAL);
         }
-        if (!krb5_principal_compare(context, princ, cred->princ)) {
+        if (!cred->proxy_cred &&
+            !krb5_principal_compare(context, princ, cred->princ)) {
             k5_mutex_unlock(&cred->lock);
             *minor_status = KG_CCACHE_NOMATCH;
             return(GSS_S_DEFECTIVE_CREDENTIAL);
index 69f390e4514a944628eefd76a48f49c6a921d50d..d641fc65b874dc15c4d260f9b56358401c85be91 100644 (file)
@@ -9,8 +9,10 @@ GSS_C_NT_USER_NAME
 GSS_KRB5_NT_PRINCIPAL_NAME
 gss_accept_sec_context
 gss_acquire_cred
+gss_acquire_cred_impersonate_name
 gss_add_buffer_set_member
 gss_add_cred
+gss_add_cred_impersonate_name
 gss_add_oid_set_member
 gss_canonicalize_name
 gss_compare_name
index 927b3755ca96b68bf19df2ab0cb8b68cbbfa105a..18e89f19dc49e25f98196a9fa3f9cf8a67236d87 100644 (file)
@@ -14,6 +14,7 @@ DEFS=-D_GSS_STATIC_LINK=1
 SRCS = \
        $(srcdir)/g_accept_sec_context.c \
        $(srcdir)/g_acquire_cred.c \
+       $(srcdir)/g_acquire_cred_imp_name.c \
        $(srcdir)/g_buffer_set.c \
        $(srcdir)/g_canon_name.c \
        $(srcdir)/g_compare_name.c \
@@ -58,6 +59,7 @@ SRCS = \
 OBJS = \
        $(OUTPRE)g_accept_sec_context.$(OBJEXT) \
        $(OUTPRE)g_acquire_cred.$(OBJEXT) \
+       $(OUTPRE)g_acquire_cred_imp_name.$(OBJEXT) \
        $(OUTPRE)g_buffer_set.$(OBJEXT) \
        $(OUTPRE)g_canon_name.$(OBJEXT) \
        $(OUTPRE)g_compare_name.$(OBJEXT) \
@@ -102,6 +104,7 @@ OBJS = \
 STLIBOBJS = \
        g_accept_sec_context.o \
        g_acquire_cred.o \
+       g_acquire_cred_imp_name.o \
        g_buffer_set.o \
        g_canon_name.o \
        g_compare_name.o \
index fa703d34d2b2d200327e2ccb41895ffcb9618361..dc439159351ae9532b8716cec5950303be09f9a0 100644 (file)
@@ -121,6 +121,7 @@ gss_cred_id_t *             d_cred;
     gss_name_t         tmp_src_name = GSS_C_NO_NAME;
     gss_OID_desc       token_mech_type_desc;
     gss_OID            token_mech_type = &token_mech_type_desc;
+    gss_OID            actual_mech = GSS_C_NO_OID;
     gss_mechanism      mech;
 
     status = val_acc_sec_ctx_args(minor_status,
@@ -198,8 +199,8 @@ gss_cred_id_t *             d_cred;
                                                  input_cred_handle,
                                                  input_token_buffer,
                                                  input_chan_bindings,
-                                                 &internal_name,
-                                                 mech_type,
+                                                 src_name ? &internal_name : NULL,
+                                                 &actual_mech,
                                                  output_token,
                                                  &temp_ret_flags,
                                                  time_rec,
@@ -222,110 +223,120 @@ gss_cred_id_t *         d_cred;
             * then call gss_import_name() to create
             * the union name struct cast to src_name
             */
-           if (internal_name != NULL) {
-               temp_status = gssint_convert_name_to_union_name(
-                      &temp_minor_status, mech,
-                      internal_name, &tmp_src_name);
-               if (temp_status != GSS_S_COMPLETE) {
-                   *minor_status = temp_minor_status;
-                   map_error(minor_status, mech);
-                   if (output_token->length)
-                       (void) gss_release_buffer(&temp_minor_status,
-                                                 output_token);
-                   if (internal_name != GSS_C_NO_NAME)
-                       mech->gss_release_name(
-                           &temp_minor_status,
-                           &internal_name);
-                   return (temp_status);
-               }
-               if (src_name != NULL) {
+           if (src_name != NULL) {
+               if (internal_name != GSS_C_NO_NAME) {
+                   /* consumes internal_name regardless of success */
+                   temp_status = gssint_convert_name_to_union_name(
+                           &temp_minor_status, mech,
+                           internal_name, &tmp_src_name);
+                   if (temp_status != GSS_S_COMPLETE) {
+                       *minor_status = temp_minor_status;
+                       map_error(minor_status, mech);
+                       if (output_token->length)
+                           (void) gss_release_buffer(&temp_minor_status,
+                                                     output_token);
+                       return (temp_status);
+                   }
                    *src_name = tmp_src_name;
-               }
-           } else if (src_name != NULL) {
-               *src_name = GSS_C_NO_NAME;
+               } else
+                   *src_name = GSS_C_NO_NAME;
            }
 
+#define g_OID_prefix_equal(o1, o2) \
+        (((o1)->length >= (o2)->length) && \
+        (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0))
+
            /* Ensure we're returning correct creds format */
            if ((temp_ret_flags & GSS_C_DELEG_FLAG) &&
                tmp_d_cred != GSS_C_NO_CREDENTIAL) {
-               gss_union_cred_t d_u_cred = NULL;
-
-               d_u_cred = malloc(sizeof (gss_union_cred_desc));
-               if (d_u_cred == NULL) {
-                   status = GSS_S_FAILURE;
-                   goto error_out;
-               }
-               (void) memset(d_u_cred, 0,
-                             sizeof (gss_union_cred_desc));
-
-               d_u_cred->count = 1;
+               if (actual_mech != GSS_C_NO_OID &&
+                   !g_OID_prefix_equal(actual_mech, token_mech_type)) {
+                   *d_cred = tmp_d_cred; /* unwrapped pseudo-mech */
+               } else {
+                   gss_union_cred_t d_u_cred = NULL;
 
-               status = generic_gss_copy_oid(&temp_minor_status,
-                                             token_mech_type,
-                                             &d_u_cred->mechs_array);
+                   d_u_cred = malloc(sizeof (gss_union_cred_desc));
+                   if (d_u_cred == NULL) {
+                       status = GSS_S_FAILURE;
+                       goto error_out;
+                   }
+                   (void) memset(d_u_cred, 0, sizeof (gss_union_cred_desc));
 
-               if (status != GSS_S_COMPLETE) {
-                   free(d_u_cred);
-                   goto error_out;
-               }
+                   d_u_cred->count = 1;
 
-               d_u_cred->cred_array = malloc(sizeof (gss_cred_id_t));
-               if (d_u_cred->cred_array != NULL) {
-                   d_u_cred->cred_array[0] = tmp_d_cred;
-               } else {
-                   free(d_u_cred);
-                   status = GSS_S_FAILURE;
-                   goto error_out;
-               }
+                   status = generic_gss_copy_oid(&temp_minor_status,
+                                                 token_mech_type,
+                                                 &d_u_cred->mechs_array);
 
-               internal_name = GSS_C_NO_NAME;
+                   if (status != GSS_S_COMPLETE) {
+                       free(d_u_cred);
+                       goto error_out;
+                   }
 
-               d_u_cred->auxinfo.creation_time = time(0);
-               d_u_cred->auxinfo.time_rec = 0;
-               d_u_cred->loopback = d_u_cred;
+                   d_u_cred->cred_array = malloc(sizeof(gss_cred_id_t));
+                   if (d_u_cred->cred_array != NULL) {
+                       d_u_cred->cred_array[0] = tmp_d_cred;
+                   } else {
+                       free(d_u_cred);
+                       status = GSS_S_FAILURE;
+                       goto error_out;
+                   }
 
-               if (mech->gss_inquire_cred) {
-                   status = mech->gss_inquire_cred(minor_status,
-                                                   tmp_d_cred,
-                                                   &internal_name,
-                                                   &d_u_cred->auxinfo.time_rec,
-                                                   &d_u_cred->auxinfo.cred_usage,
-                                                   NULL);
-                   if (status != GSS_S_COMPLETE)
-                       map_error(minor_status, mech);
-               }
+                   d_u_cred->auxinfo.creation_time = time(0);
+                   d_u_cred->auxinfo.time_rec = 0;
+                   d_u_cred->loopback = d_u_cred;
+
+                   internal_name = GSS_C_NO_NAME;
+
+                   if (mech->gss_inquire_cred) {
+                       status = mech->gss_inquire_cred(minor_status,
+                                                       tmp_d_cred,
+                                                       &internal_name,
+                                                       &d_u_cred->auxinfo.time_rec,
+                                                       &d_u_cred->auxinfo.cred_usage,
+                                                       NULL);
+                       if (status != GSS_S_COMPLETE)
+                           map_error(minor_status, mech);
+                   }
 
-               if (internal_name != NULL) {
-                   temp_status = gssint_convert_name_to_union_name(
-                       &temp_minor_status, mech,
-                       internal_name, &tmp_src_name);
-                   if (temp_status != GSS_S_COMPLETE) {
-                       *minor_status = temp_minor_status;
-                       map_error(minor_status, mech);
-                       if (output_token->length)
-                           (void) gss_release_buffer(
+                   if (internal_name != GSS_C_NO_NAME) {
+                       /* consumes internal_name regardless of success */
+                       temp_status = gssint_convert_name_to_union_name(
+                           &temp_minor_status, mech,
+                           internal_name, &tmp_src_name);
+                       if (temp_status != GSS_S_COMPLETE) {
+                           *minor_status = temp_minor_status;
+                           map_error(minor_status, mech);
+                           if (output_token->length)
+                               (void) gss_release_buffer(
+                                   &temp_minor_status,
+                                   output_token);
+                           (void) gss_release_oid(&temp_minor_status,
+                                                  &actual_mech);
+                           free(d_u_cred->cred_array);
+                           free(d_u_cred);
+                           return (temp_status);
+                       }
+
+                       if (tmp_src_name != GSS_C_NO_NAME) {
+                           status = gss_display_name(
                                &temp_minor_status,
-                               output_token);
-                       free(d_u_cred->cred_array);
-                       free(d_u_cred);
-                       return (temp_status);
+                               tmp_src_name,
+                               &d_u_cred->auxinfo.name,
+                               &d_u_cred->auxinfo.name_type);
+                           (void) gss_release_name(&temp_minor_status,
+                                                   &tmp_src_name);
+                       }
                    }
-               }
 
-               if (tmp_src_name != NULL) {
-                   status = gss_display_name(
-                       &temp_minor_status,
-                       tmp_src_name,
-                       &d_u_cred->auxinfo.name,
-                       &d_u_cred->auxinfo.name_type);
+                   *d_cred = (gss_cred_id_t)d_u_cred;
                }
-
-               *d_cred = (gss_cred_id_t)d_u_cred;
            }
 
-           if (src_name == NULL && tmp_src_name != NULL)
-               (void) gss_release_name(&temp_minor_status,
-                                       &tmp_src_name);
+           if (mech_type != NULL)
+               *mech_type = actual_mech;
+           else
+               (void) gss_release_oid(&temp_minor_status, &actual_mech);
            if (ret_flags != NULL)
                *ret_flags = temp_ret_flags;
            return      (status);
index fada9e88725c31ead0cd59dc402a4a998af6e4f4..6dfc65f7b290e68f9005d5f8828537ce9d677165 100644 (file)
@@ -2,7 +2,7 @@
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
- * 
+ *
  * Permission to use, copy, modify, distribute, and sell this software
  * and its documentation for any purpose is hereby granted without fee,
  * provided that the above copyright notice appears in all copies and
@@ -12,7 +12,7 @@
  * without specific, written prior permission. Sun Microsystems makes no
  * representations about the suitability of this software for any
  * purpose.  It is provided "as is" without express or implied warranty.
- * 
+ *
  * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 #include <errno.h>
 #include <time.h>
 
-static gss_OID_set
-create_actual_mechs(mechs_array, count)
-    const gss_OID      mechs_array;
-    int count;
-{
-    gss_OID_set        actual_mechs;
-    int                        i;
-    OM_uint32          minor;
-
-    actual_mechs = (gss_OID_set) malloc(sizeof(gss_OID_set_desc));
-    if (!actual_mechs)
-       return NULL;
-
-    actual_mechs->elements = (gss_OID)
-       malloc(sizeof (gss_OID_desc) * count);
-    if (!actual_mechs->elements) {
-       free(actual_mechs);
-       return NULL;
-    }
-    
-    actual_mechs->count = 0;
-
-    for (i = 0; i < count; i++) {
-       actual_mechs->elements[i].elements = (void *)
-           malloc(mechs_array[i].length);
-       if (actual_mechs->elements[i].elements == NULL) {
-           (void) gss_release_oid_set(&minor, &actual_mechs);
-           return (NULL);
-       }
-       g_OID_copy(&actual_mechs->elements[i], &mechs_array[i]);
-       actual_mechs->count++;
-    }
-
-    return actual_mechs;
-}
-
 static OM_uint32
 val_acq_cred_args(
     OM_uint32 *minor_status,
@@ -172,7 +136,7 @@ OM_uint32 *         time_rec;
        mech = gssint_get_mechanism(NULL);
        if (mech == NULL)
            return (GSS_S_BAD_MECH);
-       
+
        mechs = &default_OID_set;
        default_OID_set.count = 1;
        default_OID_set.elements = &default_OID;
@@ -234,12 +198,16 @@ OM_uint32 *               time_rec;
      * setup the actual mechs output parameter
      */
     if (actual_mechs != NULL) {
-       if ((*actual_mechs = create_actual_mechs(creds->mechs_array,
-                                                creds->count)) == NULL) {
+       gss_OID_set_desc oids;
+
+       oids.count = creds->count;
+       oids.elements = creds->mechs_array;
+
+       major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+       if (GSS_ERROR(major)) {
            (void) gss_release_cred(minor_status,
                                    (gss_cred_id_t *)&creds);
-           *minor_status = 0;
-           return (GSS_S_FAILURE);
+           return (major);
        }
     }
 
@@ -312,7 +280,7 @@ OM_uint32 KRB5_CALLCONV
 gss_add_cred(minor_status, input_cred_handle,
                  desired_name, desired_mech, cred_usage,
                  initiator_time_req, acceptor_time_req,
-                 output_cred_handle, actual_mechs, 
+                 output_cred_handle, actual_mechs,
                  initiator_time_rec, acceptor_time_rec)
     OM_uint32          *minor_status;
     gss_cred_id_t      input_cred_handle;
@@ -434,7 +402,7 @@ gss_add_cred(minor_status, input_cred_handle,
            status = mech->gss_display_name(&temp_minor_status, internal_name,
                                            &union_cred->auxinfo.name,
                                            &union_cred->auxinfo.name_type);
-       
+
            if (status != GSS_S_COMPLETE)
                goto errout;
        }
@@ -475,10 +443,14 @@ gss_add_cred(minor_status, input_cred_handle,
     g_OID_copy(&new_mechs_array[union_cred->count],
               &mech->mech_type);
 
-    if (actual_mechs) {
-       *actual_mechs = create_actual_mechs(new_mechs_array,
-                                           union_cred->count + 1);
-       if (*actual_mechs == NULL) {
+    if (actual_mechs != NULL) {
+       gss_OID_set_desc oids;
+
+       oids.count = union_cred->count + 1;
+       oids.elements = new_mechs_array;
+
+       status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+       if (GSS_ERROR(status)) {
            free(new_mechs_array[union_cred->count].elements);
            goto errout;
        }
diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c b/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c
new file mode 100644 (file)
index 0000000..9ba6a1f
--- /dev/null
@@ -0,0 +1,549 @@
+/* #pragma ident       "@(#)g_acquire_cred.c   1.22    04/02/23 SMI" */
+
+/*
+ * Copyright 2009 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.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ *
+ */
+/*
+ * Copyright 1996 by Sun Microsystems, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Sun Microsystems not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. Sun Microsystems makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ *  glue routine for gss_acquire_cred_impersonate_name
+ */
+
+#include "mglueP.h"
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+static OM_uint32
+val_acq_cred_impersonate_name_args(
+    OM_uint32 *minor_status,
+    const gss_cred_id_t impersonator_cred_handle,
+    const gss_name_t desired_name,
+    OM_uint32 time_req,
+    gss_OID_set desired_mechs,
+    gss_cred_usage_t cred_usage,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *time_rec)
+{
+
+    /* Initialize outputs. */
+
+    if (minor_status != NULL)
+       *minor_status = 0;
+
+    if (output_cred_handle != NULL)
+       *output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+    if (actual_mechs != NULL)
+       *actual_mechs = GSS_C_NULL_OID_SET;
+
+    if (time_rec != NULL)
+       *time_rec = 0;
+
+    /* Validate arguments. */
+
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL)
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED);
+
+    if (desired_name == GSS_C_NO_NAME)
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
+
+    if (output_cred_handle == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (cred_usage != GSS_C_ACCEPT
+       && cred_usage != GSS_C_INITIATE
+       && cred_usage != GSS_C_BOTH) {
+       if (minor_status) {
+           *minor_status = EINVAL;
+           map_errcode(minor_status);
+       }
+       return GSS_S_FAILURE;
+    }
+
+    return (GSS_S_COMPLETE);
+}
+
+
+OM_uint32 KRB5_CALLCONV
+gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
+                                 const gss_cred_id_t impersonator_cred_handle,
+                                 const gss_name_t desired_name,
+                                 OM_uint32 time_req,
+                                 const gss_OID_set desired_mechs,
+                                 gss_cred_usage_t cred_usage,
+                                 gss_cred_id_t *output_cred_handle,
+                                 gss_OID_set *actual_mechs,
+                                 OM_uint32 *time_rec)
+{
+    OM_uint32 major = GSS_S_FAILURE;
+    OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
+    gss_OID_set_desc default_OID_set;
+    gss_OID_set mechs;
+    gss_OID_desc default_OID;
+    gss_mechanism mech;
+    unsigned int i;
+    gss_union_cred_t creds;
+
+    major = val_acq_cred_impersonate_name_args(minor_status,
+                                              impersonator_cred_handle,
+                                              desired_name,
+                                              time_req,
+                                              desired_mechs,
+                                              cred_usage,
+                                              output_cred_handle,
+                                              actual_mechs,
+                                              time_rec);
+    if (major != GSS_S_COMPLETE)
+       return (major);
+
+    /* Initial value needed below. */
+    major = GSS_S_FAILURE;
+
+    /*
+     * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
+     * appropriate default.  We use the first mechanism in the
+     * mechansim list as the default. This set is created with
+     * statics thus needs not be freed
+     */
+    if(desired_mechs == GSS_C_NULL_OID_SET) {
+       mech = gssint_get_mechanism(NULL);
+       if (mech == NULL)
+           return (GSS_S_BAD_MECH);
+
+       mechs = &default_OID_set;
+       default_OID_set.count = 1;
+       default_OID_set.elements = &default_OID;
+       default_OID.length = mech->mech_type.length;
+       default_OID.elements = mech->mech_type.elements;
+    } else
+       mechs = desired_mechs;
+
+    if (mechs->count == 0)
+       return (GSS_S_BAD_MECH);
+
+    /* allocate the output credential structure */
+    creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
+    if (creds == NULL)
+       return (GSS_S_FAILURE);
+
+    /* initialize to 0s */
+    (void) memset(creds, 0, sizeof (gss_union_cred_desc));
+    creds->loopback = creds;
+
+    /* for each requested mech attempt to obtain a credential */
+    for (i = 0; i < mechs->count; i++) {
+       major = gss_add_cred_impersonate_name(minor_status,
+                                             (gss_cred_id_t)creds,
+                                             impersonator_cred_handle,
+                                             desired_name,
+                                             &mechs->elements[i],
+                                             cred_usage,
+                                             time_req,
+                                             time_req, NULL,
+                                             NULL,
+                                             &initTimeOut,
+                                             &acceptTimeOut);
+       if (major == GSS_S_COMPLETE) {
+           /* update the credential's time */
+           if (cred_usage == GSS_C_ACCEPT) {
+               if (outTime > acceptTimeOut)
+                   outTime = acceptTimeOut;
+           } else if (cred_usage == GSS_C_INITIATE) {
+               if (outTime > initTimeOut)
+                   outTime = initTimeOut;
+           } else {
+               /*
+                * time_rec is the lesser of the
+                * init/accept times
+                */
+               if (initTimeOut > acceptTimeOut)
+                   outTime = (outTime > acceptTimeOut) ?
+                       acceptTimeOut : outTime;
+               else
+                   outTime = (outTime > initTimeOut) ?
+                       initTimeOut : outTime;
+           }
+       }
+    } /* for */
+
+    /* ensure that we have at least one credential element */
+    if (creds->count < 1) {
+       free(creds);
+       return (major);
+    }
+
+    /*
+     * fill in output parameters
+     * setup the actual mechs output parameter
+     */
+    if (actual_mechs != NULL) {
+       gss_OID_set_desc oids;
+
+       oids.count = creds->count;
+       oids.elements = creds->mechs_array;
+
+       major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+       if (GSS_ERROR(major)) {
+           (void) gss_release_cred(minor_status,
+                                   (gss_cred_id_t *)&creds);
+           return (major);
+       }
+    }
+
+    if (time_rec)
+       *time_rec = outTime;
+
+
+    creds->loopback = creds;
+    *output_cred_handle = (gss_cred_id_t)creds;
+    return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+val_add_cred_impersonate_name_args(
+    OM_uint32 *minor_status,
+    gss_cred_id_t input_cred_handle,
+    const gss_cred_id_t impersonator_cred_handle,
+    gss_name_t desired_name,
+    gss_OID desired_mech,
+    gss_cred_usage_t cred_usage,
+    OM_uint32 initiator_time_req,
+    OM_uint32 acceptor_time_req,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *initiator_time_rec,
+    OM_uint32 *acceptor_time_rec)
+{
+
+    /* Initialize outputs. */
+
+    if (minor_status != NULL)
+       *minor_status = 0;
+
+    if (output_cred_handle != NULL)
+       *output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+    if (actual_mechs != NULL)
+       *actual_mechs = GSS_C_NO_OID_SET;
+
+    if (acceptor_time_rec != NULL)
+       *acceptor_time_rec = 0;
+
+    if (initiator_time_rec != NULL)
+       *initiator_time_rec = 0;
+
+    /* Validate arguments. */
+
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL)
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED);
+
+    if (desired_name == GSS_C_NO_NAME)
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
+       output_cred_handle == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
+
+    if (cred_usage != GSS_C_ACCEPT
+       && cred_usage != GSS_C_INITIATE
+       && cred_usage != GSS_C_BOTH) {
+       if (minor_status) {
+           *minor_status = EINVAL;
+           map_errcode(minor_status);
+       }
+       return GSS_S_FAILURE;
+    }
+
+    return (GSS_S_COMPLETE);
+}
+
+
+/* V2 KRB5_CALLCONV */
+OM_uint32 KRB5_CALLCONV
+gss_add_cred_impersonate_name(OM_uint32 *minor_status,
+                             gss_cred_id_t input_cred_handle,
+                             const gss_cred_id_t impersonator_cred_handle,
+                             const gss_name_t desired_name,
+                             const gss_OID desired_mech,
+                             gss_cred_usage_t cred_usage,
+                             OM_uint32 initiator_time_req,
+                             OM_uint32 acceptor_time_req,
+                             gss_cred_id_t *output_cred_handle,
+                             gss_OID_set *actual_mechs,
+                             OM_uint32 *initiator_time_rec,
+                             OM_uint32 *acceptor_time_rec)
+{
+    OM_uint32          status, temp_minor_status;
+    OM_uint32          time_req, time_rec;
+    gss_union_name_t   union_name;
+    gss_union_cred_t   new_union_cred, union_cred;
+    gss_cred_id_t      mech_impersonator_cred;
+    gss_name_t         internal_name = GSS_C_NO_NAME;
+    gss_name_t         allocated_name = GSS_C_NO_NAME;
+    gss_mechanism      mech;
+    gss_cred_id_t      cred = NULL;
+    gss_OID            new_mechs_array = NULL;
+    gss_cred_id_t *    new_cred_array = NULL;
+
+    status = val_add_cred_impersonate_name_args(minor_status,
+                                               input_cred_handle,
+                                               impersonator_cred_handle,
+                                               desired_name,
+                                               desired_mech,
+                                               cred_usage,
+                                               initiator_time_req,
+                                               acceptor_time_req,
+                                               output_cred_handle,
+                                               actual_mechs,
+                                               initiator_time_rec,
+                                               acceptor_time_rec);
+    if (status != GSS_S_COMPLETE)
+       return (status);
+
+    mech = gssint_get_mechanism(desired_mech);
+    if (!mech)
+       return GSS_S_BAD_MECH;
+    else if (!mech->gss_acquire_cred)
+       return (GSS_S_UNAVAILABLE);
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
+       union_cred = malloc(sizeof (gss_union_cred_desc));
+       if (union_cred == NULL)
+           return (GSS_S_FAILURE);
+
+       (void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
+
+       /* for default credentials we will use GSS_C_NO_NAME */
+       internal_name = GSS_C_NO_NAME;
+    } else {
+       union_cred = (gss_union_cred_t)input_cred_handle;
+       if (gssint_get_mechanism_cred(union_cred, desired_mech) !=
+           GSS_C_NO_CREDENTIAL)
+           return (GSS_S_DUPLICATE_ELEMENT);
+    }
+
+    mech_impersonator_cred =
+       gssint_get_mechanism_cred((gss_union_cred_t)impersonator_cred_handle,
+                                 desired_mech);
+    if (mech_impersonator_cred == GSS_C_NO_CREDENTIAL)
+       return (GSS_S_NO_CRED);
+
+    /* may need to create a mechanism specific name */
+    union_name = (gss_union_name_t)desired_name;
+    if (union_name->mech_type &&
+       g_OID_equal(union_name->mech_type,
+                   &mech->mech_type))
+       internal_name = union_name->mech_name;
+    else {
+       if (gssint_import_internal_name(minor_status,
+                                       &mech->mech_type, union_name,
+                                       &allocated_name) != GSS_S_COMPLETE)
+           return (GSS_S_BAD_NAME);
+       internal_name = allocated_name;
+    }
+
+    if (cred_usage == GSS_C_ACCEPT)
+       time_req = acceptor_time_req;
+    else if (cred_usage == GSS_C_INITIATE)
+       time_req = initiator_time_req;
+    else if (cred_usage == GSS_C_BOTH)
+       time_req = (acceptor_time_req > initiator_time_req) ?
+           acceptor_time_req : initiator_time_req;
+    else
+       time_req = 0;
+
+    status = mech->gss_acquire_cred_impersonate_name(minor_status,
+                                                    mech_impersonator_cred,
+                                                    internal_name,
+                                                    time_req,
+                                                    GSS_C_NULL_OID_SET,
+                                                    cred_usage,
+                                                    &cred,
+                                                    NULL,
+                                                    &time_rec);
+    if (status != GSS_S_COMPLETE) {
+       map_error(minor_status, mech);
+       goto errout;
+    }
+
+    /* may need to set credential auxinfo strucutre */
+    if (union_cred->auxinfo.creation_time == 0) {
+       union_cred->auxinfo.creation_time = time(NULL);
+       union_cred->auxinfo.time_rec = time_rec;
+       union_cred->auxinfo.cred_usage = cred_usage;
+
+       /*
+        * we must set the name; if name is not supplied
+        * we must do inquire cred to get it
+        */
+       if (internal_name == NULL) {
+           if (mech->gss_inquire_cred == NULL ||
+               ((status = mech->gss_inquire_cred(
+                     &temp_minor_status, cred,
+                     &allocated_name, NULL, NULL,
+                     NULL)) != GSS_S_COMPLETE))
+               goto errout;
+           internal_name = allocated_name;
+       }
+
+       if (internal_name != GSS_C_NO_NAME) {
+           status = mech->gss_display_name(&temp_minor_status, internal_name,
+                                           &union_cred->auxinfo.name,
+                                           &union_cred->auxinfo.name_type);
+
+           if (status != GSS_S_COMPLETE)
+               goto errout;
+       }
+    }
+
+    /* now add the new credential elements */
+    new_mechs_array = (gss_OID)
+       malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
+
+    new_cred_array = (gss_cred_id_t *)
+       malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
+
+    if (!new_mechs_array || !new_cred_array) {
+       status = GSS_S_FAILURE;
+       goto errout;
+    }
+
+    if (acceptor_time_rec)
+       if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
+           *acceptor_time_rec = time_rec;
+    if (initiator_time_rec)
+       if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
+           *initiator_time_rec = time_rec;
+
+    /*
+     * OK, expand the mechanism array and the credential array
+     */
+    (void) memcpy(new_mechs_array, union_cred->mechs_array,
+                 sizeof (gss_OID_desc) * union_cred->count);
+    (void) memcpy(new_cred_array, union_cred->cred_array,
+                 sizeof (gss_cred_id_t) * union_cred->count);
+
+    new_cred_array[union_cred->count] = cred;
+    if ((new_mechs_array[union_cred->count].elements =
+        malloc(mech->mech_type.length)) == NULL)
+       goto errout;
+
+    g_OID_copy(&new_mechs_array[union_cred->count],
+              &mech->mech_type);
+
+    if (actual_mechs != NULL) {
+       gss_OID_set_desc oids;
+
+       oids.count = union_cred->count + 1;
+       oids.elements = new_mechs_array;
+
+       status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+       if (GSS_ERROR(status)) {
+           free(new_mechs_array[union_cred->count].elements);
+           goto errout;
+       }
+    }
+
+    if (output_cred_handle == NULL) {
+       free(union_cred->mechs_array);
+       free(union_cred->cred_array);
+       new_union_cred = union_cred;
+    } else {
+       new_union_cred = malloc(sizeof (gss_union_cred_desc));
+       if (new_union_cred == NULL) {
+           free(new_mechs_array[union_cred->count].elements);
+           goto errout;
+       }
+       *new_union_cred = *union_cred;
+       *output_cred_handle = (gss_cred_id_t)new_union_cred;
+    }
+
+    new_union_cred->mechs_array = new_mechs_array;
+    new_union_cred->cred_array = new_cred_array;
+    new_union_cred->count++;
+    new_union_cred->loopback = new_union_cred;
+
+    /* We're done with the internal name. Free it if we allocated it. */
+
+    if (allocated_name)
+       (void) gssint_release_internal_name(&temp_minor_status,
+                                          &mech->mech_type,
+                                          &allocated_name);
+
+    return (GSS_S_COMPLETE);
+
+errout:
+    if (new_mechs_array)
+       free(new_mechs_array);
+    if (new_cred_array)
+       free(new_cred_array);
+
+    if (cred != NULL && mech->gss_release_cred)
+       mech->gss_release_cred(&temp_minor_status, &cred);
+
+    if (allocated_name)
+       (void) gssint_release_internal_name(&temp_minor_status,
+                                          &mech->mech_type,
+                                          &allocated_name);
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) {
+       if (union_cred->auxinfo.name.value)
+           free(union_cred->auxinfo.name.value);
+       free(union_cred);
+    }
+
+    return (status);
+}
index 5a8ea54b1e3b4d8c96c899a085d490e8a40701f2..4d35819c58ba09167e21db1c461392300b4ce84a 100644 (file)
@@ -611,25 +611,9 @@ gssint_get_mechanism_cred(union_cred, mech_type)
     if (union_cred == GSS_C_NO_CREDENTIAL)
        return GSS_C_NO_CREDENTIAL;
 
-    /* SPNEGO mechanism will again call into GSSAPI */
-    if (g_OID_equal(&gss_spnego_mechanism_oid_desc, mech_type))
-       return (gss_cred_id_t)union_cred;
-
     for (i=0; i < union_cred->count; i++) {
        if (g_OID_equal(mech_type, &union_cred->mechs_array[i]))
            return union_cred->cred_array[i];
-
-       /* for SPNEGO, check the next-lower set of creds */
-       if (g_OID_equal(&gss_spnego_mechanism_oid_desc, &union_cred->mechs_array[i])) {
-           gss_union_cred_t candidate_cred;
-           gss_cred_id_t    sub_cred;
-
-           candidate_cred = (gss_union_cred_t)union_cred->cred_array[i];
-           sub_cred = gssint_get_mechanism_cred(candidate_cred, mech_type);
-
-           if(sub_cred != GSS_C_NO_CREDENTIAL)
-               return sub_cred;
-       }
     }
     return GSS_C_NO_CREDENTIAL;
 }
index 85fbe63219fb029cf0860f8a46d25da36544c544..e34b7bf0a7ffa46e3d14a875feb3ce721fa28e48 100644 (file)
@@ -761,6 +761,9 @@ build_dynamicMech(void *dl, const gss_OID mech_type)
        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_unwrap_iov);
        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_wrap_iov_length);
        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_complete_auth_token);
+       /* New for 1.8 */
+       GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_acquire_cred_impersonate_name);
+       GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_add_cred_impersonate_name);
 
        assert(mech_type != GSS_C_NO_OID);
 
index 17d9e3bace334b0e1e9e2e5a6a0df1b5cb3fb63d..b35b36ad548f8f994850ce628d758088f4ba5ae5 100644 (file)
@@ -70,8 +70,8 @@ gss_set_sec_context_option (OM_uint32 *minor_status,
     }
 
     status = mech->gss_set_sec_context_option(minor_status,
-                                             ctx ? &internal_ctx :
-                                               &ctx->internal_ctx_id,
+                                             ctx ? &ctx->internal_ctx_id :
+                                             &internal_ctx,
                                              desired_object,
                                              value);
     if (status == GSS_S_COMPLETE) {
index 001636146ea57ddffe71e376fabc9e4b1106c018..46bfb946362cab2901089aa0337ac1d78e932a6a 100644 (file)
@@ -473,6 +473,37 @@ typedef struct gss_config {
                    gss_buffer_t        /* input_message_buffer */
                    );
 
+       /* New for 1.8 */
+
+       OM_uint32       (*gss_acquire_cred_impersonate_name)
+       (
+           OM_uint32 *,                /* minor_status */
+           const gss_cred_id_t,        /* impersonator_cred_handle */
+           const gss_name_t,           /* desired_name */
+           OM_uint32,                  /* time_req */
+           const gss_OID_set,          /* desired_mechs */
+           gss_cred_usage_t,           /* cred_usage */
+           gss_cred_id_t *,            /* output_cred_handle */
+           gss_OID_set *,              /* actual_mechs */
+           OM_uint32 *                 /* time_rec */
+       /* */);
+
+       OM_uint32       (*gss_add_cred_impersonate_name)
+       (
+           OM_uint32 *,                /* minor_status */
+           gss_cred_id_t,              /* input_cred_handle */
+           const gss_cred_id_t,        /* impersonator_cred_handle */
+           const gss_name_t,           /* desired_name */
+           const gss_OID,              /* desired_mech */
+           gss_cred_usage_t,           /* cred_usage */
+           OM_uint32,                  /* initiator_time_req */
+           OM_uint32,                  /* acceptor_time_req */
+           gss_cred_id_t *,            /* output_cred_handle */
+           gss_OID_set *,              /* actual_mechs */
+           OM_uint32 *,                /* initiator_time_rec */
+           OM_uint32 *                 /* acceptor_time_rec */
+       /* */);
+
 } *gss_mechanism;
 
 /* This structure MUST NOT be used by any code outside libgss */
index e1f3987cd25fa146b4e569bc2df0c55541c7d684..5e6cd5a0c8f31fc5eddea3e2191f77a2530a0f78 100644 (file)
@@ -218,6 +218,16 @@ OM_uint32 spnego_gss_release_name
        gss_name_t *            /* input_name */
 );
 
+OM_uint32 spnego_gss_inquire_cred
+(
+       OM_uint32 *,            /* minor_status */
+       gss_cred_id_t,          /* cred_handle */
+       gss_name_t *,           /* name */
+       OM_uint32 *,            /* lifetime */
+       int *,                  /* cred_usage */
+       gss_OID_set *           /* mechanisms */
+);
+
 OM_uint32 spnego_gss_inquire_names_for_mech
 (
        OM_uint32 *,            /* minor_status */
@@ -332,6 +342,15 @@ spnego_gss_inquire_sec_context_by_oid
        gss_buffer_set_t *data_set
 );
 
+OM_uint32
+spnego_gss_inquire_cred_by_oid
+(
+       OM_uint32 *minor_status,
+       const gss_cred_id_t cred_handle,
+       const gss_OID desired_object,
+       gss_buffer_set_t *data_set
+);
+
 OM_uint32
 spnego_gss_set_sec_context_option
 (
@@ -411,6 +430,18 @@ spnego_gss_complete_auth_token
        gss_buffer_t input_message_buffer
 );
 
+OM_uint32
+spnego_gss_acquire_cred_impersonate_name(
+    OM_uint32 *,           /* minor_status */
+    const gss_cred_id_t,    /* impersonator_cred_handle */
+    const gss_name_t,      /* desired_name */
+    OM_uint32,             /* time_req */
+    const gss_OID_set,     /* desired_mechs */
+    gss_cred_usage_t,      /* cred_usage */
+    gss_cred_id_t *,       /* output_cred_handle */
+    gss_OID_set *,         /* actual_mechs */
+    OM_uint32 *);          /* time_rec */
+
 #ifdef __cplusplus
 }
 #endif
index a2b926dc39051abc6d844a58f4aba9c21a755326..14b65f7511132eb141842d3202bf510f772bf91c 100644 (file)
@@ -231,7 +231,7 @@ static struct gss_config spnego_mechanism =
        spnego_gss_display_name,
        spnego_gss_import_name,
        spnego_gss_release_name,
-       NULL,                           /* gss_inquire_cred */
+       spnego_gss_inquire_cred,        /* gss_inquire_cred */
        NULL,                           /* gss_add_cred */
 #ifndef LEAN_CLIENT
        spnego_gss_export_sec_context,          /* gss_export_sec_context */
@@ -248,7 +248,7 @@ static struct gss_config spnego_mechanism =
        NULL,                           /* gss_export_name */
        NULL,                           /* gss_store_cred */
        spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
-       NULL,                           /* gss_inquire_cred_by_oid */
+       spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */
        spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
        NULL,                           /* gssspi_set_cred_option */
        NULL,                           /* gssspi_mech_invoke */
@@ -257,7 +257,9 @@ static struct gss_config spnego_mechanism =
        spnego_gss_wrap_iov,
        spnego_gss_unwrap_iov,
        spnego_gss_wrap_iov_length,
-       spnego_gss_complete_auth_token
+       spnego_gss_complete_auth_token,
+       spnego_gss_acquire_cred_impersonate_name,
+       NULL,                           /* gss_add_cred_impersonate_name */
 };
 
 #ifdef _GSS_STATIC_LINK
@@ -1787,6 +1789,76 @@ spnego_gss_release_name(
        return (status);
 }
 
+OM_uint32
+spnego_gss_inquire_cred(
+                       OM_uint32 *minor_status,
+                       gss_cred_id_t cred_handle,
+                       gss_name_t *name,
+                       OM_uint32 *lifetime,
+                       int *cred_usage,
+                       gss_OID_set *mechanisms)
+{
+       OM_uint32 status;
+       gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
+       OM_uint32 tmp_minor_status;
+       OM_uint32 initiator_lifetime, acceptor_lifetime;
+
+       dsyslog("Entering inquire_cred\n");
+
+       /*
+        * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
+        * supplied we call gss_inquire_cred_by_mech() on the
+        * first non-SPNEGO mechanism.
+        */
+       if (cred_handle == GSS_C_NO_CREDENTIAL) {
+               status = get_available_mechs(minor_status,
+                       GSS_C_NO_NAME,
+                       GSS_C_BOTH,
+                       &creds,
+                       mechanisms);
+               if (status != GSS_S_COMPLETE) {
+                       dsyslog("Leaving inquire_cred\n");
+                       return (status);
+               }
+
+               if ((*mechanisms)->count == 0) {
+                       gss_release_cred(&tmp_minor_status, &creds);
+                       gss_release_oid_set(&tmp_minor_status, mechanisms);
+                       dsyslog("Leaving inquire_cred\n");
+                       return (GSS_S_DEFECTIVE_CREDENTIAL);
+               }
+
+               assert((*mechanisms)->elements != NULL);
+
+               status = gss_inquire_cred_by_mech(minor_status,
+                       creds,
+                       &(*mechanisms)->elements[0],
+                       name,
+                       &initiator_lifetime,
+                       &acceptor_lifetime,
+                       cred_usage);
+               if (status != GSS_S_COMPLETE) {
+                       gss_release_cred(&tmp_minor_status, &creds);
+                       dsyslog("Leaving inquire_cred\n");
+                       return (status);
+               }
+
+               if (lifetime != NULL)
+                       *lifetime = (*cred_usage == GSS_C_ACCEPT) ?
+                               acceptor_lifetime : initiator_lifetime;
+
+               gss_release_cred(&tmp_minor_status, &creds);
+       } else {
+               status = gss_inquire_cred(minor_status, cred_handle,
+                                         name, lifetime,
+                                         cred_usage, mechanisms);
+       }
+
+       dsyslog("Leaving inquire_cred\n");
+
+       return (status);
+}
+
 /*ARGSUSED*/
 OM_uint32
 spnego_gss_compare_name(
@@ -1942,6 +2014,9 @@ spnego_gss_delete_sec_context(
         */
        if (*ctx != NULL &&
            (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
+               (void) gss_delete_sec_context(minor_status,
+                                   &(*ctx)->ctx_handle,
+                                   output_token);
                (void) release_spnego_ctx(ctx);
        } else {
                ret = gss_delete_sec_context(minor_status,
@@ -2087,6 +2162,21 @@ spnego_gss_inquire_sec_context_by_oid(
        return (ret);
 }
 
+OM_uint32
+spnego_gss_inquire_cred_by_oid(
+               OM_uint32 *minor_status,
+               const gss_cred_id_t cred_handle,
+               const gss_OID desired_object,
+               gss_buffer_set_t *data_set)
+{
+       OM_uint32 ret;
+       ret = gss_inquire_cred_by_oid(minor_status,
+                               cred_handle,
+                               desired_object,
+                               data_set);
+       return (ret);
+}
+
 OM_uint32
 spnego_gss_set_sec_context_option(
                OM_uint32 *minor_status,
@@ -2217,6 +2307,53 @@ spnego_gss_complete_auth_token(
        return (ret);
 }
 
+OM_uint32
+spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
+                                        const gss_cred_id_t impersonator_cred_handle,
+                                        gss_name_t desired_name,
+                                        OM_uint32 time_req,
+                                        gss_OID_set desired_mechs,
+                                        gss_cred_usage_t cred_usage,
+                                        gss_cred_id_t *output_cred_handle,
+                                        gss_OID_set *actual_mechs,
+                                        OM_uint32 *time_rec)
+{
+       OM_uint32 status;
+       gss_OID_set amechs = GSS_C_NULL_OID_SET;
+
+       dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
+
+       if (actual_mechs)
+               *actual_mechs = NULL;
+
+       if (time_rec)
+               *time_rec = 0;
+
+       if (desired_mechs == GSS_C_NO_OID_SET) {
+               status = gss_inquire_cred(minor_status,
+                                         impersonator_cred_handle,
+                                         NULL, NULL,
+                                         NULL, &amechs);
+               if (status != GSS_S_COMPLETE)
+                       return status;
+
+               desired_mechs = amechs;
+       }
+
+       status = gss_acquire_cred_impersonate_name(minor_status,
+                       impersonator_cred_handle,
+                       desired_name, time_req,
+                       desired_mechs, cred_usage,
+                       output_cred_handle, actual_mechs,
+                       time_rec);
+
+       if (amechs != GSS_C_NULL_OID_SET)
+               (void) gss_release_oid_set(minor_status, &amechs);
+
+       dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
+       return (status);
+}
+
 /*
  * We will release everything but the ctx_handle so that it
  * can be passed back to init/accept context. This routine should
index 2bd99adbc1536d2d70ed0964ca9263ca22dcab62..51637f7de4615339e0b79dc16a42fd61ccd81210 100644 (file)
@@ -78,6 +78,8 @@ static const char flags_pwchange_in[] = "pwchange";
 static const char flags_service_in[]   = "service";
 static const char flags_pwsvc_in[]     = "pwservice";
 static const char flags_md5_in[]       = "md5";
+static const char flags_ok_to_auth_as_delegate_in[] = "ok-to-auth-as-delegate";
+static const char flags_no_auth_data_required_in[] = "no-auth-data-required";
 static const char flags_pdate_out[]    = "Not Postdateable";
 static const char flags_fwd_out[]      = "Not Forwardable";
 static const char flags_tgtbased_out[] = "No TGT-based requests";
@@ -85,13 +87,15 @@ static const char flags_renew_out[] = "Not renewable";
 static const char flags_proxy_out[]    = "Not proxiable";
 static const char flags_dup_skey_out[] = "No DUP_SKEY requests";
 static const char flags_tickets_out[]  = "All Tickets Disallowed";
-static const char flags_preauth_out[]  = "Preauthorization required";
-static const char flags_hwauth_out[]   = "HW Authorization required";
+static const char flags_preauth_out[]  = "Preauthentication required";
+static const char flags_hwauth_out[]   = "HW authentication required";
 static const char flags_ok_as_delegate_out[]   = "OK as Delegate";
 static const char flags_pwchange_out[] = "Password Change required";
 static const char flags_service_out[]  = "Service Disabled";
 static const char flags_pwsvc_out[]    = "Password Changing Service";
 static const char flags_md5_out[]      = "RSA-MD5 supported";
+static const char flags_ok_to_auth_as_delegate_out[] = "Protocol transition with delegation allowed";
+static const char flags_no_auth_data_required_out[] = "No authorization data required";
 static const char flags_default_neg[]  = "-";
 static const char flags_default_sep[]  = " ";
 
@@ -115,7 +119,9 @@ static const struct flags_lookup_entry flags_table[] = {
 { KRB5_KDB_REQUIRES_PWCHANGE,  1,      flags_pwchange_in, flags_pwchange_out},
 { KRB5_KDB_DISALLOW_SVR,       0,      flags_service_in,  flags_service_out },
 { KRB5_KDB_PWCHANGE_SERVICE,   1,      flags_pwsvc_in,    flags_pwsvc_out   },
-{ KRB5_KDB_SUPPORT_DESMD5,     1,      flags_md5_in,      flags_md5_out     }
+{ KRB5_KDB_SUPPORT_DESMD5,     1,      flags_md5_in,      flags_md5_out     },
+{ KRB5_KDB_OK_TO_AUTH_AS_DELEGATE,  1, flags_ok_to_auth_as_delegate_in, flags_ok_to_auth_as_delegate_out },
+{ KRB5_KDB_NO_AUTH_DATA_REQUIRED,   1, flags_no_auth_data_required_in, flags_no_auth_data_required_out }
 };
 static const int flags_table_nents = sizeof(flags_table)/
                                     sizeof(flags_table[0]);
index 1917d897433952a40979106a107a026b74ba4be1..b1b09371bc51424a99766a3af094774b06157b2c 100644 (file)
@@ -1616,6 +1616,47 @@ error_out:
     return retval;
 }
 
+asn1_error_code asn1_decode_s4u_userid(asn1buf *buf, krb5_s4u_userid *val)
+{
+    setup();
+    val->nonce = 0;
+    val->user = NULL;
+    val->subject_cert.data = NULL;
+    val->options = 0;
+    { begin_structure();
+        get_field(val->nonce,0,asn1_decode_int32);
+        alloc_principal(val->user);
+        opt_field(val->user,1,asn1_decode_principal_name,0);
+        get_field(val->user,2,asn1_decode_realm);
+        opt_lenfield(val->subject_cert.length,val->subject_cert.data,3,asn1_decode_charstring);
+        opt_field(val->options,4,asn1_decode_krb5_flags,0);
+        end_structure();
+    }
+    return 0;
+error_out:
+    krb5_free_principal(NULL, val->user);
+    krb5_free_data_contents(NULL, &val->subject_cert);
+    val->user = NULL;
+    val->subject_cert.data = NULL;
+    return retval;
+}
+
+asn1_error_code asn1_decode_pa_s4u_x509_user(asn1buf *buf, krb5_pa_s4u_x509_user *val)
+{
+    setup();
+    val->cksum.contents = NULL;
+    { begin_structure();
+        get_field(val->user_id,0,asn1_decode_s4u_userid);
+        get_field(val->cksum,1,asn1_decode_checksum);
+        end_structure();
+    }
+    return 0;
+error_out:
+    krb5_free_s4u_userid_contents(NULL, &val->user_id);
+    krb5_free_checksum_contents(NULL, &val->cksum);
+    return retval;
+}
+
 asn1_error_code asn1_decode_pa_pac_req(asn1buf *buf, krb5_pa_pac_req *val)
 {
     setup();
index 7444443ba50c3e327d5d04ed5c77c8544c7cbf40..fc62c8f4ec24a5d7009ff5355311923092daaa7a 100644 (file)
@@ -263,6 +263,10 @@ asn1_error_code asn1_decode_setpw_req
         (asn1buf *buf, krb5_data *rep, krb5_principal *principal);
 asn1_error_code asn1_decode_pa_for_user
         (asn1buf *buf, krb5_pa_for_user *val);
+asn1_error_code asn1_decode_s4u_userid
+        (asn1buf *buf, krb5_s4u_userid *val);
+asn1_error_code asn1_decode_pa_s4u_x509_user
+        (asn1buf *buf, krb5_pa_s4u_x509_user *val);
 asn1_error_code asn1_decode_pa_pac_req
         (asn1buf *buf, krb5_pa_pac_req *val);
 
index ed01b7560d6e3edc4b57343b52e150deb67331ef..cd63ffbb9528818b7683c54d4a94130df8f27fb9 100644 (file)
@@ -263,6 +263,8 @@ static unsigned int optional_enc_kdc_rep_part(const void *p)
         optional |= (1u << 8);
     if (val->caddrs != NULL && val->caddrs[0] != NULL)
         optional |= (1u << 11);
+    if (val->enc_padata != NULL)
+        optional |= (1u << 12);
 
     return optional;
 }
@@ -1147,6 +1149,36 @@ static const struct field_info pa_for_user_fields[] = {
 
 DEFSEQTYPE(pa_for_user, krb5_pa_for_user, pa_for_user_fields, 0);
 
+/* [MS-SFU] Section 2.2.2. */
+static const struct field_info s4u_userid_fields[] = {
+    FIELDOF_NORM(krb5_s4u_userid, int32, nonce, 0),
+    FIELDOF_OPT(krb5_s4u_userid, principal, user, 1, 1),
+    FIELDOF_NORM(krb5_s4u_userid, realm_of_principal, user, 2),
+    FIELDOF_OPT(krb5_s4u_userid, ostring_data, subject_cert, 3, 3),
+    FIELDOF_OPT(krb5_s4u_userid, krb5_flags, options, 4, 4),
+};
+
+static unsigned int s4u_userid_optional (const void *p) {
+    const krb5_s4u_userid *val = p;
+    unsigned int optional = 0;
+    if (val->user != NULL && val->user->length != 0)
+        optional |= (1u)<<1;
+    if (val->subject_cert.length != 0)
+        optional |= (1u)<<3;
+    if (val->options != 0)
+        optional |= (1u)<<4;
+    return optional;
+}
+
+DEFSEQTYPE(s4u_userid, krb5_s4u_userid, s4u_userid_fields, s4u_userid_optional);
+
+static const struct field_info pa_s4u_x509_user_fields[] = {
+    FIELDOF_NORM(krb5_pa_s4u_x509_user, s4u_userid, user_id, 0),
+    FIELDOF_NORM(krb5_pa_s4u_x509_user, checksum, cksum, 1),
+};
+
+DEFSEQTYPE(pa_s4u_x509_user, krb5_pa_s4u_x509_user, pa_s4u_x509_user_fields, 0);
+
 /* draft-ietf-krb-wg-kerberos-referrals Appendix A. */
 static const struct field_info pa_svr_referral_data_fields[] = {
     FIELDOF_NORM(krb5_pa_svr_referral_data, realm_of_principal, principal, 0),
@@ -1323,6 +1355,8 @@ MAKE_FULL_ENCODER(encode_krb5_predicted_sam_response,
                   predicted_sam_response);
 MAKE_FULL_ENCODER(encode_krb5_setpw_req, setpw_req);
 MAKE_FULL_ENCODER(encode_krb5_pa_for_user, pa_for_user);
+MAKE_FULL_ENCODER(encode_krb5_s4u_userid, s4u_userid);
+MAKE_FULL_ENCODER(encode_krb5_pa_s4u_x509_user, pa_s4u_x509_user);
 MAKE_FULL_ENCODER(encode_krb5_pa_svr_referral_data, pa_svr_referral_data);
 MAKE_FULL_ENCODER(encode_krb5_pa_server_referral_data, pa_server_referral_data);
 MAKE_FULL_ENCODER(encode_krb5_etype_list, etype_list);
index 7a08ec88849877112ae08c5c942b4575f80a2768..a2e9c0a4dda9b34cae2e4cd350290086110de944 100644 (file)
@@ -1060,6 +1060,18 @@ decode_krb5_pa_for_user(const krb5_data *code, krb5_pa_for_user **repptr)
     cleanup(free);
 }
 
+krb5_error_code
+decode_krb5_pa_s4u_x509_user(const krb5_data *code, krb5_pa_s4u_x509_user **repptr)
+{
+    setup_buf_only(krb5_pa_s4u_x509_user *);
+    alloc_field(rep);
+
+    retval = asn1_decode_pa_s4u_x509_user(&buf, rep);
+    if (retval) clean_return(retval);
+
+    cleanup(free);
+}
+
 krb5_error_code
 decode_krb5_pa_pac_req(const krb5_data *code, krb5_pa_pac_req **repptr)
 {
index 9715e6dfccaf1da5ba55ed4942d180d6eae17c76..8b8f6d2db0fa56a448d69a08a01795d49c93f23c 100644 (file)
@@ -79,6 +79,7 @@ STLIBOBJS= \
        rd_req_dec.o    \
        rd_safe.o       \
        recvauth.o      \
+       s4u_creds.o     \
        sendauth.o      \
        send_tgs.o      \
        ser_actx.o      \
@@ -167,6 +168,7 @@ OBJS=       $(OUTPRE)addr_comp.$(OBJEXT)    \
        $(OUTPRE)rd_req_dec.$(OBJEXT)   \
        $(OUTPRE)rd_safe.$(OBJEXT)      \
        $(OUTPRE)recvauth.$(OBJEXT)     \
+       $(OUTPRE)s4u_creds.$(OBJEXT)    \
        $(OUTPRE)sendauth.$(OBJEXT)     \
        $(OUTPRE)send_tgs.$(OBJEXT)     \
        $(OUTPRE)ser_actx.$(OBJEXT)     \
@@ -256,6 +258,7 @@ SRCS=       $(srcdir)/addr_comp.c   \
        $(srcdir)/rd_req_dec.c  \
        $(srcdir)/rd_safe.c     \
        $(srcdir)/recvauth.c    \
+       $(srcdir)/s4u_creds.c   \
        $(srcdir)/sendauth.c    \
        $(srcdir)/send_tgs.c    \
        $(srcdir)/ser_actx.c    \
index 3098e8e13cd6551753231b1cb675fbcc38fdf7f9..b3144c84e24cac09bba023914c7e66b70f6c0266 100644 (file)
@@ -1007,6 +1007,11 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
 
     DUMP_PRINC("gc_from_kdc: server as requested", supplied_server);
 
+    if (in_cred->second_ticket.length != 0 &&
+       (kdcopt & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) {
+       kdcopt |= KDC_OPT_ENC_TKT_IN_SKEY;
+    }
+
     /*
      * Try requesting a service ticket from our local KDC with referrals
      * turned on.  If the first referral succeeds, follow a referral-only
@@ -1028,9 +1033,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
         retval = krb5_get_cred_via_tkt(context, tgtptr,
                                       KDC_OPT_CANONICALIZE | 
                                       FLAGS2OPTS(tgtptr->ticket_flags) |  
-                                      kdcopt |
-                                      (in_cred->second_ticket.length ?
-                                       KDC_OPT_ENC_TKT_IN_SKEY : 0),
+                                      kdcopt,
                                       tgtptr->addresses, in_cred, out_cred);
        if (retval) {
            DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n",
@@ -1048,9 +1051,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
                     "retrying without option.\n", referral_count + 1));
            retval = krb5_get_cred_via_tkt(context, tgtptr,
                                           FLAGS2OPTS(tgtptr->ticket_flags) |  
-                                          kdcopt |
-                                          (in_cred->second_ticket.length ?
-                                           KDC_OPT_ENC_TKT_IN_SKEY : 0),
+                                          kdcopt,
                                           tgtptr->addresses,
                                           in_cred, out_cred);
            /* Whether or not that succeeded, we're done. */
@@ -1090,9 +1091,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
            retval = krb5_get_cred_via_tkt(context, tgtptr,
                                           KDC_OPT_CANONICALIZE | 
                                           FLAGS2OPTS(tgtptr->ticket_flags) |  
-                                          kdcopt |
-                                          (in_cred->second_ticket.length ?
-                                           KDC_OPT_ENC_TKT_IN_SKEY : 0),
+                                          kdcopt,
                                           tgtptr->addresses,
                                           in_cred, out_cred);
            goto cleanup;
@@ -1257,9 +1256,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
     context->use_conf_ktypes = old_use_conf_ktypes;
     retval = krb5_get_cred_via_tkt(context, tgtptr,
                                   FLAGS2OPTS(tgtptr->ticket_flags) |
-                                  kdcopt |
-                                  (in_cred->second_ticket.length ?
-                                   KDC_OPT_ENC_TKT_IN_SKEY : 0),
+                                  kdcopt,
                                   tgtptr->addresses, in_cred, out_cred);
 
 cleanup:
index 83c8026fcdafb181a207cdf17f86ce1aaa5043f8..273655ab5d03164ed7766665c7556b935345edbf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lib/krb5/krb/gc_via_tgt.c
  *
- * Copyright 1990,1991,2007,2008 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2007-2009 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -158,6 +158,27 @@ krb5_error_code
 krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
                       krb5_flags kdcoptions, krb5_address *const *address,
                       krb5_creds *in_cred, krb5_creds **out_cred)
+{
+    return krb5_get_cred_via_tkt_ext (context, tkt,
+                                     kdcoptions, address,
+                                     NULL, in_cred, NULL, NULL,
+                                     NULL, NULL, out_cred, NULL);
+}
+
+krb5_error_code
+krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
+                          krb5_flags kdcoptions, krb5_address *const *address,
+                          krb5_pa_data **in_padata,
+                          krb5_creds *in_cred,
+                          krb5_error_code (*pacb_fct)(krb5_context,
+                                                      krb5_keyblock *,
+                                                      krb5_kdc_req *,
+                                                      void *),
+                          void *pacb_data,
+                          krb5_pa_data ***out_padata,
+                          krb5_pa_data ***out_enc_padata,
+                          krb5_creds **out_cred,
+                          krb5_keyblock **out_subkey)
 {
     krb5_error_code retval;
     krb5_kdc_rep *dec_rep;
@@ -165,6 +186,7 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
     krb5_response tgsrep;
     krb5_enctype *enctypes = 0;
     krb5_keyblock *subkey = NULL;
+    krb5_boolean s4u2self = FALSE, second_tkt;
 
 #ifdef DEBUG_REFERRALS
     printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off");
@@ -179,10 +201,13 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
     if (!tkt->ticket.length)
        return KRB5_NO_TKT_SUPPLIED;
 
-    if ((kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) && 
-       (!in_cred->second_ticket.length))
+    second_tkt = ((kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) != 0);
+
+    if (second_tkt && !in_cred->second_ticket.length)
         return(KRB5_NO_2ND_TKT);
 
+    s4u2self = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_S4U_X509_USER) ||
+              krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FOR_USER);
 
     /* check if we have the right TGT                    */
     /* tkt->server must be equal to                      */
@@ -210,13 +235,12 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
        enctypes[0] = in_cred->keyblock.enctype;
        enctypes[1] = 0;
     }
-    
+
     retval = krb5int_send_tgs(context, kdcoptions, &in_cred->times, enctypes, 
                           in_cred->server, address, in_cred->authdata,
-                          0,           /* no padata */
-                          (kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) ? 
-                          &in_cred->second_ticket : NULL,
-                          tkt, &tgsrep, &subkey);
+                          in_padata,
+                          second_tkt ? &in_cred->second_ticket : NULL,
+                          tkt, pacb_fct, pacb_data, &tgsrep, &subkey);
     if (enctypes)
        free(enctypes);
     if (retval) {
@@ -318,8 +342,17 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
     /* make sure the response hasn't been tampered with..... */
     retval = 0;
 
-    if (!krb5_principal_compare(context, dec_rep->client, tkt->client))
-       retval = KRB5_KDCREP_MODIFIED;
+    if (s4u2self && !IS_TGS_PRINC(context, dec_rep->ticket->server)) {
+       /* Final hop, check whether KDC supports S4U2Self */
+       if (krb5_principal_compare(context, dec_rep->client, in_cred->server))
+           retval = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+    } else if ((kdcoptions & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) {
+       /* XXX for constrained delegation this check must be performed by caller
+        * as we don't have access to the key to decrypt the evidence ticket.
+        */
+       if (!krb5_principal_compare(context, dec_rep->client, tkt->client))
+           retval = KRB5_KDCREP_MODIFIED;
+    }
 
     if (retval == 0)
        retval = check_reply_server(context, kdcoptions, in_cred, dec_rep);
@@ -356,13 +389,26 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
        retval = KRB5_KDCREP_SKEW;
        goto error_3;
     }
+
+    if (out_padata != NULL) {
+       *out_padata = dec_rep->padata;
+       dec_rep->padata = NULL;
+    }
+    if (out_enc_padata != NULL) {
+       *out_enc_padata = dec_rep->enc_part2->enc_padata;
+       dec_rep->enc_part2->enc_padata = NULL;
+    }
     
     retval = krb5_kdcrep2creds(context, dec_rep, address, 
                               &in_cred->second_ticket,  out_cred);
 
 error_3:;
-    if (subkey != NULL)
-      krb5_free_keyblock(context, subkey);
+    if (subkey != NULL) {
+      if (retval == 0 && out_subkey != NULL)
+         *out_subkey = subkey;
+      else
+         krb5_free_keyblock(context, subkey);
+    }
     
     memset(dec_rep->enc_part2->session->contents, 0,
           dec_rep->enc_part2->session->length);
index c02ddedc6bc73f4aefb06a9f8d2724a0eb6bc948..dad3e1a9175de6c80cac15654da987a1327288ec 100644 (file)
@@ -46,7 +46,7 @@
 #include "k5-int.h"
 #include "int-proto.h"
 
-static krb5_error_code
+krb5_error_code
 krb5_get_credentials_core(krb5_context context, krb5_flags options,
                          krb5_creds *in_creds, krb5_creds *mcreds,
                          krb5_flags *fields)
@@ -87,11 +87,14 @@ krb5_get_credentials_core(krb5_context context, krb5_flags options,
        if (ret)
            return ret;
     }
-    if (options & KRB5_GC_USER_USER) {
+    if (options & (KRB5_GC_USER_USER | KRB5_GC_CONSTRAINED_DELEGATION)) {
        /* also match on identical 2nd tkt and tkt encrypted in a
           session key */
-       *fields |= KRB5_TC_MATCH_2ND_TKT|KRB5_TC_MATCH_IS_SKEY;
-       mcreds->is_skey = TRUE;
+       *fields |= KRB5_TC_MATCH_2ND_TKT;
+       if (options & KRB5_GC_USER_USER) {
+           *fields |= KRB5_TC_MATCH_IS_SKEY;
+           mcreds->is_skey = TRUE;
+       }
        mcreds->second_ticket = in_creds->second_ticket;
        if (!in_creds->second_ticket.length)
            return KRB5_NO_2ND_TKT;
@@ -113,25 +116,35 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
     int not_ktype;
     int kdcopt = 0;
 
-    retval = krb5_get_credentials_core(context, options,
-                                      in_creds,
-                                      &mcreds, &fields);
+    if ((options & KRB5_GC_CONSTRAINED_DELEGATION) == 0) {
+       retval = krb5_get_credentials_core(context, options,
+                                          in_creds,
+                                          &mcreds, &fields);
 
-    if (retval) return retval;
+       if (retval)
+           return retval;
 
-    if ((ncreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL)
-       return ENOMEM;
+       if ((ncreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL)
+           return ENOMEM;
 
-    memset(ncreds, 0, sizeof(krb5_creds));
-    ncreds->magic = KV5M_CREDS;
+       memset(ncreds, 0, sizeof(krb5_creds));
+       ncreds->magic = KV5M_CREDS;
 
-    /* The caller is now responsible for cleaning up in_creds */
-    if ((retval = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds,
-                                       ncreds))) {
-       free(ncreds);
-       ncreds = in_creds;
+       /* The caller is now responsible for cleaning up in_creds */
+       if ((retval = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds,
+                                           ncreds))) {
+           free(ncreds);
+           ncreds = in_creds;
+       } else {
+           *out_creds = ncreds;
+       }
     } else {
-       *out_creds = ncreds;
+       /*
+        * To do this usefully for constrained delegation, we would
+        * need to look inside second_ticket, which we can't do.
+        */
+       ncreds = in_creds;
+       retval = KRB5_CC_NOTFOUND;
     }
 
     if ((retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE)
@@ -145,6 +158,15 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
 
     if (options & KRB5_GC_CANONICALIZE)
        kdcopt |= KDC_OPT_CANONICALIZE;
+    if (options & KRB5_GC_FORWARDABLE)
+       kdcopt |= KDC_OPT_FORWARDABLE;
+    if (options & KRB5_GC_NO_TRANSIT_CHECK)
+       kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK;
+    if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
+       if (options & KRB5_GC_USER_USER)
+           return EINVAL;
+       kdcopt |= KDC_OPT_FORWARDABLE | KDC_OPT_CNAME_IN_ADDL_TKT;
+    }
 
     retval = krb5_get_cred_from_kdc_opt(context, ccache, ncreds,
                                        out_creds, &tgts, kdcopt);
@@ -160,6 +182,13 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
        }
        krb5_free_tgt_creds(context, tgts);
     }
+    if (!retval && (options & KRB5_GC_CONSTRAINED_DELEGATION)) {
+       if (((*out_creds)->ticket_flags & TKT_FLG_FORWARDABLE) == 0) {
+           retval = KRB5_TKT_NOT_FORWARDABLE;
+           krb5_free_creds(context, *out_creds);
+           *out_creds = NULL;
+       }
+    }
     /*
      * Translate KRB5_CC_NOTFOUND if we previously got
      * KRB5_CC_NOT_KTYPE from krb5_cc_retrieve_cred(), in order to
@@ -175,7 +204,7 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
        && not_ktype)
        retval = KRB5_CC_NOT_KTYPE;
 
-    if (!retval) {
+    if (!retval && (options & KRB5_GC_NO_STORE) == 0) {
         /* the purpose of the krb5_get_credentials call is to 
          * obtain a set of credentials for the caller.  the 
          * krb5_cc_store_cred() call is to optimize performance
@@ -184,6 +213,7 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
          */
        krb5_cc_store_cred(context, ccache, *out_creds);
     }
+
     return retval;
 }
 
@@ -337,3 +367,4 @@ krb5_get_renewed_creds(krb5_context context, krb5_creds *creds, krb5_principal c
     return(krb5_validate_or_renew_creds(context, creds, client, ccache,
                                        in_tkt_service, 0));
 }
+
index 018676dbee8d80f194015ac99f072e2951d6d3c6..63594ddfd5b4e8b39e8788addb8973499b87e8aa 100644 (file)
@@ -493,6 +493,55 @@ static const krb5_enctype get_in_tkt_enctypes[] = {
     0
 };
 
+static krb5_error_code
+rewrite_server_realm(krb5_context context,
+                    krb5_const_principal old_server,
+                    const krb5_data *realm,
+                    krb5_boolean tgs,
+                    krb5_principal *server)
+{
+    krb5_error_code retval;
+
+    assert(*server == NULL);
+
+    retval = krb5_copy_principal(context, old_server, server);
+    if (retval)
+       return retval;
+
+    krb5_free_data_contents(context, &(*server)->realm);
+    (*server)->realm.data = NULL;
+
+    retval = krb5int_copy_data_contents(context, realm, &(*server)->realm);
+    if (retval)
+       goto cleanup;
+
+    if (tgs) {
+       krb5_free_data_contents(context, &(*server)->data[1]);
+       (*server)->data[1].data = NULL;
+
+       retval = krb5int_copy_data_contents(context, realm, &(*server)->data[1]);
+       if (retval)
+           goto cleanup;
+    }
+
+cleanup:
+    if (retval) {
+       krb5_free_principal(context, *server);
+       *server = NULL;
+    }
+
+    return retval;
+}
+
+static inline int
+tgt_is_local_realm(krb5_creds *tgt)
+{
+    return (tgt->server->length == 2
+            && data_eq_string(tgt->server->data[0], KRB5_TGS_NAME)
+            && data_eq(tgt->server->data[1], tgt->client->realm)
+            && data_eq(tgt->server->realm, tgt->client->realm));
+}
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_in_tkt(krb5_context context,
                krb5_flags options,
@@ -521,6 +570,8 @@ krb5_get_in_tkt(krb5_context context,
     int             use_master = 0;
     int                        referral_count = 0;
     krb5_principal_data        referred_client;
+    krb5_principal     referred_server = NULL;
+    krb5_boolean       is_tgt_req;
 
 #if APPLE_PKINIT
     inTktDebug("krb5_get_in_tkt top\n");
@@ -616,6 +667,8 @@ krb5_get_in_tkt(krb5_context context,
            goto cleanup;
     }
            
+    is_tgt_req = tgt_is_local_realm(creds);
+
     while (1) {
        if (loopcount++ > MAX_IN_TKT_LOOPS) {
            retval = KRB5_GET_IN_TKT_LOOP;
@@ -687,6 +740,21 @@ krb5_get_in_tkt(krb5_context context,
                if (retval)
                    goto cleanup;
                request.client = &referred_client;
+
+               if (referred_server != NULL) {
+                   krb5_free_principal(context, referred_server);
+                   referred_server = NULL;
+               }
+
+               retval = rewrite_server_realm(context,
+                                             creds->server,
+                                             &referred_client.realm,
+                                             is_tgt_req,
+                                             &referred_server);
+               if (retval)
+                   goto cleanup;
+               request.server = referred_server;
+
                continue;
            } else {
                retval = (krb5_error_code) err_reply->error 
@@ -739,6 +807,8 @@ cleanup:
     }
     if (referred_client.realm.data)
        krb5_free_data_contents(context, &referred_client.realm);
+    if (referred_server)
+       krb5_free_principal(context, referred_server);
     return (retval);
 }
 
@@ -939,6 +1009,52 @@ sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
     return 0;
 }
 
+static krb5_error_code
+build_in_tkt_name(krb5_context context,
+                 char *in_tkt_service,
+                 krb5_const_principal client,
+                 krb5_principal *server)
+{
+    krb5_error_code ret;
+
+    *server = NULL;
+
+    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, server)))
+           return ret;
+
+       /* stuff the client realm into the server principal.
+          realloc if necessary */
+       if ((*server)->realm.length < client->realm.length) {
+           char *p = realloc((*server)->realm.data,
+                             client->realm.length);
+           if (p == NULL) {
+               krb5_free_principal(context, *server);
+               *server = NULL;
+               return ENOMEM;
+           }
+           (*server)->realm.data = p;
+       }
+
+       (*server)->realm.length = client->realm.length;
+       memcpy((*server)->realm.data, client->realm.data, client->realm.length);
+    } else {
+       ret = krb5_build_principal_ext(context, server,
+                                      client->realm.length,
+                                      client->realm.data,
+                                      KRB5_TGS_NAME_SIZE,
+                                      KRB5_TGS_NAME,
+                                      client->realm.length,
+                                      client->realm.data,
+                                      0);
+    }
+    return ret;
+}
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds(krb5_context context,
                    krb5_creds *creds,
@@ -1125,41 +1241,9 @@ krb5_get_init_creds(krb5_context context,
        client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
 
     /* service */
-    
-    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) {
-           char *p = realloc(request.server->realm.data,
-                             request.client->realm.length);
-           if (p == NULL) {
-               ret = ENOMEM;
-               goto cleanup;
-           }
-           request.server->realm.data = p;
-       }
-
-       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 = build_in_tkt_name(context, in_tkt_service,
+                                request.client, &request.server)))
+       goto cleanup;
 
     krb5_preauth_request_context_init(context);
 
@@ -1337,8 +1421,10 @@ krb5_get_init_creds(krb5_context context,
                }
                preauth_to_use = out_padata;
                out_padata = NULL;
-               krb5_free_error(context, err_reply);
-               err_reply = NULL;
+               if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED) {
+                   krb5_free_error(context, err_reply);
+                   err_reply = NULL;
+               }
                ret = sort_krb5_padata_sequence(context,
                                                &request.server->realm,
                                                preauth_to_use);
@@ -1365,6 +1451,14 @@ krb5_get_init_creds(krb5_context context,
                if (ret)
                    goto cleanup;
                request.client = &referred_client;
+
+               krb5_free_principal(context, request.server);
+               request.server = NULL;
+
+               ret = build_in_tkt_name(context, in_tkt_service,
+                                       request.client, &request.server);
+               if (ret)
+                   goto cleanup;
            } else {
                if (retry)  {
                    /* continue to next iteration */
index b81fe2566bc50154e6b2274cbf57a16e236fa4ca..cc0c9f2de03f04ac292be5d5d5a34c62a4378f4f 100644 (file)
@@ -59,11 +59,31 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
                           krb5_creds *in_cred, krb5_creds **out_cred,
                           krb5_creds ***tgts, int kdcopt);
 
+krb5_error_code
+krb5_get_credentials_core(krb5_context context, krb5_flags options,
+                         krb5_creds *in_creds, krb5_creds *mcreds,
+                         krb5_flags *fields);
+
 #define in_clock_skew(date, now) (labs((date)-(now)) < context->clockskew)
 
 #define IS_TGS_PRINC(c, p)                                             \
     (krb5_princ_size((c), (p)) == 2 &&                                 \
      data_eq_string(*krb5_princ_component((c), (p), 0), KRB5_TGS_NAME))
 
+krb5_error_code
+krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
+                          krb5_flags kdcoptions, krb5_address *const *address,
+                          krb5_pa_data **in_padata,
+                          krb5_creds *in_cred,
+                          krb5_error_code (*gcvt_fct)(krb5_context,
+                                                      krb5_keyblock *,
+                                                      krb5_kdc_req *,
+                                                      void *),
+                          void *gcvt_data,
+                          krb5_pa_data ***out_padata,
+                          krb5_pa_data ***enc_padata,
+                          krb5_creds **out_cred,
+                          krb5_keyblock **out_subkey);
+
 #endif /* KRB5_INT_FUNC_PROTO__ */
 
index bec9a61bfa894cb92bc1b4b38aebf3bf6235535f..8cef95431a734cb93ca234aa6c84dcb37d134c0d 100644 (file)
@@ -170,7 +170,7 @@ krb5_free_checksum_contents(krb5_context context, register krb5_checksum *val)
     if (val == NULL)
        return;
     free(val->contents);
-    val->contents = 0;
+    val->contents = NULL;
 }
 
 void KRB5_CALLCONV
@@ -297,6 +297,7 @@ krb5_free_enc_kdc_rep_part(krb5_context context, register krb5_enc_kdc_rep_part
     krb5_free_last_req(context, val->last_req);
     krb5_free_principal(context, val->server);
     krb5_free_addresses(context, val->caddrs);
+    krb5_free_pa_data(context, val->enc_padata);
     free(val);
 }
 
@@ -754,6 +755,30 @@ krb5_free_pa_for_user(krb5_context context, krb5_pa_for_user *req)
     free(req);
 }
 
+void KRB5_CALLCONV
+krb5_free_s4u_userid_contents(krb5_context context, krb5_s4u_userid *user_id)
+{
+    if (user_id == NULL)
+       return;
+    user_id->nonce = 0;
+    krb5_free_principal(context, user_id->user);
+    user_id->user = NULL;
+    krb5_free_data_contents(context, &user_id->subject_cert);
+    user_id->subject_cert.length = 0;
+    user_id->subject_cert.data = NULL;
+    user_id->options = 0;
+}
+
+void KRB5_CALLCONV
+krb5_free_pa_s4u_x509_user(krb5_context context, krb5_pa_s4u_x509_user *req)
+{
+    if (req == NULL)
+       return;
+    krb5_free_s4u_userid_contents(context, &req->user_id);
+    krb5_free_checksum_contents(context, &req->cksum);
+    free(req);
+}
+
 void KRB5_CALLCONV
 krb5_free_pa_server_referral_data(krb5_context context,
                                  krb5_pa_server_referral_data *ref)
index e6f4215d5c4d0502db51e6077591fc575eefbc3d..996cbfd364402977d81383b9e8683d32f61ae335 100644 (file)
@@ -1706,6 +1706,59 @@ krb5_error_code pa_sam_2(krb5_context context,
    return(0);
 }
 
+static krb5_error_code pa_s4u_x509_user(
+    krb5_context context,
+    krb5_kdc_req *request,
+    krb5_pa_data *in_padata,
+    krb5_pa_data **out_padata,
+    krb5_data *salt,
+    krb5_data *s2kparams,
+    krb5_enctype *etype,
+    krb5_keyblock *as_key,
+    krb5_prompter_fct prompter,
+    void *prompter_data,
+    krb5_gic_get_as_key_fct gak_fct,
+    void *gak_data)
+{
+    krb5_s4u_userid *userid = (krb5_s4u_userid *)gak_data; /* XXX private contract */
+    krb5_pa_data *s4u_padata;
+    krb5_error_code code;
+    krb5_principal client;
+
+    *out_padata = NULL;
+
+    if (userid == NULL)
+       return EINVAL;
+
+    code = krb5_copy_principal(context, request->client, &client);
+    if (code != 0)
+       return code;
+
+    if (userid->user != NULL)
+       krb5_free_principal(context, userid->user);
+    userid->user = client;
+
+    if (userid->subject_cert.length != 0) {
+       s4u_padata = malloc(sizeof(*s4u_padata));
+       if (s4u_padata == NULL)
+           return ENOMEM;
+
+       s4u_padata->magic = KV5M_PA_DATA;
+       s4u_padata->pa_type = KRB5_PADATA_S4U_X509_USER;
+       s4u_padata->contents = malloc(userid->subject_cert.length);
+       if (s4u_padata->contents == NULL) {
+           free(s4u_padata);
+           return ENOMEM;
+       }
+       memcpy(s4u_padata->contents, userid->subject_cert.data, userid->subject_cert.length);
+       s4u_padata->length = userid->subject_cert.length;
+
+       *out_padata = s4u_padata;
+    }
+
+    return 0;
+}
+
 /* FIXME - order significant? */
 static const pa_types_t pa_types[] = {
     {
@@ -1750,6 +1803,11 @@ static const pa_types_t pa_types[] = {
        pa_fx_cookie,
        PA_INFO,
     },
+    {
+       KRB5_PADATA_S4U_X509_USER,
+       pa_s4u_x509_user,
+       PA_INFO,
+    },
     {
        -1,
        NULL,
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
new file mode 100644 (file)
index 0000000..613bbef
--- /dev/null
@@ -0,0 +1,829 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * lib/krb5/krb/s4u_creds.c
+ *
+ * Copyright (C) 2009 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.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ *
+ *
+ *
+ */
+
+#include "k5-int.h"
+#include "int-proto.h"
+
+/* Convert ticket flags to necessary KDC options */
+#define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK)
+
+/*
+ * Implements S4U2Self, by which a service can request a ticket to
+ * itself on behalf of an arbitrary principal.
+ */
+
+static krb5_error_code
+krb5_get_as_key_noop(
+    krb5_context context,
+    krb5_principal client,
+    krb5_enctype etype,
+    krb5_prompter_fct prompter,
+    void *prompter_data,
+    krb5_data *salt,
+    krb5_data *params,
+    krb5_keyblock *as_key,
+    void *gak_data)
+{
+    /* force a hard error, we don't actually have the key */
+    return KDC_ERR_PREAUTH_FAILED;
+}
+
+static krb5_error_code
+s4u_identify_user(krb5_context context,
+                  krb5_creds *in_creds,
+                  krb5_data *subject_cert,
+                  krb5_principal *canon_user)
+{
+    krb5_error_code code;
+    krb5_preauthtype ptypes[1] = { KRB5_PADATA_S4U_X509_USER };
+    krb5_creds creds;
+    int use_master = 0;
+    krb5_get_init_creds_opt *opts = NULL;
+    krb5_gic_opt_ext *opte = NULL;
+    krb5_principal_data client_data;
+    krb5_principal client;
+    krb5_s4u_userid userid;
+
+    *canon_user = NULL;
+
+    if (in_creds->client == NULL && subject_cert == NULL) {
+        return EINVAL;
+    }
+
+    if (in_creds->client != NULL &&
+        krb5_princ_type(context, in_creds->client) !=
+            KRB5_NT_ENTERPRISE_PRINCIPAL)
+        /* we already know the realm of the user */
+        return krb5_copy_principal(context, in_creds->client, canon_user);
+
+    memset(&creds, 0, sizeof(creds));
+
+    memset(&userid, 0, sizeof(userid));
+    if (subject_cert != NULL)
+        userid.subject_cert = *subject_cert;
+
+    code = krb5_get_init_creds_opt_alloc(context, &opts);
+    if (code != 0)
+        goto cleanup;
+    krb5_get_init_creds_opt_set_tkt_life(opts, 15);
+    krb5_get_init_creds_opt_set_renew_life(opts, 0);
+    krb5_get_init_creds_opt_set_forwardable(opts, 0);
+    krb5_get_init_creds_opt_set_proxiable(opts, 0);
+    krb5_get_init_creds_opt_set_canonicalize(opts, 1);
+    krb5_get_init_creds_opt_set_preauth_list(opts, ptypes, 1);
+    code = krb5int_gic_opt_to_opte(context, opts, &opte,
+                                   0, "s4u_identify_user");
+    if (code != 0)
+        goto cleanup;
+
+    if (in_creds->client != NULL)
+        client = in_creds->client;
+    else {
+        client_data.magic = KV5M_PRINCIPAL;
+        client_data.realm = in_creds->server->realm;
+        /* should this be NULL, empty or a fixed string? XXX */
+        client_data.data = NULL;
+        client_data.length = 0;
+        client_data.type = KRB5_NT_ENTERPRISE_PRINCIPAL;
+        client = &client_data;
+    }
+
+    code = krb5_get_init_creds(context, &creds, in_creds->client,
+                               NULL, NULL, 0, NULL, opte,
+                               krb5_get_as_key_noop, &userid,
+                               &use_master, NULL);
+    if (code == 0 ||
+        code == KDC_ERR_PREAUTH_REQUIRED ||
+        code == KDC_ERR_PREAUTH_FAILED) {
+        *canon_user = userid.user;
+        userid.user = NULL;
+        code = 0;
+    }
+
+cleanup:
+    krb5_free_cred_contents(context, &creds);
+    if (opts != NULL)
+        krb5_get_init_creds_opt_free(context, opts);
+    if (userid.user != NULL)
+        krb5_free_principal(context, userid.user);
+
+    return code;
+}
+
+static krb5_error_code
+make_pa_for_user_checksum(krb5_context context,
+                          krb5_keyblock *key,
+                          krb5_pa_for_user *req,
+                          krb5_checksum *cksum)
+{
+    krb5_error_code code;
+    int i;
+    krb5_int32 name_type;
+    char *p;
+    krb5_data data;
+    krb5_cksumtype cksumtype;
+
+    data.length = 4;
+    for (i = 0; i < krb5_princ_size(context, req->user); i++) {
+        data.length += krb5_princ_component(context, req->user, i)->length;
+    }
+    data.length += krb5_princ_realm(context, req->user)->length;
+    data.length += req->auth_package.length;
+
+    p = data.data = malloc(data.length);
+    if (data.data == NULL)
+        return ENOMEM;
+
+    name_type = krb5_princ_type(context, req->user);
+    p[0] = (name_type >> 0 ) & 0xFF;
+    p[1] = (name_type >> 8 ) & 0xFF;
+    p[2] = (name_type >> 16) & 0xFF;
+    p[3] = (name_type >> 24) & 0xFF;
+    p += 4;
+
+    for (i = 0; i < krb5_princ_size(context, req->user); i++) {
+        memcpy(p, krb5_princ_component(context, req->user, i)->data,
+               krb5_princ_component(context, req->user, i)->length);
+        p += krb5_princ_component(context, req->user, i)->length;
+    }
+
+    memcpy(p, krb5_princ_realm(context, req->user)->data,
+           krb5_princ_realm(context, req->user)->length);
+    p += krb5_princ_realm(context, req->user)->length;
+
+    memcpy(p, req->auth_package.data, req->auth_package.length);
+
+    code = krb5int_c_mandatory_cksumtype(context, key->enctype, &cksumtype);
+    if (code != 0) {
+        free(data.data);
+        return code;
+    }
+
+    code = krb5_c_make_checksum(context, cksumtype, key,
+                                KRB5_KEYUSAGE_APP_DATA_CKSUM, &data,
+                                cksum);
+
+    free(data.data);
+
+    return code;
+}
+
+static krb5_error_code
+build_pa_for_user(krb5_context context,
+                  krb5_creds *tgt,
+                  krb5_s4u_userid *userid,
+                  krb5_pa_data **out_padata)
+{
+    krb5_error_code code;
+    krb5_pa_data *padata;
+    krb5_pa_for_user for_user;
+    krb5_data *for_user_data = NULL;
+    char package[] = "Kerberos";
+
+    if (userid->user == NULL) {
+        code = EINVAL;
+        goto cleanup;
+    }
+
+    memset(&for_user, 0, sizeof(for_user));
+    for_user.user = userid->user;
+    for_user.auth_package.data = package;
+    for_user.auth_package.length = sizeof(package) - 1;
+
+    code = make_pa_for_user_checksum(context, &tgt->keyblock,
+                                     &for_user, &for_user.cksum);
+    if (code != 0)
+        goto cleanup;
+
+    code = encode_krb5_pa_for_user(&for_user, &for_user_data);
+    if (code != 0)
+        goto cleanup;
+
+    padata = malloc(sizeof(*padata));
+    if (padata == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+
+    padata->magic = KV5M_PA_DATA;
+    padata->pa_type = KRB5_PADATA_FOR_USER;
+    padata->length = for_user_data->length;
+    padata->contents = (krb5_octet *)for_user_data->data;
+
+    free(for_user_data);
+    for_user_data = NULL;
+
+    *out_padata = padata;
+
+cleanup:
+    if (for_user.cksum.contents != NULL)
+        krb5_free_checksum_contents(context, &for_user.cksum);
+    krb5_free_data(context, for_user_data);
+
+    return code;
+}
+
+/*
+ * This function is invoked by krb5int_send_tgs() just before
+ * the request is encoded; it gives us access to the nonce and
+ * subkey without requiring them to be generated by the caller.
+ */
+static krb5_error_code
+build_pa_s4u_x509_user(krb5_context context,
+                       krb5_keyblock *subkey,
+                       krb5_kdc_req *tgsreq,
+                       void *gcvt_data)
+{
+    krb5_error_code code;
+    krb5_pa_s4u_x509_user *s4u_user = (krb5_pa_s4u_x509_user *)gcvt_data;
+    krb5_data *data = NULL;
+    krb5_pa_data **padata;
+    krb5_cksumtype cksumtype;
+    int i;
+
+    assert(s4u_user->cksum.contents == NULL);
+
+    s4u_user->user_id.nonce = tgsreq->nonce;
+
+    code = encode_krb5_s4u_userid(&s4u_user->user_id, &data);
+    if (code != 0)
+        goto cleanup;
+
+    /* [MS-SFU] 2.2.2: unusual to say the least, but enc_padata secures it */
+    if (subkey->enctype == ENCTYPE_ARCFOUR_HMAC ||
+        subkey->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+        cksumtype = CKSUMTYPE_RSA_MD4;
+    } else {
+        code = krb5int_c_mandatory_cksumtype(context, subkey->enctype,
+                                             &cksumtype);
+    }
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_c_make_checksum(context, cksumtype, subkey,
+                                KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST, data,
+                                &s4u_user->cksum);
+    if (code != 0)
+        goto cleanup;
+
+    krb5_free_data(context, data);
+    data = NULL;
+
+    code = encode_krb5_pa_s4u_x509_user(s4u_user, &data);
+    if (code != 0)
+        goto cleanup;
+
+    assert(tgsreq->padata != NULL);
+
+    for (i = 0; tgsreq->padata[i] != NULL; i++)
+        ;
+
+    padata = realloc(tgsreq->padata,
+                     (i + 2) * sizeof(krb5_pa_data *));
+    if (padata == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+    tgsreq->padata = padata;
+
+    padata[i] = malloc(sizeof(krb5_pa_data));
+    if (padata[i] == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+    padata[i]->magic = KV5M_PA_DATA;
+    padata[i]->pa_type = KRB5_PADATA_S4U_X509_USER;
+    padata[i]->length = data->length;
+    padata[i]->contents = (krb5_octet *)data->data;
+
+    padata[i + 1] = NULL;
+
+    free(data);
+    data = NULL;
+
+cleanup:
+    if (code != 0 && s4u_user->cksum.contents != NULL) {
+        krb5_free_checksum_contents(context, &s4u_user->cksum);
+        s4u_user->cksum.contents = NULL;
+    }
+    krb5_free_data(context, data);
+
+    return code;
+}
+
+static krb5_error_code
+verify_s4u2self_reply(krb5_context context,
+                      krb5_keyblock *subkey,
+                      krb5_pa_s4u_x509_user *req_s4u_user,
+                      krb5_pa_data **rep_padata,
+                      krb5_pa_data **enc_padata)
+{
+    krb5_error_code code;
+    krb5_pa_data *rep_s4u_padata, *enc_s4u_padata;
+    krb5_pa_s4u_x509_user *rep_s4u_user = NULL;
+    krb5_data data, *datap = NULL;
+    krb5_keyusage usage;
+    krb5_boolean valid;
+    krb5_boolean not_newer;
+
+    assert(req_s4u_user != NULL);
+
+    switch (subkey->enctype) {
+    case ENCTYPE_DES_CBC_CRC:
+    case ENCTYPE_DES_CBC_MD4:
+    case ENCTYPE_DES_CBC_MD5:
+    case ENCTYPE_DES3_CBC_SHA1:
+    case ENCTYPE_DES3_CBC_RAW:
+    case ENCTYPE_ARCFOUR_HMAC:
+    case ENCTYPE_ARCFOUR_HMAC_EXP :
+        not_newer = TRUE;
+        break;
+    default:
+        not_newer = FALSE;
+        break;
+    }
+
+    enc_s4u_padata = krb5int_find_pa_data(context,
+                                          enc_padata,
+                                          KRB5_PADATA_S4U_X509_USER);
+
+    /* XXX this will break newer enctypes with a MIT 1.7 KDC */
+    rep_s4u_padata = krb5int_find_pa_data(context,
+                                          rep_padata,
+                                          KRB5_PADATA_S4U_X509_USER);
+    if (rep_s4u_padata == NULL) {
+        if (not_newer == FALSE || enc_s4u_padata != NULL)
+            return KRB5_KDCREP_MODIFIED;
+        else
+            return 0;
+    }
+
+    data.length = rep_s4u_padata->length;
+    data.data = (char *)rep_s4u_padata->contents;
+
+    code = decode_krb5_pa_s4u_x509_user(&data, &rep_s4u_user);
+    if (code != 0)
+        goto cleanup;
+
+    if (rep_s4u_user->user_id.nonce != req_s4u_user->user_id.nonce) {
+        code = KRB5_KDCREP_MODIFIED;
+        goto cleanup;
+    }
+
+    code = encode_krb5_s4u_userid(&rep_s4u_user->user_id, &datap);
+    if (code != 0)
+        goto cleanup;
+
+    if (rep_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE)
+        usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY;
+    else
+        usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST;
+
+    code = krb5_c_verify_checksum(context, subkey, usage, datap,
+                                  &rep_s4u_user->cksum, &valid);
+    if (code != 0)
+        goto cleanup;
+    if (valid == FALSE) {
+        code = KRB5_KDCREP_MODIFIED;
+        goto cleanup;
+    }
+
+    /*
+     * KDCs that support KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE also return
+     * S4U enc_padata for older (pre-AES) encryption types only.
+     */
+    if (not_newer) {
+        if (enc_s4u_padata == NULL) {
+            if (rep_s4u_user->user_id.options &
+                    KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) {
+                code = KRB5_KDCREP_MODIFIED;
+                goto cleanup;
+            }
+        } else {
+            if (enc_s4u_padata->length !=
+                req_s4u_user->cksum.length + rep_s4u_user->cksum.length) {
+                code = KRB5_KDCREP_MODIFIED;
+                goto cleanup;
+            }
+            if (memcmp(enc_s4u_padata->contents,
+                       req_s4u_user->cksum.contents,
+                       req_s4u_user->cksum.length) ||
+                memcmp(&enc_s4u_padata->contents[req_s4u_user->cksum.length],
+                       rep_s4u_user->cksum.contents,
+                       rep_s4u_user->cksum.length)) {
+                code = KRB5_KDCREP_MODIFIED;
+                goto cleanup;
+            }
+        }
+    } else if (!krb5_c_is_keyed_cksum(rep_s4u_user->cksum.checksum_type)) {
+        code = KRB5KRB_AP_ERR_INAPP_CKSUM;
+        goto cleanup;
+    }
+
+cleanup:
+    krb5_free_pa_s4u_x509_user(context, rep_s4u_user);
+    krb5_free_data(context, datap);
+
+    return code;
+}
+
+static krb5_error_code
+krb5_get_self_cred_from_kdc(krb5_context context,
+                            krb5_flags options,
+                            krb5_ccache ccache,
+                            krb5_creds *in_creds,
+                            krb5_data *subject_cert,
+                            krb5_data *user_realm,
+                            krb5_creds **out_creds)
+{
+    krb5_error_code code;
+    krb5_principal tgs = NULL;
+    krb5_creds tgtq, s4u_creds, *tgt = NULL, *tgtptr;
+    krb5_creds *referral_tgts[KRB5_REFERRAL_MAXHOPS];
+    krb5_pa_s4u_x509_user s4u_user;
+    int referral_count = 0, i;
+    krb5_flags kdcopt;
+
+    memset(&tgtq, 0, sizeof(tgtq));
+    memset(&s4u_creds, 0, sizeof(s4u_creds));
+    memset(referral_tgts, 0, sizeof(referral_tgts));
+    *out_creds = NULL;
+
+    memset(&s4u_user, 0, sizeof(s4u_user));
+
+    if (in_creds->client != NULL &&
+        krb5_princ_size(context, in_creds->client)) {
+        if (krb5_princ_type(context, in_creds->client) ==
+            KRB5_NT_ENTERPRISE_PRINCIPAL)
+        {
+            code = krb5_build_principal_ext(context,
+                                            &s4u_user.user_id.user,
+                                            user_realm->length,
+                                            user_realm->data,
+                                            in_creds->client->data[0].length,
+                                            in_creds->client->data[0].data,
+                                            0);
+            if (code != 0)
+                goto cleanup;
+            s4u_user.user_id.user->type = KRB5_NT_ENTERPRISE_PRINCIPAL;
+        } else {
+            code = krb5_copy_principal(context,
+                                       in_creds->client,
+                                       &s4u_user.user_id.user);
+            if (code != 0)
+                goto cleanup;
+        }
+    } else {
+        code = krb5_build_principal_ext(context, &s4u_user.user_id.user,
+                                        user_realm->length,
+                                        user_realm->data);
+        if (code != 0)
+            goto cleanup;
+        s4u_user.user_id.user->type = KRB5_NT_ENTERPRISE_PRINCIPAL;
+    }
+    if (subject_cert != NULL)
+        s4u_user.user_id.subject_cert = *subject_cert;
+    s4u_user.user_id.options = KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;
+
+    /* First, acquire a TGT to the user's realm. */
+    code = krb5_tgtname(context, user_realm,
+                        krb5_princ_realm(context, in_creds->server), &tgs);
+    if (code != 0)
+        goto cleanup;
+
+    tgtq.client = in_creds->server;
+    tgtq.server = tgs;
+
+    code = krb5_get_credentials(context, options, ccache, &tgtq, &tgt);
+    if (code != 0)
+        goto cleanup;
+
+    tgtptr = tgt;
+
+    code = krb5int_copy_creds_contents(context, in_creds, &s4u_creds);
+    if (code != 0)
+        goto cleanup;
+
+    if (s4u_creds.client != NULL) {
+        krb5_free_principal(context, s4u_creds.client);
+        s4u_creds.client = NULL;
+    }
+
+    code = krb5_copy_principal(context, in_creds->server, &s4u_creds.client);
+    if (code != 0)
+        goto cleanup;
+
+    /* Then, walk back the referral path to S4U2Self for user */
+    kdcopt = 0;
+    if (options & KRB5_GC_CANONICALIZE)
+        kdcopt |= KDC_OPT_CANONICALIZE;
+    if (options & KRB5_GC_FORWARDABLE)
+        kdcopt |= KDC_OPT_FORWARDABLE;
+    if (options & KRB5_GC_NO_TRANSIT_CHECK)
+        kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK;
+
+    for (referral_count = 0;
+         referral_count < KRB5_REFERRAL_MAXHOPS;
+         referral_count++)
+    {
+        krb5_pa_data **in_padata = NULL;
+        krb5_pa_data **out_padata = NULL;
+        krb5_pa_data **enc_padata = NULL;
+        krb5_keyblock *subkey = NULL;
+
+        if (s4u_user.user_id.user != NULL &&
+            krb5_princ_size(context, s4u_user.user_id.user)) {
+            in_padata = calloc(2, sizeof(krb5_pa_data *));
+            if (in_padata == NULL) {
+                code = ENOMEM;
+                goto cleanup;
+            }
+            code = build_pa_for_user(context,
+                                     tgtptr,
+                                     &s4u_user.user_id, &in_padata[0]);
+            if (code != 0) {
+                krb5_free_pa_data(context, in_padata);
+                goto cleanup;
+            }
+        }
+
+        /* Rewrite server realm to match TGS realm */
+        krb5_free_data_contents(context, &s4u_creds.server->realm);
+
+        code = krb5int_copy_data_contents(context,
+                                          &tgtptr->server->data[1],
+                                          &s4u_creds.server->realm);
+        if (code != 0)
+            goto cleanup;
+
+        code = krb5_get_cred_via_tkt_ext(context, tgtptr,
+                                         KDC_OPT_CANONICALIZE |
+                                         FLAGS2OPTS(tgtptr->ticket_flags) |
+                                         kdcopt,
+                                         tgtptr->addresses,
+                                         in_padata, &s4u_creds,
+                                         build_pa_s4u_x509_user, &s4u_user,
+                                         &out_padata, &enc_padata,
+                                         out_creds, &subkey);
+        if (code != 0) {
+            krb5_free_checksum_contents(context, &s4u_user.cksum);
+            krb5_free_pa_data(context, in_padata);
+            goto cleanup;
+        }
+
+        code = verify_s4u2self_reply(context, subkey, &s4u_user,
+                                     out_padata, enc_padata);
+
+        krb5_free_checksum_contents(context, &s4u_user.cksum);
+        krb5_free_pa_data(context, in_padata);
+        krb5_free_pa_data(context, out_padata);
+        krb5_free_pa_data(context, enc_padata);
+        krb5_free_keyblock(context, subkey);
+
+        if (code != 0)
+            goto cleanup;
+
+        if (krb5_principal_compare(context,
+                                   in_creds->server,
+                                   (*out_creds)->server)) {
+            code = 0;
+            goto cleanup;
+        } else if (IS_TGS_PRINC(context, (*out_creds)->server)) {
+            krb5_data *r1 = &tgtptr->server->data[1];
+            krb5_data *r2 = &(*out_creds)->server->data[1];
+
+            if (data_eq(*r1, *r2)) {
+                krb5_free_creds(context, *out_creds);
+                *out_creds = NULL;
+                code = KRB5_ERR_HOST_REALM_UNKNOWN;
+                break;
+            }
+            for (i = 0; i < referral_count; i++) {
+                if (krb5_principal_compare(context,
+                                           (*out_creds)->server,
+                                           referral_tgts[i]->server)) {
+                    code = KRB5_KDC_UNREACH;
+                    goto cleanup;
+                }
+            }
+
+            tgtptr = *out_creds;
+            referral_tgts[referral_count] = *out_creds;
+            *out_creds = NULL;
+        } else {
+            krb5_free_creds(context, *out_creds);
+            *out_creds = NULL;
+            code = KRB5KRB_AP_WRONG_PRINC; /* XXX */
+            break;
+        }
+    }
+
+cleanup:
+    for (i = 0; i < KRB5_REFERRAL_MAXHOPS; i++) {
+        if (referral_tgts[i] != NULL)
+            krb5_free_creds(context, referral_tgts[i]);
+    }
+    krb5_free_principal(context, tgs);
+    krb5_free_creds(context, tgt);
+    krb5_free_cred_contents(context, &s4u_creds);
+    krb5_free_principal(context, s4u_user.user_id.user);
+    krb5_free_checksum_contents(context, &s4u_user.cksum);
+
+    return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
+                              krb5_ccache ccache, krb5_creds *in_creds,
+                              krb5_data *subject_cert,
+                              krb5_creds **out_creds)
+{
+    krb5_error_code code;
+    krb5_principal realm = NULL;
+
+    *out_creds = NULL;
+
+    if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
+        code = EINVAL;
+        goto cleanup;
+    }
+
+    if (in_creds->client != NULL) {
+        /* Uncanonicalised check */
+        code = krb5_get_credentials(context, options | KRB5_GC_CACHED,
+                                    ccache, in_creds, out_creds);
+        if (code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE)
+            goto cleanup;
+
+        if ((options & KRB5_GC_CACHED) && !(options & KRB5_GC_CANONICALIZE))
+            goto cleanup;
+    }
+
+    code = s4u_identify_user(context, in_creds, subject_cert, &realm);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_get_credentials(context, options | KRB5_GC_CACHED,
+                                ccache, in_creds, out_creds);
+    if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE)
+        || options & KRB5_GC_CACHED)
+        goto cleanup;
+
+    code = krb5_get_self_cred_from_kdc(context, options, ccache,
+                                       in_creds, subject_cert,
+                                       krb5_princ_realm(context, realm),
+                                       out_creds);
+    if (code != 0)
+        goto cleanup;
+
+    assert(*out_creds != NULL);
+
+    if ((options & KRB5_GC_NO_STORE) == 0) {
+        code = krb5_cc_store_cred(context, ccache, *out_creds);
+        if (code != 0)
+            goto cleanup;
+    }
+
+cleanup:
+    if (code != 0 && *out_creds != NULL) {
+        krb5_free_creds(context, *out_creds);
+        *out_creds = NULL;
+    }
+
+    krb5_free_principal(context, realm);
+
+    return code;
+}
+
+/*
+ * Exported API for constrained delegation (S4U2Proxy).
+ *
+ * This is preferable to using krb5_get_credentials directly because
+ * it can perform some additional checks.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_credentials_for_proxy(krb5_context context,
+                               krb5_flags options,
+                               krb5_ccache ccache,
+                               krb5_creds *in_creds,
+                               krb5_ticket *evidence_tkt,
+                               krb5_creds **out_creds)
+{
+    krb5_error_code code;
+    krb5_creds mcreds;
+    krb5_creds *ncreds = NULL;
+    krb5_flags fields;
+    krb5_data *evidence_tkt_data = NULL;
+    krb5_creds s4u_creds;
+
+    *out_creds = NULL;
+
+    if (in_creds == NULL || in_creds->client == NULL ||
+        evidence_tkt == NULL || evidence_tkt->enc_part2 == NULL) {
+        code = EINVAL;
+        goto cleanup;
+    }
+
+    /*
+     * Caller should have set in_creds->client to match evidence
+     * ticket client
+     */
+    if (!krb5_principal_compare(context, evidence_tkt->enc_part2->client,
+                                in_creds->client)) {
+        code = EINVAL;
+        goto cleanup;
+    }
+
+    if ((evidence_tkt->enc_part2->flags & TKT_FLG_FORWARDABLE) == 0) {
+        code = KRB5_TKT_NOT_FORWARDABLE;
+        goto cleanup;
+    }
+
+    code = krb5_get_credentials_core(context, options, in_creds,
+                                     &mcreds, &fields);
+    if (code != 0)
+        goto cleanup;
+
+    ncreds = calloc(1, sizeof(*ncreds));
+    if (ncreds == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+    ncreds->magic = KV5M_CRED;
+
+    code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds);
+    if (code != 0) {
+        free(ncreds);
+        ncreds = in_creds;
+    } else {
+        *out_creds = ncreds;
+    }
+
+    if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE)
+        || options & KRB5_GC_CACHED)
+        goto cleanup;
+
+    code = encode_krb5_ticket(evidence_tkt, &evidence_tkt_data);
+    if (code != 0)
+        goto cleanup;
+
+    s4u_creds = *in_creds;
+    s4u_creds.client = evidence_tkt->server;
+    s4u_creds.second_ticket = *evidence_tkt_data;
+
+    code = krb5_get_credentials(context,
+                                options | KRB5_GC_CONSTRAINED_DELEGATION,
+                                ccache,
+                                &s4u_creds,
+                                out_creds);
+    if (code != 0)
+        goto cleanup;
+
+    /*
+     * Check client name because we couldn't compare that inside
+     * krb5_get_credentials() (enc_part2 is unavailable in clear)
+     */
+    if (!krb5_principal_compare(context,
+                                evidence_tkt->enc_part2->client,
+                                (*out_creds)->client)) {
+        code = KRB5_KDCREP_MODIFIED;
+        goto cleanup;
+    }
+
+cleanup:
+    if (*out_creds != NULL && code != 0) {
+        krb5_free_creds(context, *out_creds);
+        *out_creds = NULL;
+    }
+    if (evidence_tkt_data != NULL)
+        krb5_free_data(context, evidence_tkt_data);
+
+    return code;
+}
index 97cd02bf75e22d1dce73a3264075d9e506462d74..eee47ed570a2f3786541b769aea9b4b677ff0a9c 100644 (file)
@@ -77,7 +77,7 @@ tgs_construct_tgsreq(krb5_context context, krb5_data *in_data,
        if (retval)
            goto cleanup;
     }
-    
+
     /* Generate checksum */
     if ((retval = krb5_c_make_checksum(context, cksumtype,
                        &in_cred->keyblock,
@@ -142,6 +142,9 @@ cleanup:
 }
 /*
  * Note that this function fills in part of rep even on failure.
+ *
+ * The pacb_fct callback allows the caller access to the nonce
+ * and request subkey, for binding preauthentication data
  */
 krb5_error_code
 krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
@@ -149,7 +152,13 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
           krb5_const_principal sname, krb5_address *const *addrs,
           krb5_authdata *const *authorization_data,
           krb5_pa_data *const *padata, const krb5_data *second_ticket,
-          krb5_creds *in_cred, krb5_response *rep, krb5_keyblock **subkey)
+          krb5_creds *in_cred,
+          krb5_error_code (*pacb_fct)(krb5_context,
+                                      krb5_keyblock *,
+                                      krb5_kdc_req *,
+                                      void *),
+          void *pacb_data,
+          krb5_response *rep, krb5_keyblock **subkey)
 {
     krb5_error_code retval;
     krb5_kdc_req tgsreq;
@@ -157,13 +166,14 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
     krb5_ticket *sec_ticket = 0;
     krb5_ticket *sec_ticket_arr[2];
     krb5_timestamp time_now;
-    krb5_pa_data **combined_padata;
+    krb5_pa_data **combined_padata = NULL;
     krb5_pa_data ap_req_padata;
     int tcp_only = 0, use_master;
     krb5_keyblock *local_subkey = NULL;
 
     assert (subkey != NULL);
     *subkey  = NULL;
+
     /* 
      * in_creds MUST be a valid credential NOT just a partially filled in
      * place holder for us to get credentials for the caller.
@@ -215,8 +225,8 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
 
     /* Get the encryption types list */
     if (ktypes) {
-    /* Check passed ktypes and make sure they're valid. */
-       for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) {
+        /* Check passed ktypes and make sure they're valid. */
+        for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) {
             if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes]))
                 return KRB5_PROG_ETYPE_NOSUPP;
         }
@@ -236,6 +246,8 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
     } else
         tgsreq.second_ticket = 0;
 
+    ap_req_padata.contents = NULL;
+
     /* encode the body; then checksum it */
     if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch)))
         goto send_tgs_error_2;
@@ -250,47 +262,74 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
     }
     krb5_free_data(context, scratch);
 
-    ap_req_padata.pa_type = KRB5_PADATA_AP_REQ;
-    ap_req_padata.length = scratch2.length;
-    ap_req_padata.contents = (krb5_octet *)scratch2.data;
-
-    /* combine in any other supplied padata */
+    tgsreq.padata = (krb5_pa_data **)calloc(2, sizeof(krb5_pa_data *));
+    if (tgsreq.padata == NULL) {
+        free(scratch2.data);
+        goto send_tgs_error_2;
+    }
+    tgsreq.padata[0] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
+    if (tgsreq.padata[0] == NULL) {
+        free(scratch2.data);
+        goto send_tgs_error_2;
+    }
+    tgsreq.padata[0]->pa_type = KRB5_PADATA_AP_REQ;
+    tgsreq.padata[0]->length = scratch2.length;
+    tgsreq.padata[0]->contents = (krb5_octet *)scratch2.data;
+    tgsreq.padata[1] = NULL;
+
+    /* combine in any other supplied padata, unfortunately now it is
+     * necessary to copy it as the callback function might modify the
+     * padata, and having a separate path for the non-callback case,
+     * or attempting to determine which elements were changed by the
+     * callback, would have complicated the code significantly.
+     */
     if (padata) {
-        krb5_pa_data * const * counter;
-        register unsigned int i = 0;
-        for (counter = padata; *counter; counter++, i++);
-        combined_padata = malloc((i+2) * sizeof(*combined_padata));
-        if (!combined_padata) {
-            free(ap_req_padata.contents);
-            retval = ENOMEM;
-            goto send_tgs_error_2;
-        }
-        combined_padata[0] = &ap_req_padata;
-        for (i = 1, counter = padata; *counter; counter++, i++)
-            combined_padata[i] = (krb5_pa_data *) *counter;
-        combined_padata[i] = 0;
-    } else {
-        combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata));
-        if (!combined_padata) {
-            free(ap_req_padata.contents);
-            retval = ENOMEM;
+        krb5_pa_data **tmp;
+        int i;
+
+        for (i = 0; padata[i]; i++)
+            ;
+
+        tmp = (krb5_pa_data **)realloc(tgsreq.padata,
+                                       (i + 2) * sizeof(*combined_padata));
+        if (tmp == NULL)
             goto send_tgs_error_2;
+
+        tgsreq.padata = tmp;
+
+        for (i = 0; padata[i]; i++) {
+            krb5_pa_data *pa;
+
+            pa = tgsreq.padata[1 + i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
+            if (tgsreq.padata == NULL) {
+                retval = ENOMEM;
+                goto send_tgs_error_2;
+            }
+
+            pa->pa_type = padata[i]->pa_type;
+            pa->length = padata[i]->length;
+            pa->contents = (krb5_octet *)malloc(padata[i]->length);
+            if (pa->contents == NULL) {
+                retval = ENOMEM;
+                goto send_tgs_error_2;
+            }
+            memcpy(pa->contents, padata[i]->contents, padata[i]->length);
         }
-        combined_padata[0] = &ap_req_padata;
-        combined_padata[1] = 0;
+        tgsreq.padata[1 + i] = NULL;
     }
-    tgsreq.padata = combined_padata;
 
+    if (pacb_fct != NULL) {
+        if ((retval = (*pacb_fct)(context, local_subkey, &tgsreq, pacb_data)))
+            goto send_tgs_error_2;
+    }
     /* the TGS_REQ is assembled in tgsreq, so encode it */
-    if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) {
-        free(ap_req_padata.contents);
-        free(combined_padata);
+    if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch)))
         goto send_tgs_error_2;
-    }
-    free(ap_req_padata.contents);
-    free(combined_padata);
 
     /* now send request & get response from KDC */
+    krb5_free_pa_data(context, tgsreq.padata);
+    tgsreq.padata = NULL;
+
 send_again:
     use_master = 0;
     retval = krb5_sendto_kdc(context, scratch, 
@@ -325,6 +364,8 @@ send_again:
     krb5_free_data(context, scratch);
     
 send_tgs_error_2:;
+    if (tgsreq.padata)
+        krb5_free_pa_data(context, tgsreq.padata);
     if (sec_ticket) 
         krb5_free_ticket(context, sec_ticket);
 
index b5cf260f2c0a203bafb004682ee695c06ba6da0a..0934e27e104e6ef65efef933beefc443809f06eb 100644 (file)
@@ -70,27 +70,70 @@ krb5int_server_decrypt_ticket_keyblock(krb5_context context,
 }
 
 
-krb5_error_code        KRB5_CALLCONV
+krb5_error_code KRB5_CALLCONV
 krb5_server_decrypt_ticket_keytab(krb5_context context,
-                                 const krb5_keytab kt,
+                                 const krb5_keytab keytab,
                                  krb5_ticket *ticket)
 {
-    krb5_error_code       retval;
-    krb5_enctype          enctype;
-    krb5_keytab_entry     ktent;
+    krb5_error_code      retval;
+    krb5_keytab_entry    ktent;
+
+    retval = KRB5_KT_NOTFOUND;
+
+    if (keytab->ops->start_seq_get == NULL) {
+       retval = krb5_kt_get_entry(context, keytab,
+                                  ticket->server,
+                                  ticket->enc_part.kvno,
+                                  ticket->enc_part.enctype, &ktent);
+       if (retval == 0) {
+           retval = krb5int_server_decrypt_ticket_keyblock(context, &ktent.key, ticket);
+
+           (void) krb5_free_keytab_entry_contents(context, &ktent);
+       }
+    } else {
+       krb5_error_code code;
+       krb5_kt_cursor cursor;
+
+       retval = krb5_kt_start_seq_get(context, keytab, &cursor);
+       if (retval != 0)
+           goto map_error;
 
-    enctype = ticket->enc_part.enctype;
+       while ((code = krb5_kt_next_entry(context, keytab,
+                                         &ktent, &cursor)) == 0) {
+           if (ktent.key.enctype != ticket->enc_part.enctype)
+               continue;
 
-    if ((retval = krb5_kt_get_entry(context, kt, ticket->server,
-                                    ticket->enc_part.kvno,
-                                    enctype, &ktent)))
-        return retval;
+           retval = krb5int_server_decrypt_ticket_keyblock(context, &ktent.key, ticket);
+           if (retval == 0) {
+               krb5_principal tmp;
 
-    retval = krb5int_server_decrypt_ticket_keyblock(context,
-                                                   &ktent.key, ticket);
-    /* Upon error, Free keytab entry first, then return */
+               retval = krb5_copy_principal(context, ktent.principal, &tmp);
+               if (retval == 0) {
+                   krb5_free_principal(context, ticket->server);
+                   ticket->server = tmp;
+               }
+               (void) krb5_free_keytab_entry_contents(context, &ktent);
+               break;
+           }
+           (void) krb5_free_keytab_entry_contents(context, &ktent);
+       }
+
+       code = krb5_kt_end_seq_get(context, keytab, &cursor);
+       if (code != 0)
+           retval = code;
+    }
+
+map_error:
+    switch (retval) {
+    case KRB5_KT_KVNONOTFOUND:
+    case KRB5_KT_NOTFOUND:
+    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+       retval = KRB5KRB_AP_WRONG_PRINC;
+       break;
+    default:
+       break;
+    }
 
-    (void) krb5_kt_free_entry(context, &ktent);
     return retval;
 }
 #endif /* LEAN_CLIENT */
index bd50fddb56b22990a0149f7a0c4e3e16ec56efca..b809e83cf751ece5c409fe85c6f7c6792cd160be 100644 (file)
@@ -20,11 +20,12 @@ decode_krb5_error
 decode_krb5_etype_info
 decode_krb5_etype_info2
 decode_krb5_fast_req
-decode_krb5_pa_fx_fast_request
 decode_krb5_kdc_req_body
 decode_krb5_pa_enc_ts
 decode_krb5_pa_for_user
+decode_krb5_pa_fx_fast_request
 decode_krb5_pa_pac_req
+decode_krb5_pa_s4u_x509_user
 decode_krb5_padata_sequence
 decode_krb5_predicted_sam_response
 decode_krb5_priv
@@ -60,10 +61,11 @@ encode_krb5_error
 encode_krb5_etype_info
 encode_krb5_etype_info2
 encode_krb5_fast_response
-encode_krb5_pa_fx_fast_reply
 encode_krb5_kdc_req_body
 encode_krb5_pa_enc_ts
 encode_krb5_pa_for_user
+encode_krb5_pa_fx_fast_reply
+encode_krb5_pa_s4u_x509_user
 encode_krb5_pa_server_referral_data
 encode_krb5_pa_svr_referral_data
 encode_krb5_padata_sequence
@@ -71,6 +73,7 @@ encode_krb5_predicted_sam_response
 encode_krb5_priv
 encode_krb5_pwd_data
 encode_krb5_pwd_sequence
+encode_krb5_s4u_userid
 encode_krb5_safe
 encode_krb5_sam_challenge
 encode_krb5_sam_key
@@ -134,9 +137,9 @@ krb5_auth_con_setsendsubkey
 krb5_auth_con_setuseruserkey
 krb5_auth_to_rep
 krb5_build_principal
+krb5_build_principal_alloc_va
 krb5_build_principal_ext
 krb5_build_principal_va
-krb5_build_principal_alloc_va
 krb5_cc_close
 krb5_cc_copy_creds
 krb5_cc_default
@@ -243,8 +246,9 @@ krb5_free_ktypes
 krb5_free_last_req
 krb5_free_pa_data
 krb5_free_pa_enc_ts
-krb5_free_pa_pac_req
 krb5_free_pa_for_user
+krb5_free_pa_pac_req
+krb5_free_pa_s4u_x509_user
 krb5_free_pa_server_referral_data
 krb5_free_pa_svr_referral_data
 krb5_free_passwd_phrase_element
@@ -284,6 +288,8 @@ krb5_get_cred_from_kdc_renew
 krb5_get_cred_from_kdc_validate
 krb5_get_cred_via_tkt
 krb5_get_credentials
+krb5_get_credentials_for_proxy
+krb5_get_credentials_for_user
 krb5_get_credentials_renew
 krb5_get_credentials_validate
 krb5_get_default_config_files
@@ -380,7 +386,6 @@ krb5_os_free_context
 krb5_os_hostaddr
 krb5_os_init_context
 krb5_os_localaddr
-krb5int_get_domain_realm_mapping
 krb5_overridekeyname
 krb5_pac_add_buffer
 krb5_pac_free
@@ -529,6 +534,7 @@ krb5int_find_authdata
 krb5int_find_pa_data
 krb5int_foreach_localaddr
 krb5int_free_addrlist
+krb5int_get_domain_realm_mapping
 krb5int_init_context_kdc
 krb5int_initialize_library
 krb5int_pac_sign
index dcf08d996be45d9c907542659976118b9b409c1b..3e5f9e2341d3442a339e02e595502f4844b77f5c 100644 (file)
@@ -57,7 +57,7 @@
 #define DEFAULT_UDP_PREF_LIMIT  1465
 #define HARD_UDP_LIMIT         32700 /* could probably do 64K-epsilon ? */
 
-#undef DEBUG
+#define DEBUG 1
 
 #ifdef DEBUG
 int krb5int_debug_sendto_kdc = 0;
index 43a7738d4129d350c356df9ebd1b1a5a428f7fd2..6fb2e2288f57496264efdad68ef691c784abd90a 100644 (file)
@@ -40,23 +40,31 @@ static void
 print_flags(unsigned int flags)
 {
     unsigned int i;
-    static char *prflags[] = { 
-       "DISALLOW_POSTDATED",   /* 0x00000001 */ 
-       "DISALLOW_FORWARDABLE", /* 0x00000002 */ 
-       "DISALLOW_TGT_BASED",   /* 0x00000004 */ 
-       "DISALLOW_RENEWABLE",   /* 0x00000008 */ 
-       "DISALLOW_PROXIABLE",   /* 0x00000010 */ 
-       "DISALLOW_DUP_SKEY",    /* 0x00000020 */ 
-       "DISALLOW_ALL_TIX",     /* 0x00000040 */ 
-       "REQUIRES_PRE_AUTH",    /* 0x00000080 */ 
-       "REQUIRES_HW_AUTH",     /* 0x00000100 */ 
-       "REQUIRES_PWCHANGE",    /* 0x00000200 */ 
-       "UNKNOWN_0x00000400",   /* 0x00000400 */ 
-       "UNKNOWN_0x00000800",   /* 0x00000800 */ 
-       "DISALLOW_SVR",         /* 0x00001000 */ 
-       "PWCHANGE_SERVICE",     /* 0x00002000 */ 
-       "SUPPORT_DESMD5",       /* 0x00004000 */ 
-       "NEW_PRINC",            /* 0x00008000 */ 
+    static char *prflags[] = {
+       "DISALLOW_POSTDATED",     /* 0x00000001 */
+       "DISALLOW_FORWARDABLE",   /* 0x00000002 */
+       "DISALLOW_TGT_BASED",     /* 0x00000004 */
+       "DISALLOW_RENEWABLE",     /* 0x00000008 */
+       "DISALLOW_PROXIABLE",     /* 0x00000010 */
+       "DISALLOW_DUP_SKEY",      /* 0x00000020 */
+       "DISALLOW_ALL_TIX",       /* 0x00000040 */
+       "REQUIRES_PRE_AUTH",      /* 0x00000080 */
+       "REQUIRES_HW_AUTH",       /* 0x00000100 */
+       "REQUIRES_PWCHANGE",      /* 0x00000200 */
+       "UNKNOWN_0x00000400",     /* 0x00000400 */
+       "UNKNOWN_0x00000800",     /* 0x00000800 */
+       "DISALLOW_SVR",           /* 0x00001000 */
+       "PWCHANGE_SERVICE",       /* 0x00002000 */
+       "SUPPORT_DESMD5",         /* 0x00004000 */
+       "NEW_PRINC",              /* 0x00008000 */
+       "UNKNOWN_0x00010000",     /* 0x00010000 */
+       "UNKNOWN_0x00020000",     /* 0x00020000 */
+       "UNKNOWN_0x00040000",     /* 0x00040000 */
+       "UNKNOWN_0x00080000",     /* 0x00080000 */
+       "OK_AS_DELEGATE",         /* 0x00100000 */
+       "OK_TO_AUTH_AS_DELEGATE", /* 0x00200000 */
+       "NO_AUTH_DATA_REQUIRED",  /* 0x00400000 */
+
     };
 
     for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
@@ -169,7 +177,7 @@ print_key(kdbe_key_t *k)
 
     for (i = 0; i < k->k_enctype.k_enctype_len; i++) {
        printf("\t\t\tenc type: 0x%x\n",
-              k->k_enctype.k_enctype_val[i]); 
+              k->k_enctype.k_enctype_val[i]);
     }
 
     str = k->k_contents.k_contents_val;
index 41045b5a811eeab71d997e65d96e8d80f65dd63a..be0a536e924637ae50831a4430a7346630795778 100644 (file)
@@ -658,7 +658,18 @@ main(int argc, char **argv)
                   krb5_free_enc_sam_response_enc_2);
         ktest_empty_enc_sam_response_enc_2(&sam_ch2);
     }
+    /****************************************************************/
+    /* encode_krb5_pa_s4u_x509_user */
+    {
+        krb5_pa_s4u_x509_user s4u, *tmp;
 
+        setup(s4u, "pa_s4u_x509_user",
+              ktest_make_sample_pa_s4u_x509_user);
+        leak_test(s4u, encode_krb5_pa_s4u_x509_user,
+                  decode_krb5_pa_s4u_x509_user,
+                  krb5_free_pa_s4u_x509_user);
+        ktest_empty_pa_s4u_x509_user(&s4u);
+    }
     krb5_free_context(test_context);
     return 0;
 }
index 7136669acc82f996a7537e043334572afd9f17cc..2d2000422030477a2bcb755c99894fc704a2f767 100644 (file)
@@ -890,7 +890,13 @@ int main(argc, argv)
 
        ktest_empty_sam_response(&ref);
     }
-  
+
+    {
+       setup(krb5_pa_s4u_x509_user,"krb5_pa_s4u_x509_user",ktest_make_sample_pa_s4u_x509_user);
+       decode_run("pa_s4u_x509_user","","30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 12 04 10 70 61 5F 73 34 75 5F 78 35 30 39 5F 75 73 65 72 A4 07 03 05 00 80 00 00 00 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34",decode_krb5_pa_s4u_x509_user,ktest_equal_pa_s4u_x509_user,krb5_free_pa_s4u_x509_user);
+       ktest_empty_pa_s4u_x509_user(&ref);
+    }
+
 #ifdef ENABLE_LDAP
     /* ldap sequence_of_keys */
     {
index 2da5c1e7f707e51f3219a6d0d533392c0f9744f2..7ae32ec757d8cc34bbab96ab1463360d48e8b661 100644 (file)
@@ -695,6 +695,18 @@ main(argc, argv)
                   acc.encode_krb5_enc_sam_response_enc_2);
        ktest_empty_enc_sam_response_enc_2(&sam_ch2);
     }
+    /****************************************************************/
+    /* encode_krb5_pa_s4u_x509_user */
+    {
+       krb5_pa_s4u_x509_user s4u;
+       setup(s4u,krb5_pa_s4u_x509_user,"pa_s4u_x509_user",
+             ktest_make_sample_pa_s4u_x509_user);
+       encode_run(s4u,krb5_pa_s4u_x509_user,
+                  "pa_s4u_x509_user","",
+                  encode_krb5_pa_s4u_x509_user);
+       ktest_empty_pa_s4u_x509_user(&s4u);
+    }
+
 #ifdef ENABLE_LDAP
     {
        ldap_seqof_key_data skd;
index 5951b6c7eb99e1cd5f44595de4cb70634b7b130c..8b6367918c681783977e24bd052ddeb394d40f29 100644 (file)
@@ -825,6 +825,23 @@ krb5_error_code ktest_make_sample_enc_sam_response_enc_2(p)
     return 0;
 }
 
+krb5_error_code ktest_make_sample_pa_s4u_x509_user(p)
+    krb5_pa_s4u_x509_user *p;
+{
+    krb5_error_code retval;
+    krb5_s4u_userid *u = &p->user_id;
+    u->nonce = 13243546;
+    retval = ktest_make_sample_principal(&u->user);
+    if (retval) return retval;
+    u->subject_cert.data = strdup("pa_s4u_x509_user");
+    if (u->subject_cert.data == NULL) return ENOMEM;
+    u->subject_cert.length = strlen(u->subject_cert.data);
+    u->options = 0x80000000;
+    retval = ktest_make_sample_checksum(&p->cksum);
+    if (retval) return retval;
+    return 0;
+}
+
 #ifdef ENABLE_LDAP
 static krb5_error_code ktest_make_sample_key_data(krb5_key_data *p, int i)
 {
@@ -1420,6 +1437,14 @@ void ktest_empty_enc_sam_response_enc_2(p)
   ktest_empty_data(&p->sam_sad);
 }
 
+void ktest_empty_pa_s4u_x509_user(p)
+    krb5_pa_s4u_x509_user *p;
+{
+    ktest_destroy_principal(&p->user_id.user);
+    ktest_empty_data(&p->user_id.subject_cert);
+    if (p->cksum.contents) free(p->cksum.contents);
+}
+
 #ifdef ENABLE_LDAP
 void ktest_empty_ldap_seqof_key_data(ctx, p)
     krb5_context ctx;
index af7c9acc8c366afb9b89303242861ba0c64666ed..a2951d26f5d653616d7ea18aa79dccbccd06d35f 100644 (file)
@@ -105,7 +105,7 @@ krb5_error_code ktest_make_sample_enc_sam_response_enc
        (krb5_enc_sam_response_enc *p);
 krb5_error_code ktest_make_sample_predicted_sam_response(krb5_predicted_sam_response *p);
 krb5_error_code ktest_make_sample_enc_sam_response_enc_2(krb5_enc_sam_response_enc_2 *p);
-
+krb5_error_code ktest_make_sample_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
 
 #ifdef ENABLE_LDAP
 krb5_error_code ktest_make_sample_ldap_seqof_key_data(ldap_seqof_key_data * p);
@@ -214,6 +214,7 @@ void ktest_empty_enc_sam_response_enc(krb5_enc_sam_response_enc *p);
 void ktest_empty_predicted_sam_response(krb5_predicted_sam_response *p);
 void ktest_empty_sam_response_2(krb5_sam_response_2 *p);
 void ktest_empty_enc_sam_response_enc_2(krb5_enc_sam_response_enc_2 *p);
+void ktest_empty_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
 
 #ifdef ENABLE_LDAP
 void ktest_empty_ldap_seqof_key_data(krb5_context, ldap_seqof_key_data *p);
index 5ec0a01dc64fde38c362e5231615446481c5aaa1..da0324973006696dffb498e2765d710d44a38865 100644 (file)
@@ -542,6 +542,20 @@ int ktest_equal_sam_response(ref, var)
     return p;
 }
 
+int ktest_equal_pa_s4u_x509_user(ref, var)
+    krb5_pa_s4u_x509_user *ref;
+    krb5_pa_s4u_x509_user *var;
+{
+    int p = TRUE;
+    if (ref == var) return TRUE;
+    else if (ref == NULL || var == NULL) return FALSE;
+    p=p&&scalar_equal(user_id.nonce);
+    p=p&&ptr_equal(user_id.user,ktest_equal_principal_data);
+    p=p&&struct_equal(user_id.subject_cert,ktest_equal_data);
+    p=p&&scalar_equal(user_id.options);
+    p=p&&struct_equal(cksum,ktest_equal_checksum);
+    return p;
+}
 #ifdef ENABLE_LDAP
 static int equal_key_data(ref, var)
     krb5_key_data *ref;
index 217272378a2c5963defbfe8c89a592274cd22ffd..8a0641de5286c0a39be76fc0954f89574f51b90e 100644 (file)
@@ -91,6 +91,10 @@ int ktest_equal_krb5_etype_info_entry
     (krb5_etype_info_entry * ref,
                    krb5_etype_info_entry * var);
 
+int ktest_equal_pa_s4u_x509_user
+    (krb5_pa_s4u_x509_user *ref,
+                   krb5_pa_s4u_x509_user *var);
+
 int ktest_equal_ldap_sequence_of_keys(ldap_seqof_key_data *ref,
                                      ldap_seqof_key_data *var);
 #endif
index b6ac7fb2de20a27dc016a65dddd81cfbdc6992b1..0d913cdb24d6344f589d43e39aca199d4c864bf9 100644 (file)
@@ -56,3 +56,4 @@ encode_krb5_enc_sam_response_enc: 30 38 A0 05 02 03 01 33 2A A1 11 18 0F 31 39 3
 encode_krb5_predicted_sam_response: 30 6D A0 13 30 11 A0 03 02 01 01 A1 0A 04 08 31 32 33 34 35 36 37 38 A1 07 03 05 00 00 00 00 09 A2 11 18 0F 31 39 37 30 30 31 30 31 30 30 30 30 31 37 5A A3 03 02 01 12 A4 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A5 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A6 07 04 05 68 65 6C 6C 6F
 encode_krb5_sam_response_2: 30 42 A0 03 02 01 2B A1 07 03 05 00 80 00 00 00 A2 0C 04 0A 74 72 61 63 6B 20 64 61 74 61 A3 1D 30 1B A0 03 02 01 01 A1 04 02 02 0D 36 A2 0E 04 0C 6E 6F 6E 63 65 20 6F 72 20 73 61 64 A4 05 02 03 54 32 10
 encode_krb5_enc_sam_response_enc_2: 30 1F A0 03 02 01 58 A1 18 04 16 65 6E 63 5F 73 61 6D 5F 72 65 73 70 6F 6E 73 65 5F 65 6E 63 5F 32
+encode_krb5_pa_s4u_x509_user: 30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 12 04 10 70 61 5F 73 34 75 5F 78 35 30 39 5F 75 73 65 72 A4 07 03 05 00 80 00 00 00 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
index 9c5f8cc1e0cb37a485ea64686066fa7dbb56a2be..c8aa48e3f91c3afdf1329bd1d3dd736dee3f2313 100644 (file)
@@ -1246,3 +1246,20 @@ encode_krb5_enc_sam_response_enc_2:
 .  [0] [Integer] 88
 .  [1] [Octet String] "enc_sam_response_enc_2"
 
+encode_krb5_pa_s4u_x509_user:
+
+[Sequence/Sequence Of] 
+.  [0] [Sequence/Sequence Of] 
+.  .  [0] [Integer] 13243546
+.  .  [1] [Sequence/Sequence Of] 
+.  .  .  [0] [Integer] 1
+.  .  .  [1] [Sequence/Sequence Of] 
+.  .  .  .  [General string] "hftsai"
+.  .  .  .  [General string] "extra"
+.  .  [2] [General string] "ATHENA.MIT.EDU"
+.  .  [3] [Octet String] "pa_s4u_x509_user"
+.  .  [4] [Bit String] 0x80000000
+.  [1] [Sequence/Sequence Of] 
+.  .  [0] [Integer] 1
+.  .  [1] [Octet String] "1234"
+
index d0ea1e137ebf2bcefeb2cb43247a26d0aef491ce..e385c68f6c4dc8281d75ed9d45a07e5a86e574bd 100644 (file)
@@ -6,15 +6,18 @@ DEFINES = -DUSE_AUTOCONF_H
 PROG_LIBPATH=-L$(TOPLIBD)
 PROG_RPATH=$(KRB5_LIBDIR)
 
-SRCS= $(srcdir)/t_imp_name.c
+SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c
 
-OBJS= t_imp_name.o
+OBJS= t_imp_name.o t_s4u.o
 
-all:: t_imp_name
+all:: t_imp_name t_s4u
 
 t_imp_name: t_imp_name.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o t_imp_name t_imp_name.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
 
+t_s4u: t_s4u.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+       $(CC_LINK) -o t_s4u t_s4u.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
+
 clean::
-       $(RM) t_imp_name
+       $(RM) t_imp_name t_s4u
 
diff --git a/src/tests/gssapi/t_s4u.c b/src/tests/gssapi/t_s4u.c
new file mode 100644 (file)
index 0000000..264e60a
--- /dev/null
@@ -0,0 +1,418 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009  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.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gssapi/gssapi_krb5.h>
+
+/*
+ * Test program for protocol transition (S4U2Self) and constrained delegation
+ * (S4U2Proxy)
+ *
+ * Note: because of name canonicalization, the following tips may help
+ * when configuring with Active Directory:
+ *
+ * - Create a computer account FOO$
+ * - Set the UPN to host/foo.domain (no suffix); this is necessary to
+ *   be able to send an AS-REQ as this principal, otherwise you would
+ *   need to use the canonical name (FOO$), which will cause principal
+ *   comparison errors in gss_accept_sec_context().
+ * - Add a SPN of host/foo.domain
+ * - Configure the computer account to support constrained delegation with
+ *   protocol transition (Trust this computer for delegation to specified
+ *   services only / Use any authentication protocol)
+ * - Add host/foo.domain to the keytab (possibly easiest to do this
+ *   with ktadd)
+ *
+ * For S4U2Proxy to work the TGT must be forwardable too.
+ *
+ * Usage eg:
+ *
+ * kinit -k -t test.keytab -f 'host/test.win.mit.edu@WIN.MIT.EDU'
+ * ./t_s4u delegtest@WIN.MIT.EDU HOST/WIN-EQ7E4AA2WR8.win.mit.edu@WIN.MIT.EDU test.keytab
+ */
+
+static gss_OID_desc spnego_mech = { 6, "\053\006\001\005\005\002" };
+
+int use_spnego = 0;
+
+static void displayStatus_1(m, code, type)
+     char *m;
+     OM_uint32 code;
+     int type;
+{
+     OM_uint32 maj_stat, min_stat;
+     gss_buffer_desc msg;
+     OM_uint32 msg_ctx;
+
+     msg_ctx = 0;
+     while (1) {
+          maj_stat = gss_display_status(&min_stat, code,
+                                       type, GSS_C_NULL_OID,
+                                       &msg_ctx, &msg);
+          fprintf(stderr, "%s: %s\n", m, (char *)msg.value);
+          (void) gss_release_buffer(&min_stat, &msg);
+
+          if (!msg_ctx)
+               break;
+     }
+}
+
+static void displayStatus(msg, maj_stat, min_stat)
+     char *msg;
+     OM_uint32 maj_stat;
+     OM_uint32 min_stat;
+{
+     displayStatus_1(msg, maj_stat, GSS_C_GSS_CODE);
+     displayStatus_1(msg, min_stat, GSS_C_MECH_CODE);
+}
+
+static OM_uint32
+displayCanonName(OM_uint32 *minor, gss_name_t name, char *tag)
+{
+    gss_name_t canon;
+    OM_uint32 major, tmp_minor;
+    gss_buffer_desc buf;
+
+    major = gss_canonicalize_name(minor, name,
+                                  (gss_OID)gss_mech_krb5, &canon);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_canonicalize_name", major, *minor);
+        return major;
+    }
+
+    major = gss_display_name(minor, canon, &buf, NULL);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_display_name", major, *minor);
+        gss_release_name(&tmp_minor, &canon);
+        return major;
+    }
+
+    printf("%s:\t%s\n", tag, (char *)buf.value);
+
+    gss_release_buffer(&tmp_minor, &buf);
+    gss_release_name(&tmp_minor, &canon);
+
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+displayOID(OM_uint32 *minor, gss_OID oid, char *tag)
+{
+    OM_uint32 major, tmp_minor;
+    gss_buffer_desc buf;
+
+    major = gss_oid_to_str(minor, oid, &buf);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_oid_to_str", major, *minor);
+        return major;
+    }
+
+    printf("%s:\t%s\n", tag, (char *)buf.value);
+
+    gss_release_buffer(&tmp_minor, &buf);
+
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+initAcceptSecContext(OM_uint32 *minor,
+                     gss_cred_id_t claimant_cred_handle,
+                     gss_cred_id_t verifier_cred_handle,
+                     gss_cred_id_t *deleg_cred_handle)
+{
+    OM_uint32 major, tmp_minor;
+    gss_buffer_desc token, tmp;
+    gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
+    gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT;
+    gss_name_t source_name = GSS_C_NO_NAME;
+    gss_name_t target_name = GSS_C_NO_NAME;
+    OM_uint32 time_rec;
+    gss_OID mech = GSS_C_NO_OID;
+
+    token.value = NULL;
+    token.length = 0;
+
+    tmp.value = NULL;
+    tmp.length = 0;
+
+    *deleg_cred_handle = GSS_C_NO_CREDENTIAL;
+
+    major = gss_inquire_cred(minor, verifier_cred_handle,
+                             &target_name, NULL, NULL, NULL);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_inquire_cred", major, *minor);
+        return major;
+    }
+
+    displayCanonName(minor, target_name, "Target name");
+
+    mech = use_spnego ? (gss_OID)&spnego_mech : (gss_OID)gss_mech_krb5;
+    displayOID(minor, mech, "Target mech");
+
+    major = gss_init_sec_context(minor,
+                                 claimant_cred_handle,
+                                 &initiator_context,
+                                 target_name,
+                                 mech,
+                                 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+                                 GSS_C_INDEFINITE,
+                                 GSS_C_NO_CHANNEL_BINDINGS,
+                                 GSS_C_NO_BUFFER,
+                                 NULL,
+                                 &token,
+                                 NULL,
+                                 &time_rec);
+
+    if (target_name != GSS_C_NO_NAME)
+        (void) gss_release_name(&tmp_minor, &target_name);
+
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_init_sec_context", major, *minor);
+        return major;
+    }
+
+    (void) gss_delete_sec_context(minor, &initiator_context, NULL);
+    mech = GSS_C_NO_OID;
+
+    major = gss_accept_sec_context(minor,
+                                   &acceptor_context,
+                                   verifier_cred_handle,
+                                   &token,
+                                   GSS_C_NO_CHANNEL_BINDINGS,
+                                   &source_name,
+                                   &mech,
+                                   &tmp,
+                                   NULL,
+                                   &time_rec,
+                                   deleg_cred_handle);
+
+    if (GSS_ERROR(major))
+        displayStatus("gss_accept_sec_context", major, *minor);
+    else {
+        displayCanonName(minor, source_name, "Source name");
+        displayOID(minor, mech, "Source mech");
+    }
+
+    (void) gss_release_name(&tmp_minor, &source_name);
+    (void) gss_delete_sec_context(&tmp_minor, &acceptor_context, NULL);
+    (void) gss_release_buffer(&tmp_minor, &token);
+    (void) gss_release_buffer(&tmp_minor, &tmp);
+    (void) gss_release_oid(&tmp_minor, &mech);
+
+    return major;
+}
+
+static OM_uint32
+constrainedDelegate(OM_uint32 *minor,
+                    gss_OID_set desired_mechs,
+                    gss_name_t target,
+                    gss_cred_id_t delegated_cred_handle,
+                    gss_cred_id_t verifier_cred_handle)
+{
+    OM_uint32 major, tmp_minor;
+    gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
+    gss_name_t cred_name = GSS_C_NO_NAME;
+    OM_uint32 time_rec, lifetime;
+    gss_cred_usage_t usage;
+    gss_buffer_desc token;
+    gss_OID_set mechs;
+
+    printf("Constrained delegation tests follow\n");
+    printf("-----------------------------------\n\n");
+
+    if (gss_inquire_cred(minor, verifier_cred_handle, &cred_name,
+                         &lifetime, &usage, NULL) == GSS_S_COMPLETE) {
+        displayCanonName(minor, cred_name, "Proxy name");
+        gss_release_name(&tmp_minor, &cred_name);
+    }
+    displayCanonName(minor, target, "Target name");
+    if (gss_inquire_cred(minor, delegated_cred_handle, &cred_name,
+                         &lifetime, &usage, &mechs) == GSS_S_COMPLETE) {
+        displayCanonName(minor, cred_name, "Delegated name");
+        displayOID(minor, &mechs->elements[0], "Delegated mech");
+        gss_release_name(&tmp_minor, &cred_name);
+    }
+
+    printf("\n");
+
+    major = gss_init_sec_context(minor,
+                                 delegated_cred_handle,
+                                 &initiator_context,
+                                 target,
+                                 mechs ? &mechs->elements[0] :
+                                         (gss_OID)gss_mech_krb5,
+                                 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+                                 GSS_C_INDEFINITE,
+                                 GSS_C_NO_CHANNEL_BINDINGS,
+                                 GSS_C_NO_BUFFER,
+                                 NULL,
+                                 &token,
+                                 NULL,
+                                 &time_rec);
+    if (GSS_ERROR(major))
+        displayStatus("gss_init_sec_context", major, *minor);
+
+    (void) gss_release_buffer(&tmp_minor, &token);
+    (void) gss_delete_sec_context(&tmp_minor, &initiator_context, NULL);
+    (void) gss_release_oid_set(&tmp_minor, &mechs);
+
+    return major;
+}
+
+int main(int argc, char *argv[])
+{
+    OM_uint32 minor, major;
+    gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL;
+    gss_cred_id_t user_cred_handle = GSS_C_NO_CREDENTIAL;
+    gss_cred_id_t delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+    gss_name_t user = GSS_C_NO_NAME, target = GSS_C_NO_NAME;
+    gss_OID_set_desc mechs;
+    gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
+    gss_buffer_desc buf;
+
+    if (argc < 2 || argc > 5) {
+        fprintf(stderr, "Usage: %s [--spnego] [user] "
+                        "[proxy-target] [keytab]\n", argv[0]);
+        fprintf(stderr, "       proxy-target and keytab are optional\n");
+        exit(1);
+    }
+
+    if (strcmp(argv[1], "--spnego") == 0) {
+        use_spnego++;
+        argc--;
+        argv++;
+    }
+
+    buf.value = argv[1];
+    buf.length = strlen((char *)buf.value);
+
+    major = gss_import_name(&minor, &buf,
+                            (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME,
+                            &user);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_import_name(user)", major, minor);
+        goto out;
+    }
+
+    if (argc > 2 && strcmp(argv[2], "-")) {
+        buf.value = argv[2];
+        buf.length = strlen((char *)buf.value);
+
+        major = gss_import_name(&minor, &buf,
+                                (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME,
+                                &target);
+        if (GSS_ERROR(major)) {
+            displayStatus("gss_import_name(target)", major, minor);
+            goto out;
+        }
+    } else {
+        target = GSS_C_NO_NAME;
+    }
+
+    if (argc > 3) {
+        major = krb5_gss_register_acceptor_identity(argv[3]);
+        if (GSS_ERROR(major)) {
+            displayStatus("krb5_gss_register_acceptor_identity",
+                          major, minor);
+            goto out;
+        }
+    }
+
+    mechs.elements = use_spnego ? (gss_OID)&spnego_mech :
+                                  (gss_OID)gss_mech_krb5;
+    mechs.count = 1;
+
+    /* get default cred */
+    major = gss_acquire_cred(&minor,
+                             GSS_C_NO_NAME,
+                             GSS_C_INDEFINITE,
+                             &mechs,
+                             GSS_C_BOTH,
+                             &impersonator_cred_handle,
+                             &actual_mechs,
+                             NULL);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_acquire_cred", major, minor);
+        goto out;
+    }
+
+    (void) gss_release_oid_set(&minor, &actual_mechs);
+
+    printf("Protocol transition tests follow\n");
+    printf("-----------------------------------\n\n");
+
+    /* get S4U2Self cred */
+    major = gss_acquire_cred_impersonate_name(&minor,
+                                              impersonator_cred_handle,
+                                              user,
+                                              GSS_C_INDEFINITE,
+                                              &mechs,
+                                              GSS_C_INITIATE,
+                                              &user_cred_handle,
+                                              &actual_mechs,
+                                              NULL);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_acquire_cred_impersonate_name", major, minor);
+        goto out;
+    }
+
+    major = initAcceptSecContext(&minor,
+                                 user_cred_handle,
+                                 impersonator_cred_handle,
+                                 &delegated_cred_handle);
+    if (GSS_ERROR(major))
+        goto out;
+
+    printf("\n");
+
+    if (target != GSS_C_NO_NAME &&
+        delegated_cred_handle != GSS_C_NO_CREDENTIAL) {
+        major = constrainedDelegate(&minor, &mechs, target,
+                                    delegated_cred_handle,
+                                    impersonator_cred_handle);
+    } else if (target != GSS_C_NO_NAME) {
+        fprintf(stderr, "Warning: no delegated credentials handle returned\n\n");
+        fprintf(stderr, "Verify:\n\n");
+        fprintf(stderr, " - The TGT for the impersonating service is forwardable\n");
+        fprintf(stderr, " - The T2A4D flag set on the impersonating service's UAC\n");
+        fprintf(stderr, " - The user is not marked sensitive and cannot be delegated\n");
+        fprintf(stderr, "\n");
+    }
+
+out:
+    (void) gss_release_name(&minor, &user);
+    (void) gss_release_name(&minor, &target);
+    (void) gss_release_cred(&minor, &delegated_cred_handle);
+    (void) gss_release_cred(&minor, &impersonator_cred_handle);
+    (void) gss_release_cred(&minor, &user_cred_handle);
+    (void) gss_release_oid_set(&minor, &actual_mechs);
+
+    return GSS_ERROR(major) ? 1 : 0;
+}
+