diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2023-08-04 17:53:10 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2023-08-04 17:53:10 +0000 |
| commit | 0320e0d5bb9fbb5da53478b3fd80ad79b110191d (patch) | |
| tree | e1185f75bd2d3f87b0c17f787debc3ee8648214b /src/plugins/preauth/pkinit | |
| parent | b0e4d68d5124581ae353493d69bea352de4cff8a (diff) | |
Diffstat (limited to 'src/plugins/preauth/pkinit')
| -rw-r--r-- | src/plugins/preauth/pkinit/Makefile.in | 6 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/deps | 13 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkcs11.h | 2 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit.h | 17 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_accessor.c | 12 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_accessor.h | 6 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_clnt.c | 303 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_constants.c | 325 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto.h | 66 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto_openssl.c | 3120 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto_openssl.h | 23 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_identity.c | 99 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_kdf_constants.c | 59 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_kdf_test.c | 22 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_lib.c | 95 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_matching.c | 60 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_srv.c | 701 | ||||
| -rw-r--r-- | src/plugins/preauth/pkinit/pkinit_trace.h | 66 |
18 files changed, 2310 insertions, 2685 deletions
diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in index d8b9398180ef..86f143d72d1c 100644 --- a/src/plugins/preauth/pkinit/Makefile.in +++ b/src/plugins/preauth/pkinit/Makefile.in @@ -10,14 +10,14 @@ RELDIR=../plugins/preauth/pkinit SHLIB_EXPDEPS = \ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ $(TOPLIBD)/libkrb5$(SHLIBEXT) -SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto -lcrypto $(DL_LIB) $(SUPPORT_LIB) $(LIBS) +SHLIB_EXPLIBS= -lkrb5 $(COM_ERR_LIB) -lk5crypto -lcrypto $(DL_LIB) $(SUPPORT_LIB) $(LIBS) STLIBOBJS= \ pkinit_accessor.o \ pkinit_srv.o \ pkinit_lib.o \ pkinit_clnt.o \ - pkinit_kdf_constants.o \ + pkinit_constants.o \ pkinit_profile.o \ pkinit_identity.o \ pkinit_matching.o \ @@ -28,7 +28,7 @@ SRCS= \ $(srcdir)/pkinit_srv.c \ $(srcdir)/pkinit_lib.c \ $(srcdir)/pkinit_kdf_test.c \ - $(srcdir)/pkinit_kdf_constants.c \ + $(srcdir)/pkinit_constants.c \ $(srcdir)/pkinit_clnt.c \ $(srcdir)/pkinit_profile.c \ $(srcdir)/pkinit_identity.c \ diff --git a/src/plugins/preauth/pkinit/deps b/src/plugins/preauth/pkinit/deps index 57116db1618c..58320aa80124 100644 --- a/src/plugins/preauth/pkinit/deps +++ b/src/plugins/preauth/pkinit/deps @@ -44,14 +44,14 @@ pkinit_kdf_test.so pkinit_kdf_test.po $(OUTPRE)pkinit_kdf_test.$(OBJEXT): \ $(top_srcdir)/include/krb5/preauth_plugin.h pkcs11.h \ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_kdf_test.c \ pkinit_trace.h -pkinit_kdf_constants.so pkinit_kdf_constants.po $(OUTPRE)pkinit_kdf_constants.$(OBJEXT): \ +pkinit_constants.so pkinit_constants.po $(OUTPRE)pkinit_constants.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-int-pkinit.h \ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \ $(top_srcdir)/include/krb5/kdcpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \ $(top_srcdir)/include/krb5/preauth_plugin.h pkcs11.h \ - pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_kdf_constants.c \ + pkinit.h pkinit_accessor.h pkinit_constants.c pkinit_crypto.h \ pkinit_trace.h pkinit_clnt.so pkinit_clnt.po $(OUTPRE)pkinit_clnt.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ @@ -101,10 +101,15 @@ pkinit_matching.so pkinit_matching.po $(OUTPRE)pkinit_matching.$(OBJEXT): \ pkinit_trace.h pkinit_crypto_openssl.so pkinit_crypto_openssl.po $(OUTPRE)pkinit_crypto_openssl.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ - $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ - $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-platform.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-hex.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ $(top_srcdir)/include/krb5/clpreauth_plugin.h $(top_srcdir)/include/krb5/kdcpreauth_plugin.h \ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ pkcs11.h pkinit.h pkinit_accessor.h pkinit_crypto.h \ pkinit_crypto_openssl.c pkinit_crypto_openssl.h pkinit_trace.h diff --git a/src/plugins/preauth/pkinit/pkcs11.h b/src/plugins/preauth/pkinit/pkcs11.h index 28ded4a89f37..e3d2846315ad 100644 --- a/src/plugins/preauth/pkinit/pkcs11.h +++ b/src/plugins/preauth/pkinit/pkcs11.h @@ -12,7 +12,7 @@ PURPOSE. */ /* Please submit changes back to the Scute project at - http://www.scute.org/ (or send them to marcus@g10code.com), so that + https://www.scute.org/ (or send them to marcus@g10code.com), so that they can be picked up by other projects from there as well. */ /* This file is a modified implementation of the PKCS #11 standard by diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h index f3de9ad7a15d..66f92d8f03f5 100644 --- a/src/plugins/preauth/pkinit/pkinit.h +++ b/src/plugins/preauth/pkinit/pkinit.h @@ -42,7 +42,6 @@ #ifndef WITHOUT_PKCS11 #include "pkcs11.h" -#define PKCS11_MODNAME "opensc-pkcs11.so" #define PK_SIGLEN_GUESS 1000 #define PK_NOSLOT 999999 #endif @@ -77,6 +76,7 @@ #define KRB5_CONF_PKINIT_KDC_OCSP "pkinit_kdc_ocsp" #define KRB5_CONF_PKINIT_POOL "pkinit_pool" #define KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING "pkinit_require_crl_checking" +#define KRB5_CONF_PKINIT_REQUIRE_FRESHNESS "pkinit_require_freshness" #define KRB5_CONF_PKINIT_REVOKE "pkinit_revoke" /* Make pkiDebug(fmt,...) print, or not. */ @@ -148,6 +148,8 @@ typedef struct _pkinit_plg_opts { int allow_upn; /* allow UPN-SAN instead of pkinit-SAN */ int dh_or_rsa; /* selects DH or RSA based pkinit */ int require_crl_checking; /* require CRL for a CA (default is false) */ + int require_freshness; /* require freshness token (default is false) */ + int disable_freshness; /* disable freshness token on client for testing */ int dh_min_bits; /* minimum DH modulus size allowed */ } pkinit_plg_opts; @@ -162,6 +164,7 @@ typedef struct _pkinit_req_opts { int require_crl_checking; int dh_size; /* initial request DH modulus size (default=1024) */ int require_hostname_match; + int disable_freshness; } pkinit_req_opts; /* @@ -209,11 +212,11 @@ struct _pkinit_req_context { pkinit_identity_opts *idopts; int do_identity_matching; krb5_preauthtype pa_type; - int rfc4556_kdc; int rfc6112_kdc; int identity_initialized; int identity_prompted; krb5_error_code identity_prompt_retval; + krb5_data *freshness_token; }; typedef struct _pkinit_req_context *pkinit_req_context; @@ -239,7 +242,6 @@ struct _pkinit_kdc_req_context { int magic; pkinit_req_crypto_context cryptoctx; krb5_auth_pack *rcv_auth_pack; - krb5_auth_pack_draft9 *rcv_auth_pack9; krb5_preauthtype pa_type; }; typedef struct _pkinit_kdc_req_context *pkinit_kdc_req_context; @@ -324,27 +326,18 @@ void pkinit_free_deferred_ids(pkinit_deferred_id *identities); * initialization and free functions */ void init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in); -void init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in); void init_krb5_reply_key_pack(krb5_reply_key_pack **in); -void init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in); void init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in); -void init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in); -void init_krb5_subject_pk_info(krb5_subject_pk_info **in); void free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in); -void free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in); void free_krb5_reply_key_pack(krb5_reply_key_pack **in); -void free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in); void free_krb5_auth_pack(krb5_auth_pack **in); -void free_krb5_auth_pack_draft9(krb5_context, krb5_auth_pack_draft9 **in); void free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in); -void free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in); void free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in); void free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in); void free_krb5_algorithm_identifier(krb5_algorithm_identifier *in); void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in); -void free_krb5_subject_pk_info(krb5_subject_pk_info **in); krb5_error_code pkinit_copy_krb5_data(krb5_data *dst, const krb5_data *src); diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.c b/src/plugins/preauth/pkinit/pkinit_accessor.c index 6bae94969a87..0908f1b9bd89 100644 --- a/src/plugins/preauth/pkinit/pkinit_accessor.c +++ b/src/plugins/preauth/pkinit/pkinit_accessor.c @@ -41,23 +41,16 @@ krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***) DEF_FUNC_PTRS(krb5_auth_pack); -DEF_FUNC_PTRS(krb5_auth_pack_draft9); DEF_FUNC_PTRS(krb5_kdc_dh_key_info); DEF_FUNC_PTRS(krb5_pa_pk_as_rep); DEF_FUNC_PTRS(krb5_pa_pk_as_req); -DEF_FUNC_PTRS(krb5_pa_pk_as_req_draft9); DEF_FUNC_PTRS(krb5_reply_key_pack); -DEF_FUNC_PTRS(krb5_reply_key_pack_draft9); /* special cases... */ krb5_error_code (*k5int_decode_krb5_principal_name)(const krb5_data *, krb5_principal_data **); krb5_error_code -(*k5int_encode_krb5_pa_pk_as_rep_draft9)(const krb5_pa_pk_as_rep_draft9 *, - krb5_data **code); - -krb5_error_code (*k5int_encode_krb5_td_dh_parameters)(krb5_algorithm_identifier *const *, krb5_data **code); krb5_error_code @@ -101,21 +94,16 @@ pkinit_accessor_init(void) k5int_decode_##type = k5int.decode_##type; SET_PTRS(krb5_auth_pack); - SET_PTRS(krb5_auth_pack_draft9); SET_PTRS(krb5_kdc_dh_key_info); SET_PTRS(krb5_pa_pk_as_rep); SET_PTRS(krb5_pa_pk_as_req); - SET_PTRS(krb5_pa_pk_as_req_draft9); SET_PTRS(krb5_reply_key_pack); - SET_PTRS(krb5_reply_key_pack_draft9); SET_PTRS(krb5_td_dh_parameters); SET_PTRS(krb5_td_trusted_certifiers); /* special cases... */ k5int_decode_krb5_principal_name = k5int.decode_krb5_principal_name; k5int_encode_krb5_kdc_req_body = k5int.encode_krb5_kdc_req_body; - k5int_encode_krb5_pa_pk_as_rep_draft9 = \ - k5int.encode_krb5_pa_pk_as_rep_draft9; k5int_krb5_free_kdc_req = k5int.free_kdc_req; k5int_set_prompt_types = k5int.set_prompt_types; return 0; diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.h b/src/plugins/preauth/pkinit/pkinit_accessor.h index dcee3db539e6..e510ab624ed4 100644 --- a/src/plugins/preauth/pkinit/pkinit_accessor.h +++ b/src/plugins/preauth/pkinit/pkinit_accessor.h @@ -45,21 +45,15 @@ extern krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \ extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***) DEF_EXT_FUNC_PTRS(krb5_auth_pack); -DEF_EXT_FUNC_PTRS(krb5_auth_pack_draft9); DEF_EXT_FUNC_PTRS(krb5_kdc_dh_key_info); DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep); DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req); -DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req_draft9); DEF_EXT_FUNC_PTRS(krb5_reply_key_pack); -DEF_EXT_FUNC_PTRS(krb5_reply_key_pack_draft9); /* special cases... */ extern krb5_error_code (*k5int_decode_krb5_principal_name) (const krb5_data *, krb5_principal_data **); -extern krb5_error_code (*k5int_encode_krb5_pa_pk_as_rep_draft9) - (const krb5_pa_pk_as_rep_draft9 *, krb5_data **code); - extern krb5_error_code (*k5int_encode_krb5_td_dh_parameters) (krb5_algorithm_identifier *const *, krb5_data **code); extern krb5_error_code (*k5int_decode_krb5_td_dh_parameters) diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c index f1bc6b21dc47..725d5bc43863 100644 --- a/src/plugins/preauth/pkinit/pkinit_clnt.c +++ b/src/plugins/preauth/pkinit/pkinit_clnt.c @@ -34,7 +34,6 @@ #include "k5-json.h" #include <unistd.h> -#include <dlfcn.h> #include <sys/stat.h> /** @@ -119,8 +118,8 @@ pa_pkinit_gen_req(krb5_context context, goto cleanup; } - retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, - der_req, &cksum); + retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req, + &cksum); if (retval) goto cleanup; TRACE_PKINIT_CLIENT_REQ_CHECKSUM(context, &cksum); @@ -148,11 +147,7 @@ pa_pkinit_gen_req(krb5_context context, goto cleanup; } - /* - * The most we'll return is two pa_data, normally just one. - * We need to make room for the NULL terminator. - */ - return_pa_data = k5calloc(3, sizeof(*return_pa_data), &retval); + return_pa_data = k5calloc(2, sizeof(*return_pa_data), &retval); if (return_pa_data == NULL) goto cleanup; @@ -162,23 +157,14 @@ pa_pkinit_gen_req(krb5_context context, return_pa_data[0]->magic = KV5M_PA_DATA; - if (pa_type == KRB5_PADATA_PK_AS_REQ_OLD) - return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD; - else - return_pa_data[0]->pa_type = pa_type; + return_pa_data[0]->pa_type = pa_type; return_pa_data[0]->length = out_data->length; return_pa_data[0]->contents = (krb5_octet *) out_data->data; *out_data = empty_data(); - if (return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD) { - return_pa_data[1] = k5alloc(sizeof(*return_pa_data[1]), &retval); - if (return_pa_data[1] == NULL) - goto cleanup; - return_pa_data[1]->pa_type = KRB5_PADATA_AS_CHECKSUM; - } - *out_padata = return_pa_data; return_pa_data = NULL; + cb->disable_fallback(context, rock); cleanup: krb5_free_data(context, der_req); @@ -201,84 +187,51 @@ pkinit_as_req_create(krb5_context context, krb5_data ** as_req) { krb5_error_code retval = ENOMEM; - krb5_subject_pk_info info; - krb5_data *coded_auth_pack = NULL; + krb5_data spki = empty_data(), *coded_auth_pack = NULL; krb5_auth_pack auth_pack; krb5_pa_pk_as_req *req = NULL; - krb5_auth_pack_draft9 auth_pack9; - krb5_pa_pk_as_req_draft9 *req9 = NULL; krb5_algorithm_identifier **cmstypes = NULL; int protocol = reqctx->opts->dh_or_rsa; - unsigned char *dh_params = NULL, *dh_pubkey = NULL; - unsigned int dh_params_len, dh_pubkey_len; pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type); /* Create the authpack */ - switch((int)reqctx->pa_type) { - case KRB5_PADATA_PK_AS_REQ_OLD: - protocol = RSA_PROTOCOL; - memset(&auth_pack9, 0, sizeof(auth_pack9)); - auth_pack9.pkAuthenticator.ctime = ctsec; - auth_pack9.pkAuthenticator.cusec = cusec; - auth_pack9.pkAuthenticator.nonce = nonce; - auth_pack9.pkAuthenticator.kdcName = server; - break; - case KRB5_PADATA_PK_AS_REQ: - memset(&info, 0, sizeof(info)); - memset(&auth_pack, 0, sizeof(auth_pack)); - auth_pack.pkAuthenticator.ctime = ctsec; - auth_pack.pkAuthenticator.cusec = cusec; - auth_pack.pkAuthenticator.nonce = nonce; - auth_pack.pkAuthenticator.paChecksum = *cksum; - auth_pack.clientDHNonce.length = 0; - auth_pack.clientPublicValue = &info; - auth_pack.supportedKDFs = (krb5_data **)supported_kdf_alg_ids; + memset(&auth_pack, 0, sizeof(auth_pack)); + auth_pack.pkAuthenticator.ctime = ctsec; + auth_pack.pkAuthenticator.cusec = cusec; + auth_pack.pkAuthenticator.nonce = nonce; + auth_pack.pkAuthenticator.paChecksum = *cksum; + if (!reqctx->opts->disable_freshness) + auth_pack.pkAuthenticator.freshnessToken = reqctx->freshness_token; + auth_pack.clientDHNonce.length = 0; + auth_pack.supportedKDFs = (krb5_data **)supported_kdf_alg_ids; - /* add List of CMS algorithms */ - retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx, - reqctx->cryptoctx, - reqctx->idctx, &cmstypes); - auth_pack.supportedCMSTypes = cmstypes; - if (retval) - goto cleanup; - break; - default: - pkiDebug("as_req: unrecognized pa_type = %d\n", - (int)reqctx->pa_type); - retval = -1; + /* add List of CMS algorithms */ + retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx, + reqctx->cryptoctx, + reqctx->idctx, &cmstypes); + auth_pack.supportedCMSTypes = cmstypes; + if (retval) goto cleanup; - } switch(protocol) { case DH_PROTOCOL: TRACE_PKINIT_CLIENT_REQ_DH(context); pkiDebug("as_req: DH key transport algorithm\n"); - info.algorithm.algorithm = dh_oid; /* create client-side DH keys */ retval = client_create_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, - reqctx->opts->dh_size, &dh_params, - &dh_params_len, &dh_pubkey, &dh_pubkey_len); + reqctx->opts->dh_size, &spki); + auth_pack.clientPublicValue = spki; if (retval != 0) { pkiDebug("failed to create dh parameters\n"); goto cleanup; } - info.algorithm.parameters = make_data(dh_params, dh_params_len); - info.subjectPublicKey = make_data(dh_pubkey, dh_pubkey_len); break; case RSA_PROTOCOL: TRACE_PKINIT_CLIENT_REQ_RSA(context); pkiDebug("as_req: RSA key transport algorithm\n"); - switch((int)reqctx->pa_type) { - case KRB5_PADATA_PK_AS_REQ_OLD: - auth_pack9.clientPublicValue = NULL; - break; - case KRB5_PADATA_PK_AS_REQ: - auth_pack.clientPublicValue = NULL; - break; - } break; default: pkiDebug("as_req: unknown key transport protocol %d\n", @@ -287,16 +240,7 @@ pkinit_as_req_create(krb5_context context, goto cleanup; } - /* Encode the authpack */ - switch((int)reqctx->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - retval = k5int_encode_krb5_auth_pack(&auth_pack, &coded_auth_pack); - break; - case KRB5_PADATA_PK_AS_REQ_OLD: - retval = k5int_encode_krb5_auth_pack_draft9(&auth_pack9, - &coded_auth_pack); - break; - } + retval = k5int_encode_krb5_auth_pack(&auth_pack, &coded_auth_pack); if (retval) { pkiDebug("failed to encode the AuthPack %d\n", retval); goto cleanup; @@ -308,60 +252,39 @@ pkinit_as_req_create(krb5_context context, #endif /* create PKCS7 object from authpack */ - switch((int)reqctx->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - init_krb5_pa_pk_as_req(&req); - if (req == NULL) { - retval = ENOMEM; - goto cleanup; - } - if (use_content_info(context, reqctx, client)) { - retval = cms_contentinfo_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, - CMS_SIGN_CLIENT, - (unsigned char *) - coded_auth_pack->data, - coded_auth_pack->length, - (unsigned char **) - &req->signedAuthPack.data, - &req->signedAuthPack.length); - } else { - retval = cms_signeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, - CMS_SIGN_CLIENT, 1, - (unsigned char *) - coded_auth_pack->data, - coded_auth_pack->length, - (unsigned char **) - &req->signedAuthPack.data, - &req->signedAuthPack.length); - } -#ifdef DEBUG_ASN1 - print_buffer_bin((unsigned char *)req->signedAuthPack.data, - req->signedAuthPack.length, - "/tmp/client_signed_data"); -#endif - break; - case KRB5_PADATA_PK_AS_REQ_OLD: - init_krb5_pa_pk_as_req_draft9(&req9); - if (req9 == NULL) { - retval = ENOMEM; - goto cleanup; - } + init_krb5_pa_pk_as_req(&req); + if (req == NULL) { + retval = ENOMEM; + goto cleanup; + } + if (use_content_info(context, reqctx, client)) { + retval = cms_contentinfo_create(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, + CMS_SIGN_CLIENT, + (unsigned char *) + coded_auth_pack->data, + coded_auth_pack->length, + (unsigned char **) + &req->signedAuthPack.data, + &req->signedAuthPack.length); + } else { retval = cms_signeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1, - (unsigned char *)coded_auth_pack->data, + reqctx->cryptoctx, reqctx->idctx, + CMS_SIGN_CLIENT, + (unsigned char *) + coded_auth_pack->data, coded_auth_pack->length, (unsigned char **) - &req9->signedAuthPack.data, - &req9->signedAuthPack.length); - break; + &req->signedAuthPack.data, + &req->signedAuthPack.length); + } + #ifdef DEBUG_ASN1 - print_buffer_bin((unsigned char *)req9->signedAuthPack.data, - req9->signedAuthPack.length, - "/tmp/client_signed_data_draft9"); + print_buffer_bin((unsigned char *)req->signedAuthPack.data, + req->signedAuthPack.length, + "/tmp/client_signed_data"); #endif - } + krb5_free_data(context, coded_auth_pack); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); @@ -369,33 +292,21 @@ pkinit_as_req_create(krb5_context context, } /* create a list of trusted CAs */ - switch((int)reqctx->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers); - if (retval) - goto cleanup; - retval = create_issuerAndSerial(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, - (unsigned char **)&req->kdcPkId.data, - &req->kdcPkId.length); - if (retval) - goto cleanup; + retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, + &req->trustedCertifiers); + if (retval) + goto cleanup; + retval = create_issuerAndSerial(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, + (unsigned char **)&req->kdcPkId.data, + &req->kdcPkId.length); + if (retval) + goto cleanup; + + /* Encode the as-req */ + retval = k5int_encode_krb5_pa_pk_as_req(req, as_req); - /* Encode the as-req */ - retval = k5int_encode_krb5_pa_pk_as_req(req, as_req); - break; - case KRB5_PADATA_PK_AS_REQ_OLD: - retval = create_issuerAndSerial(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, - (unsigned char **)&req9->kdcCert.data, - &req9->kdcCert.length); - if (retval) - goto cleanup; - /* Encode the as-req */ - retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req); - break; - } #ifdef DEBUG_ASN1 if (!retval) print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length, @@ -404,10 +315,8 @@ pkinit_as_req_create(krb5_context context, cleanup: free_krb5_algorithm_identifiers(&cmstypes); - free(dh_params); - free(dh_pubkey); free_krb5_pa_pk_as_req(&req); - free_krb5_pa_pk_as_req_draft9(&req9); + krb5_free_data_contents(context, &spki); pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval); @@ -504,24 +413,6 @@ verify_kdc_san(krb5_context context, for (hostptr = certhosts; *hostptr != NULL; hostptr++) TRACE_PKINIT_CLIENT_SAN_KDCCERT_DNSNAME(context, *hostptr); } -#if 0 - retval = call_san_checking_plugins(context, plgctx, reqctx, idctx, - princs, hosts, &plugin_decision, - need_eku_checking); - pkiDebug("%s: call_san_checking_plugins() returned retval %d\n", - __FUNCTION__); - if (retval) { - retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; - goto out; - } - pkiDebug("%s: call_san_checking_plugins() returned decision %d and " - "need_eku_checking %d\n", - __FUNCTION__, plugin_decision, *need_eku_checking); - if (plugin_decision != NO_DECISION) { - retval = plugin_decision; - goto out; - } -#endif pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__); for (i = 0; princs != NULL && princs[i] != NULL; i++) { @@ -807,13 +698,6 @@ pkinit_as_rep_parse(krb5_context context, pkiDebug("failed to decode reply_key_pack\n"); goto cleanup; } - /* - * This is hack but Windows sends back SHA1 checksum - * with checksum type of 14. There is currently no - * checksum type of 14 defined. - */ - if (key_pack->asChecksum.checksum_type == 14) - key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA; retval = krb5_c_make_checksum(context, key_pack->asChecksum.checksum_type, &key_pack->replyKey, @@ -856,7 +740,7 @@ pkinit_as_rep_parse(krb5_context context, break; default: - pkiDebug("unknow as_rep type %d\n", kdc_reply->choice); + pkiDebug("unknown as_rep type %d\n", kdc_reply->choice); goto cleanup; } @@ -1003,6 +887,11 @@ pkinit_client_prep_questions(krb5_context context, k5_json_object jval = NULL; k5_json_number jflag = NULL; + /* Don't ask questions for the informational padata items or when the + * ticket is issued. */ + if (pa_data->pa_type != KRB5_PADATA_PK_AS_REQ) + return 0; + if (!reqctx->identity_initialized) { pkinit_client_profile(context, plgctx, reqctx, cb, rock, &request->server->realm); @@ -1017,8 +906,6 @@ pkinit_client_prep_questions(krb5_context context, } reqctx->identity_initialized = TRUE; - crypto_free_cert_info(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx); if (retval != 0) { pkiDebug("%s: not asking responder question\n", __FUNCTION__); retval = 0; @@ -1162,6 +1049,7 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata, pkinit_context plgctx = (pkinit_context)moddata; pkinit_req_context reqctx = (pkinit_req_context)modreq; krb5_keyblock as_key; + krb5_data d; pkiDebug("pkinit_client_process %p %p %p %p\n", context, plgctx, reqctx, request); @@ -1174,32 +1062,20 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata, case KRB5_PADATA_PKINIT_KX: reqctx->rfc6112_kdc = 1; return 0; + case KRB5_PADATA_AS_FRESHNESS: + TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(context); + krb5_free_data(context, reqctx->freshness_token); + reqctx->freshness_token = NULL; + d = make_data(in_padata->contents, in_padata->length); + return krb5_copy_data(context, &d, &reqctx->freshness_token); case KRB5_PADATA_PK_AS_REQ: - reqctx->rfc4556_kdc = 1; pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); processing_request = 1; break; case KRB5_PADATA_PK_AS_REP: - reqctx->rfc4556_kdc = 1; pkiDebug("processing KRB5_PADATA_PK_AS_REP\n"); break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - /* Don't fall back to draft9 code if the KDC supports RFC 4556. */ - if (reqctx->rfc4556_kdc) { - TRACE_PKINIT_CLIENT_NO_DRAFT9(context); - return KRB5KDC_ERR_PREAUTH_FAILED; - } - if (in_padata->length == 0) { - pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); - in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD; - processing_request = 1; - } else { - pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n"); - in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD; - } - break; default: pkiDebug("unrecognized patype = %d for PKINIT\n", in_padata->pa_type); @@ -1207,8 +1083,11 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata, } if (processing_request) { - pkinit_client_profile(context, plgctx, reqctx, cb, rock, - &request->server->realm); + if (reqctx->idopts->anchors == NULL) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + _("No pkinit_anchors supplied")); + return KRB5_PREAUTH_FAILED; + } /* Pull in PINs and passwords for identities which we deferred * loading earlier. */ retval = pkinit_client_parse_answers(context, moddata, modreq, @@ -1359,7 +1238,7 @@ cleanup: static int pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype) { - if (patype == KRB5_PADATA_PKINIT_KX) + if (patype == KRB5_PADATA_PKINIT_KX || patype == KRB5_PADATA_AS_FRESHNESS) return PA_INFO; return PA_REAL; } @@ -1373,9 +1252,8 @@ pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype) static krb5_preauthtype supported_client_pa_types[] = { KRB5_PADATA_PK_AS_REP, KRB5_PADATA_PK_AS_REQ, - KRB5_PADATA_PK_AS_REP_OLD, - KRB5_PADATA_PK_AS_REQ_OLD, KRB5_PADATA_PKINIT_KX, + KRB5_PADATA_AS_FRESHNESS, 0 }; @@ -1400,6 +1278,7 @@ pkinit_client_req_init(krb5_context context, reqctx->opts = NULL; reqctx->idctx = NULL; reqctx->idopts = NULL; + reqctx->freshness_token = NULL; retval = pkinit_init_req_opts(&reqctx->opts); if (retval) @@ -1410,6 +1289,7 @@ pkinit_client_req_init(krb5_context context, reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa; reqctx->opts->allow_upn = plgctx->opts->allow_upn; reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking; + reqctx->opts->disable_freshness = plgctx->opts->disable_freshness; retval = pkinit_init_req_crypto(&reqctx->cryptoctx); if (retval) @@ -1468,6 +1348,8 @@ pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata, if (reqctx->idopts != NULL) pkinit_fini_identity_opts(reqctx->idopts); + krb5_free_data(context, reqctx->freshness_token); + free(reqctx); return; } @@ -1580,6 +1462,9 @@ handle_gic_opt(krb5_context context, pkiDebug("Setting flag to use RSA_PROTOCOL\n"); plgctx->opts->dh_or_rsa = RSA_PROTOCOL; } + } else if (strcmp(attr, "disable_freshness") == 0) { + if (strcmp(value, "yes") == 0) + plgctx->opts->disable_freshness = 1; } return 0; } diff --git a/src/plugins/preauth/pkinit/pkinit_constants.c b/src/plugins/preauth/pkinit/pkinit_constants.c new file mode 100644 index 000000000000..1da482e0b4ab --- /dev/null +++ b/src/plugins/preauth/pkinit/pkinit_constants.c @@ -0,0 +1,325 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* plugins/preauth/pkinit/pkinit_constants.c */ +/* + * Copyright (C) 2011,2021 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pkinit.h" + +/* RFC 8636 id-pkinit-kdf-ah-sha1: iso(1) identified-organization(3) dod(6) + * internet(1) security(5) kerberosv5(2) pkinit(3) kdf(6) sha1(1) */ +static char sha1_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x01 }; +/* RFC 8636 id-pkinit-kdf-ah-sha256: iso(1) identified-organization(3) dod(6) + * internet(1) security(5) kerberosv5(2) pkinit(3) kdf(6) sha256(2) */ +static char sha256_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x02 }; +/* RFC 8636 id-pkinit-kdf-ah-sha512: iso(1) identified-organization(3) dod(6) + * internet(1) security(5) kerberosv5(2) pkinit(3) kdf(6) sha512(3) */ +static char sha512_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x03 }; + +const krb5_data sha1_id = { KV5M_DATA, sizeof(sha1_oid), sha1_oid }; +const krb5_data sha256_id = { KV5M_DATA, sizeof(sha256_oid), sha256_oid }; +const krb5_data sha512_id = { KV5M_DATA, sizeof(sha512_oid), sha512_oid }; + +krb5_data const * const supported_kdf_alg_ids[] = { + &sha256_id, + &sha1_id, + &sha512_id, + NULL +}; + +/* RFC 4055 sha256WithRSAEncryption: iso(1) member-body(2) us(840) + * rsadsi(113549) pkcs(1) 1 11 */ +static char sha256WithRSAEncr_oid[9] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b +}; +/* RFC 4055 sha256WithRSAEncryption: iso(1) member-body(2) us(840) + * rsadsi(113549) pkcs(1) 1 13 */ +static char sha512WithRSAEncr_oid[9] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d +}; + +const krb5_data sha256WithRSAEncr_id = { + KV5M_DATA, sizeof(sha256WithRSAEncr_oid), sha256WithRSAEncr_oid +}; +const krb5_data sha512WithRSAEncr_id = { + KV5M_DATA, sizeof(sha512WithRSAEncr_oid), sha512WithRSAEncr_oid +}; + +krb5_data const * const supported_cms_algs[] = { + &sha512WithRSAEncr_id, + &sha256WithRSAEncr_id, + NULL +}; + +/* RFC 2412 section E.2 (well-known group 2) parameters, DER-encoded as + * DomainParameters (RFC 3279 section 2.3.3). */ +static const uint8_t o1024[] = { + 0x30, 0x82, 0x01, 0x0A, 0x02, 0x81, 0x81, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x02, 0x01, 0x02, 0x02, 0x81, 0x80, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, + 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, + 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, + 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, + 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, + 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, + 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, + 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, + 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, + 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, + 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, + 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, + 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, + 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, + 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526 section 3 (2048-bit MODP Group), RFC 3279 encoding */ +static const uint8_t o2048[] = { + 0x30, 0x82, 0x02, 0x0C, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, + 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, + 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, + 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, + 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, + 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, + 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, + 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, + 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, + 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, + 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, + 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, + 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, + 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, + 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, + 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, + 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, + 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, + 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, + 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, + 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, + 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, + 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, + 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, + 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, + 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, + 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, + 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, + 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, + 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, + 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x02, 0x01, 0x02, 0x02, 0x82, 0x01, 0x00, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526 section 5 (4096-bit MODP Group), RFC 3279 encoding */ +static const uint8_t o4096[] = { + 0x30, 0x82, 0x04, 0x0C, 0x02, 0x82, 0x02, 0x01, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, + 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, + 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, + 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, + 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, + 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, + 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, + 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, + 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, + 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, + 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, + 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, + 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, + 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, + 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, + 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, + 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, + 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, + 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, + 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, + 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, + 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, + 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, + 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, + 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, + 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, + 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, + 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, + 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, + 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, + 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, + 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, + 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, + 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, + 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, + 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, + 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, + 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, + 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, + 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, + 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, + 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, + 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, + 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, + 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, + 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, + 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, + 0xD7, 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, + 0x26, 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, + 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, + 0xDA, 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, + 0xE8, 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, + 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, + 0xA6, 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, + 0x5D, 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, + 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, + 0xED, 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, + 0xAF, 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, + 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, + 0xA9, 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, + 0xC1, 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, + 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, + 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x02, 0x01, 0x02, 0x02, 0x82, 0x02, 0x00, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +const krb5_data oakley_1024 = { KV5M_DATA, sizeof(o1024), (char *)o1024 }; +const krb5_data oakley_2048 = { KV5M_DATA, sizeof(o2048), (char *)o2048 }; +const krb5_data oakley_4096 = { KV5M_DATA, sizeof(o4096), (char *)o4096 }; diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h index 2d3733bbc8dd..e22798f668b6 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto.h +++ b/src/plugins/preauth/pkinit/pkinit_crypto.h @@ -46,7 +46,6 @@ */ enum cms_msg_types { CMS_SIGN_CLIENT, - CMS_SIGN_DRAFT9, CMS_SIGN_SERVER, CMS_ENVEL_SERVER }; @@ -97,8 +96,8 @@ typedef struct _pkinit_cert_matching_data { char *issuer_dn; /* rfc2253-style issuer name string */ unsigned int ku_bits; /* key usage information */ unsigned int eku_bits; /* extended key usage information */ - krb5_principal *sans; /* Null-terminated array of subject alternative - name info (pkinit and ms-upn) */ + krb5_principal *sans; /* Null-terminated array of PKINIT SANs */ + char **upns; /* Null-terimnated array of UPN SANs */ } pkinit_cert_matching_data; /* @@ -133,9 +132,6 @@ krb5_error_code cms_signeddata_create int cms_msg_type, /* IN specifies CMS_SIGN_CLIENT for client-side CMS message and CMS_SIGN_SERVER for kdc-side */ - int include_certchain, /* IN - specifies where certificates field in SignedData - should contain certificate path */ unsigned char *auth_pack, /* IN contains DER encoded AuthPack (CMS_SIGN_CLIENT) or DER encoded DHRepInfo (CMS_SIGN_SERVER) */ @@ -193,9 +189,6 @@ krb5_error_code cms_envelopeddata_create pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_preauthtype pa_type, /* IN */ - int include_certchain, /* IN - specifies whether the certificates field in - SignedData should contain certificate path */ unsigned char *key_pack, /* IN contains DER encoded ReplyKeyPack */ unsigned int key_pack_len, /* IN @@ -250,7 +243,7 @@ krb5_error_code crypto_retrieve_cert_sans if non-NULL, a null-terminated array of id-pkinit-san values found in the certificate are returned */ - krb5_principal **upn_sans, /* OUT + char ***upn_sans, /* OUT if non-NULL, a null-terminated array of id-ms-upn-san values found in the certificate are returned */ @@ -316,14 +309,8 @@ krb5_error_code client_create_dh pkinit_identity_crypto_context id_cryptoctx, /* IN */ int dh_size, /* IN specifies the DH modulous, eg 1024, 2048, or 4096 */ - unsigned char **dh_paramas, /* OUT - contains DER encoded DH params */ - unsigned int *dh_params_len, /* OUT - contains length of dh_parmas */ - unsigned char **dh_pubkey, /* OUT - receives DER encoded DH pub key */ - unsigned int *dh_pubkey_len); /* OUT - receives length of dh_pubkey */ + krb5_data *spki_out); /* OUT + receives SubjectPublicKeyInfo encoding */ /* * this function completes client's the DH protocol. client @@ -339,10 +326,10 @@ krb5_error_code client_process_dh contains client's DER encoded DH pub key */ unsigned int dh_pubkey_len, /* IN contains length of dh_pubkey */ - unsigned char **dh_session_key, /* OUT + unsigned char **client_key_out, /* OUT receives DH secret key */ - unsigned int *dh_session_key_len); /* OUT - receives length of dh_session_key */ + unsigned int *client_key_len_out); /* OUT + receives length of DH secret key */ /* * this function implements the KDC first part of the DH protocol. @@ -354,10 +341,10 @@ krb5_error_code server_check_dh pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ - krb5_data *dh_params, /* IN - ???? */ + const krb5_data *client_spki, /* IN + SubjectPublicKeyInfo encoding from client */ int minbits); /* IN - the mininum number of key bits acceptable */ + the minimum number of key bits acceptable */ /* * this function completes the KDC's DH protocol. The KDC generates @@ -368,18 +355,14 @@ krb5_error_code server_process_dh pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ - unsigned char *received_pubkey, /* IN - contains client's DER encoded DH pub key */ - unsigned int received_pub_len, /* IN - contains length of received_pubkey */ - unsigned char **dh_pubkey, /* OUT + unsigned char **dh_pubkey_out, /* OUT receives KDC's DER encoded DH pub key */ - unsigned int *dh_pubkey_len, /* OUT + unsigned int *dh_pubkey_len_out, /* OUT receives length of dh_pubkey */ - unsigned char **server_key, /* OUT + unsigned char **server_key_out, /* OUT receives DH secret key */ - unsigned int *server_key_len); /* OUT - receives length of server_key */ + unsigned int *server_key_len_out); /* OUT + receives length of DH secret key */ /* * this functions takes in crypto specific representation of @@ -617,12 +600,13 @@ pkinit_alg_agility_kdf(krb5_context context, krb5_data *pk_as_rep, krb5_keyblock *key_block); -extern const krb5_octet krb5_pkinit_sha1_oid[]; -extern const size_t krb5_pkinit_sha1_oid_len; -extern const krb5_octet krb5_pkinit_sha256_oid[]; -extern const size_t krb5_pkinit_sha256_oid_len; -extern const krb5_octet krb5_pkinit_sha512_oid[]; -extern const size_t krb5_pkinit_sha512_oid_len; +extern const krb5_data sha1_id; +extern const krb5_data sha256_id; +extern const krb5_data sha512_id; +extern const krb5_data oakley_1024; +extern const krb5_data oakley_2048; +extern const krb5_data oakley_4096; + /** * An ordered set of OIDs, stored as krb5_data, of KDF algorithms * supported by this implementation. The order of this array controls @@ -630,6 +614,10 @@ extern const size_t krb5_pkinit_sha512_oid_len; */ extern krb5_data const * const supported_kdf_alg_ids[]; +/* CMS signature algorithms supported by this implementation, in order of + * decreasing preference. */ +extern krb5_data const * const supported_cms_algs[]; + krb5_error_code crypto_encode_der_cert(krb5_context context, pkinit_req_crypto_context reqctx, uint8_t **der_out, size_t *der_len); diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c index ac107c2c1b67..f41328763ec6 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c @@ -29,13 +29,21 @@ * SUCH DAMAGES. */ +#include "k5-int.h" #include "pkinit_crypto_openssl.h" #include "k5-buf.h" -#include <dlfcn.h> +#include "k5-err.h" +#include "k5-hex.h" #include <unistd.h> #include <dirent.h> #include <arpa/inet.h> +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include <openssl/core_names.h> +#include <openssl/kdf.h> +#include <openssl/params.h> +#endif + static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context ); static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ); @@ -48,11 +56,6 @@ static void pkinit_fini_certs(pkinit_identity_crypto_context ctx); static krb5_error_code pkinit_init_pkcs11(pkinit_identity_crypto_context ctx); static void pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx); -static krb5_error_code pkinit_encode_dh_params -(const BIGNUM *, const BIGNUM *, const BIGNUM *, uint8_t **, unsigned int *); -static DH *decode_dh_params(const uint8_t *, unsigned int ); -static int pkinit_check_dh_params(DH *dh1, DH *dh2); - static krb5_error_code pkinit_sign_data (krb5_context context, pkinit_identity_crypto_context cryptoctx, unsigned char *data, unsigned int data_len, @@ -79,12 +82,8 @@ static int openssl_callback (int, X509_STORE_CTX *); static int openssl_callback_ignore_crls (int, X509_STORE_CTX *); static int pkcs7_decrypt -(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, - PKCS7 *p7, BIO *bio); - -static BIO * pkcs7_dataDecode -(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, - PKCS7 *p7); +(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7, + unsigned char **data_out, unsigned int *len_out); static ASN1_OBJECT * pkinit_pkcs7type2oid (pkinit_plg_crypto_context plg_cryptoctx, int pkcs7_type); @@ -104,8 +103,6 @@ static krb5_error_code pkinit_login CK_TOKEN_INFO *tip, const char *password); static krb5_error_code pkinit_open_session (krb5_context context, pkinit_identity_crypto_context id_cryptoctx); -static void * pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p); -static CK_RV pkinit_C_UnloadModule(void *handle); #ifdef SILLYDECRYPT CK_RV pkinit_C_Decrypt (pkinit_identity_crypto_context id_cryptoctx, @@ -146,46 +143,9 @@ static int wrap_signeddata(unsigned char *data, unsigned int data_len, unsigned char **out, unsigned int *out_len); -static char * -pkinit_pkcs11_code_to_text(int err); - +static const char * +pkcs11err(int err); -#ifdef HAVE_OPENSSL_CMS -/* Use CMS support present in OpenSSL. */ -#include <openssl/cms.h> -#define pkinit_CMS_get0_content_signed(_cms) CMS_get0_content(_cms) -#define pkinit_CMS_get0_content_data(_cms) CMS_get0_content(_cms) -#define pkinit_CMS_free1_crls(_sk_x509crl) \ - sk_X509_CRL_pop_free((_sk_x509crl), X509_CRL_free) -#define pkinit_CMS_free1_certs(_sk_x509) \ - sk_X509_pop_free((_sk_x509), X509_free) -#define pkinit_CMS_SignerInfo_get_cert(_cms,_si,_x509_pp) \ - CMS_SignerInfo_get0_algs(_si,NULL,_x509_pp,NULL,NULL) -#else -/* Fake up CMS support using PKCS7. */ -#define pkinit_CMS_free1_crls(_stack_of_x509crls) /* Don't free these */ -#define pkinit_CMS_free1_certs(_stack_of_x509certs) /* Don't free these */ -#define CMS_NO_SIGNER_CERT_VERIFY PKCS7_NOVERIFY -#define CMS_NOATTR PKCS7_NOATTR -#define CMS_ContentInfo PKCS7 -#define CMS_SignerInfo PKCS7_SIGNER_INFO -#define d2i_CMS_ContentInfo d2i_PKCS7 -#define CMS_get0_type(_p7) ((_p7)->type) -#define pkinit_CMS_get0_content_signed(_p7) (&((_p7)->d.sign->contents->d.other->value.octet_string)) -#define pkinit_CMS_get0_content_data(_p7) (&((_p7)->d.other->value.octet_string)) -#define CMS_set1_signers_certs(_p7,_stack_of_x509,_uint) -#define CMS_get0_SignerInfos PKCS7_get_signer_info -#define stack_st_CMS_SignerInfo stack_st_PKCS7_SIGNER_INFO -#undef sk_CMS_SignerInfo_value -#define sk_CMS_SignerInfo_value sk_PKCS7_SIGNER_INFO_value -#define CMS_get0_eContentType(_p7) (_p7->d.sign->contents->type) -#define CMS_verify PKCS7_verify -#define CMS_get1_crls(_p7) (_p7->d.sign->crl) -#define CMS_get1_certs(_p7) (_p7->d.sign->cert) -#define CMS_ContentInfo_free(_p7) PKCS7_free(_p7) -#define pkinit_CMS_SignerInfo_get_cert(_p7,_si,_x509_pp) \ - (*_x509_pp) = PKCS7_cert_from_signer_info(_p7,_si) -#endif #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -196,35 +156,34 @@ pkinit_pkcs11_code_to_text(int err); #define EVP_MD_CTX_free EVP_MD_CTX_destroy #define ASN1_STRING_get0_data ASN1_STRING_data +/* + * 1.1 adds DHX support, which uses the RFC 3279 DomainParameters encoding we + * need for PKINIT. For 1.0 we must use the original DH type when creating + * EVP_PKEY objects. + */ +#define EVP_PKEY_DHX EVP_PKEY_DH + /* 1.1 makes many handle types opaque and adds accessors. Add compatibility * versions of the new accessors we use for pre-1.1. */ #define OBJ_get0_data(o) ((o)->data) #define OBJ_length(o) ((o)->length) -#define DH_set0_pqg compat_dh_set0_pqg -static int compat_dh_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) +#define DH_set0_key compat_dh_set0_key +static int +compat_dh_set0_key(DH *dh, BIGNUM *pub, BIGNUM *priv) { - /* The real function frees the old values and does argument checking, but - * our code doesn't need that. */ - dh->p = p; - dh->q = q; - dh->g = g; + if (pub != NULL) { + BN_clear_free(dh->pub_key); + dh->pub_key = pub; + } + if (priv != NULL) { + BN_clear_free(dh->priv_key); + dh->priv_key = priv; + } return 1; } -#define DH_get0_pqg compat_dh_get0_pqg -static void compat_dh_get0_pqg(const DH *dh, const BIGNUM **p, - const BIGNUM **q, const BIGNUM **g) -{ - if (p != NULL) - *p = dh->p; - if (q != NULL) - *q = dh->q; - if (g != NULL) - *g = dh->g; -} - #define DH_get0_key compat_dh_get0_key static void compat_dh_get0_key(const DH *dh, const BIGNUM **pub, const BIGNUM **priv) @@ -235,6 +194,16 @@ static void compat_dh_get0_key(const DH *dh, const BIGNUM **pub, *priv = dh->priv_key; } +#define EVP_PKEY_get0_DH compat_get0_DH +static DH * +compat_get0_DH(const EVP_PKEY *pkey) +{ + if (pkey->type != EVP_PKEY_DH) + return NULL; + return pkey->pkey.dh; + +} + /* Return true if the cert c includes a key usage which doesn't include u. * Define using direct member access for pre-1.1. */ #define ku_reject(c, u) \ @@ -247,6 +216,581 @@ static void compat_dh_get0_key(const DH *dh, const BIGNUM **pub, #endif +#if OPENSSL_VERSION_NUMBER < 0x30000000L +/* OpenSSL 3.0 changes several preferred function names. */ +#define EVP_PKEY_parameters_eq EVP_PKEY_cmp_parameters +#define EVP_MD_CTX_get0_md EVP_MD_CTX_md +#define EVP_PKEY_get_size EVP_PKEY_size +#define EVP_PKEY_get_bits EVP_PKEY_bits + +/* + * Convert *dh to an EVP_PKEY object, taking ownership of *dh and setting it to + * NULL. On error, return NULL and do not take ownership of or change *dh. + * OpenSSL 3.0 deprecates the low-level DH interfaces, so this helper will only + * be used with prior versions. + */ +static EVP_PKEY * +dh_to_pkey(DH **dh) +{ + EVP_PKEY *pkey; + + pkey = EVP_PKEY_new(); + if (pkey == NULL) + return NULL; + if (!EVP_PKEY_assign(pkey, EVP_PKEY_DHX, *dh)) { + EVP_PKEY_free(pkey); + return NULL; + } + *dh = NULL; + return pkey; +} +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + +/* Encode a bignum as an ASN.1 integer in DER. */ +static int +encode_bn_der(const BIGNUM *bn, uint8_t **der_out, int *len_out) +{ + ASN1_INTEGER *intval; + int len; + uint8_t *der = NULL, *outptr; + + intval = BN_to_ASN1_INTEGER(bn, NULL); + if (intval == NULL) + return 0; + len = i2d_ASN1_INTEGER(intval, NULL); + if (len > 0 && (outptr = der = malloc(len)) != NULL) + (void)i2d_ASN1_INTEGER(intval, &outptr); + ASN1_INTEGER_free(intval); + if (der == NULL) + return 0; + *der_out = der; + *len_out = len; + return 1; +} + +/* Decode an ASN.1 integer, returning a bignum. */ +static BIGNUM * +decode_bn_der(const uint8_t *der, size_t len) +{ + ASN1_INTEGER *intval; + BIGNUM *bn; + + intval = d2i_ASN1_INTEGER(NULL, &der, len); + if (intval == NULL) + return NULL; + bn = ASN1_INTEGER_to_BN(intval, NULL); + ASN1_INTEGER_free(intval); + return bn; +} + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +static int +params_valid(EVP_PKEY *params) +{ + EVP_PKEY_CTX *ctx; + int result; + + ctx = EVP_PKEY_CTX_new(params, NULL); + if (ctx == NULL) + return 0; + result = EVP_PKEY_param_check(ctx); + EVP_PKEY_CTX_free(ctx); + return result == 1; +} +#else +static int +params_valid(EVP_PKEY *params) +{ + DH *dh; + int codes; + + dh = EVP_PKEY_get0_DH(params); + return (dh == NULL) ? 0 : (DH_check(dh, &codes) && codes == 0); +} +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +static EVP_PKEY * +decode_dh_params(const krb5_data *params_der) +{ + EVP_PKEY *pkey = NULL; + const uint8_t *inptr = (uint8_t *)params_der->data; + size_t len = params_der->length; + OSSL_DECODER_CTX *dctx; + int ok; + + dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", "type-specific", "DHX", + EVP_PKEY_KEY_PARAMETERS, NULL, NULL); + if (dctx == NULL) + return NULL; + + ok = OSSL_DECODER_from_data(dctx, &inptr, &len); + OSSL_DECODER_CTX_free(dctx); + return ok ? pkey : NULL; +} +#else +static EVP_PKEY * +decode_dh_params(const krb5_data *params_der) +{ + const uint8_t *p = (uint8_t *)params_der->data; + DH *dh; + EVP_PKEY *pkey; + + dh = d2i_DHxparams(NULL, &p, params_der->length); + pkey = dh_to_pkey(&dh); + DH_free(dh); + return pkey; +} +#endif + +static krb5_error_code +encode_spki(EVP_PKEY *pkey, krb5_data *spki_out) +{ + krb5_error_code ret = ENOMEM; + int len; + uint8_t *outptr; + + len = i2d_PUBKEY(pkey, NULL); + ret = alloc_data(spki_out, len); + if (ret) + goto cleanup; + outptr = (uint8_t *)spki_out->data; + (void)i2d_PUBKEY(pkey, &outptr); + +cleanup: + return ret; +} + +static EVP_PKEY * +decode_spki(const krb5_data *spki) +{ + const uint8_t *inptr = (uint8_t *)spki->data; + + return d2i_PUBKEY(NULL, &inptr, spki->length); +} + +#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + +/* + * OpenSSL 1.0 has no DHX support, so we need a custom decoder for RFC 3279 + * DomainParameters, and we need to use X509_PUBKEY values to marshal + * SubjectPublicKeyInfo. + */ + +typedef struct { + ASN1_BIT_STRING *seed; + BIGNUM *counter; +} int_dhvparams; + +typedef struct { + BIGNUM *p; + BIGNUM *q; + BIGNUM *g; + BIGNUM *j; + int_dhvparams *vparams; +} int_dhxparams; + +ASN1_SEQUENCE(int_dhvparams) = { + ASN1_SIMPLE(int_dhvparams, seed, ASN1_BIT_STRING), + ASN1_SIMPLE(int_dhvparams, counter, BIGNUM) +} ASN1_SEQUENCE_END(int_dhvparams); + +ASN1_SEQUENCE(int_dhxparams) = { + ASN1_SIMPLE(int_dhxparams, p, BIGNUM), + ASN1_SIMPLE(int_dhxparams, g, BIGNUM), + ASN1_SIMPLE(int_dhxparams, q, BIGNUM), + ASN1_OPT(int_dhxparams, j, BIGNUM), + ASN1_OPT(int_dhxparams, vparams, int_dhvparams) +} ASN1_SEQUENCE_END(int_dhxparams); + +static EVP_PKEY * +decode_dh_params(const krb5_data *params_der) +{ + int_dhxparams *params; + DH *dh; + EVP_PKEY *pkey; + const uint8_t *p; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + p = (uint8_t *)params_der->data; + params = (int_dhxparams *)ASN1_item_d2i(NULL, &p, params_der->length, + ASN1_ITEM_rptr(int_dhxparams)); + if (params == NULL) { + DH_free(dh); + return NULL; + } + + /* Steal p, q, and g from dhparams for dh. Ignore j and vparams. */ + dh->p = params->p; + dh->q = params->q; + dh->g = params->g; + params->p = params->q = params->g = NULL; + ASN1_item_free((ASN1_VALUE *)params, ASN1_ITEM_rptr(int_dhxparams)); + pkey = dh_to_pkey(&dh); + DH_free(dh); + return pkey; +} + +static krb5_error_code +encode_spki(EVP_PKEY *pkey, krb5_data *spki_out) +{ + krb5_error_code ret = ENOMEM; + const DH *dh; + uint8_t *param_der = NULL, *pubkey_der = NULL, *outptr; + int param_der_len, pubkey_der_len, len; + X509_PUBKEY pubkey; + int_dhxparams dhxparams; + X509_ALGOR algor; + ASN1_OBJECT algorithm; + ASN1_TYPE parameter; + ASN1_STRING param_str, pubkey_str; + + dh = EVP_PKEY_get0_DH(pkey); + if (dh == NULL) + goto cleanup; + + dhxparams.p = dh->p; + dhxparams.q = dh->q; + dhxparams.g = dh->g; + dhxparams.j = NULL; + dhxparams.vparams = NULL; + param_der_len = ASN1_item_i2d((ASN1_VALUE *)&dhxparams, ¶m_der, + ASN1_ITEM_rptr(int_dhxparams)); + if (param_der_len < 0) + goto cleanup; + param_str.length = param_der_len; + param_str.type = V_ASN1_SEQUENCE; + param_str.data = param_der; + param_str.flags = 0; + parameter.type = V_ASN1_SEQUENCE; + parameter.value.sequence = ¶m_str; + + memset(&algorithm, 0, sizeof(algorithm)); + algorithm.data = (uint8_t *)dh_oid.data; + algorithm.length = dh_oid.length; + + algor.algorithm = &algorithm; + algor.parameter = ¶meter; + + if (!encode_bn_der(dh->pub_key, &pubkey_der, &pubkey_der_len)) + goto cleanup; + pubkey_str.length = pubkey_der_len; + pubkey_str.type = V_ASN1_BIT_STRING; + pubkey_str.data = pubkey_der; + pubkey_str.flags = ASN1_STRING_FLAG_BITS_LEFT; + + pubkey.algor = &algor; + pubkey.public_key = &pubkey_str; + len = i2d_X509_PUBKEY(&pubkey, NULL); + if (len < 0) + goto cleanup; + ret = alloc_data(spki_out, len); + if (ret) + goto cleanup; + outptr = (uint8_t *)spki_out->data; + i2d_X509_PUBKEY(&pubkey, &outptr); + +cleanup: + OPENSSL_free(param_der); + free(pubkey_der); + return ret; +} + +static EVP_PKEY * +decode_spki(const krb5_data *spki) +{ + X509_PUBKEY *pubkey = NULL; + const uint8_t *inptr; + DH *dh; + EVP_PKEY *pkey = NULL, *pkey_ret = NULL; + const ASN1_STRING *params; + const ASN1_BIT_STRING *public_key; + krb5_data d; + + inptr = (uint8_t *)spki->data; + pubkey = d2i_X509_PUBKEY(NULL, &inptr, spki->length); + if (pubkey == NULL) + goto cleanup; + + if (pubkey->algor->parameter->type != V_ASN1_SEQUENCE) + goto cleanup; + params = pubkey->algor->parameter->value.sequence; + d = make_data(params->data, params->length); + pkey = decode_dh_params(&d); + if (pkey == NULL) + goto cleanup; + dh = EVP_PKEY_get0_DH(pkey); + if (dh == NULL) + goto cleanup; + public_key = pubkey->public_key; + dh->pub_key = decode_bn_der(public_key->data, public_key->length); + if (dh->pub_key == NULL) + goto cleanup; + + pkey_ret = pkey; + pkey = NULL; + +cleanup: + X509_PUBKEY_free(pubkey); + EVP_PKEY_free(pkey); + return pkey_ret; +} + +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + +/* Attempt to specify padded Diffie-Hellman result derivation. Don't error out + * if this fails since we also detect short results and adjust them. */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +static void +set_padded_derivation(EVP_PKEY_CTX *ctx) +{ + EVP_PKEY_CTX_set_dh_pad(ctx, 1); +} +#elif OPENSSL_VERSION_NUMBER >= 0x10100000L +static void +set_padded_derivation(EVP_PKEY_CTX *ctx) +{ + /* We would use EVP_PKEY_CTX_set_dh_pad() but it doesn't work with DHX. */ + EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, + EVP_PKEY_CTRL_DH_PAD, 1, NULL); +} +#else +static void +set_padded_derivation(EVP_PKEY_CTX *ctx) +{ + /* There's no support for padded derivation in 1.0. */ +} +#endif + +static int +dh_result(EVP_PKEY *pkey, EVP_PKEY *peer, + uint8_t **result_out, unsigned int *len_out) +{ + EVP_PKEY_CTX *derive_ctx = NULL; + int ok = 0; + uint8_t *buf = NULL; + size_t len, dh_size = EVP_PKEY_get_size(pkey); + + *result_out = NULL; + *len_out = 0; + + derive_ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (derive_ctx == NULL) + goto cleanup; + if (EVP_PKEY_derive_init(derive_ctx) <= 0) + goto cleanup; + set_padded_derivation(derive_ctx); + if (EVP_PKEY_derive_set_peer(derive_ctx, peer) <= 0) + goto cleanup; + + buf = malloc(dh_size); + if (buf == NULL) + goto cleanup; + len = dh_size; + if (EVP_PKEY_derive(derive_ctx, buf, &len) <= 0) + goto cleanup; + if (len < dh_size) { /* only possible without padded derivation */ + memmove(buf + (dh_size - len), buf, len); + memset(buf, 0, dh_size - len); + } + + ok = 1; + *result_out = buf; + *len_out = dh_size; + buf = NULL; + +cleanup: + EVP_PKEY_CTX_free(derive_ctx); + free(buf); + return ok; +} + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +static int +dh_pubkey_der(EVP_PKEY *pkey, uint8_t **pubkey_out, unsigned int *len_out) +{ + BIGNUM *pubkey_bn = NULL; + int len, ok; + uint8_t *buf; + + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pubkey_bn)) + return 0; + ok = encode_bn_der(pubkey_bn, &buf, &len); + BN_free(pubkey_bn); + if (ok) { + *pubkey_out = buf; + *len_out = len; + } + return ok; +} +#else +static int +dh_pubkey_der(EVP_PKEY *pkey, uint8_t **pubkey_out, unsigned int *len_out) +{ + const DH *dh; + const BIGNUM *pubkey_bn; + uint8_t *buf; + int len; + + dh = EVP_PKEY_get0_DH(pkey); + if (dh == NULL) + return 0; + DH_get0_key(dh, &pubkey_bn, NULL); + if (!encode_bn_der(pubkey_bn, &buf, &len)) + return 0; + *pubkey_out = buf; + *len_out = len; + return 1; +} +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +/* OpenSSL 1.1 and later will copy the q parameter when generating keys. */ +static int +copy_q_openssl10(EVP_PKEY *src, EVP_PKEY *dest) +{ + return 1; +} +#else +/* OpenSSL 1.0 won't copy the q parameter, so we have to do it. */ +static int +copy_q_openssl10(EVP_PKEY *src, EVP_PKEY *dest) +{ + DH *dhsrc = EVP_PKEY_get0_DH(src), *dhdest = EVP_PKEY_get0_DH(dest); + + if (dhsrc == NULL || dhsrc->q == NULL || dhdest == NULL) + return 0; + if (dhdest->q != NULL) + return 1; + dhdest->q = BN_dup(dhsrc->q); + return dhdest->q != NULL; +} +#endif + +static EVP_PKEY * +generate_dh_pkey(EVP_PKEY *params) +{ + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + + ctx = EVP_PKEY_CTX_new(params, NULL); + if (ctx == NULL) + goto cleanup; + if (EVP_PKEY_keygen_init(ctx) <= 0) + goto cleanup; + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) + goto cleanup; + if (!copy_q_openssl10(params, pkey)) { + EVP_PKEY_free(pkey); + pkey = NULL; + } + +cleanup: + EVP_PKEY_CTX_free(ctx); + return pkey; +} + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +static EVP_PKEY * +compose_dh_pkey(EVP_PKEY *params, const uint8_t *pubkey_der, size_t der_len) +{ + EVP_PKEY *pkey = NULL, *pkey_ret = NULL; + BIGNUM *pubkey_bn = NULL; + uint8_t *pubkey_bin = NULL; + int binlen; + + pkey = EVP_PKEY_dup(params); + if (pkey == NULL) + goto cleanup; + + pubkey_bn = decode_bn_der(pubkey_der, der_len); + if (pubkey_bn == NULL) + goto cleanup; + binlen = EVP_PKEY_get_size(pkey); + pubkey_bin = malloc(binlen); + if (pubkey_bin == NULL) + goto cleanup; + if (BN_bn2binpad(pubkey_bn, pubkey_bin, binlen) != binlen) + goto cleanup; + if (EVP_PKEY_set1_encoded_public_key(pkey, pubkey_bin, binlen) != 1) + goto cleanup; + + pkey_ret = pkey; + pkey = NULL; + +cleanup: + EVP_PKEY_free(pkey); + BN_free(pubkey_bn); + free(pubkey_bin); + return pkey_ret; +} + +#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +static DH * +dup_dh_params(DH *src) +{ + return DHparams_dup(src); +} +#else +/* DHparams_dup() won't copy q in OpenSSL 1.0. */ +static DH * +dup_dh_params(DH *src) +{ + DH *dh; + + dh = DH_new(); + if (dh == NULL) + return NULL; + dh->p = BN_dup(src->p); + dh->q = BN_dup(src->q); + dh->g = BN_dup(src->g); + if (dh->p == NULL || dh->q == NULL || dh->g == NULL) { + DH_free(dh); + return NULL; + } + return dh; +} +#endif + +static EVP_PKEY * +compose_dh_pkey(EVP_PKEY *params, const uint8_t *pubkey_der, size_t der_len) +{ + DH *dhparams, *dh = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *pubkey_bn = NULL; + + pubkey_bn = decode_bn_der(pubkey_der, der_len); + if (pubkey_bn == NULL) + goto cleanup; + + dhparams = EVP_PKEY_get0_DH(params); + if (dhparams == NULL) + goto cleanup; + dh = dup_dh_params(dhparams); + if (dh == NULL) + goto cleanup; + if (!DH_set0_key(dh, pubkey_bn, NULL)) + goto cleanup; + pubkey_bn = NULL; + + pkey = dh_to_pkey(&dh); + +cleanup: + BN_free(pubkey_bn); + DH_free(dh); + return pkey; +} + +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + static struct pkcs11_errstrings { short code; char *text; @@ -339,128 +883,6 @@ static struct pkcs11_errstrings { { -1, NULL } }; -/* DH parameters */ -static uint8_t oakley_1024[128] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, - 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, - 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, - 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, - 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, - 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, - 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, - 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; - -static uint8_t oakley_2048[2048/8] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, - 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, - 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, - 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, - 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, - 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, - 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, - 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, - 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, - 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, - 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, - 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, - 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, - 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, - 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, - 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, - 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, - 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, - 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, - 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, - 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, - 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, - 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, - 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; - -static uint8_t oakley_4096[4096/8] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, - 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, - 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, - 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, - 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, - 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, - 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, - 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, - 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, - 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, - 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, - 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, - 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, - 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, - 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, - 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, - 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, - 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, - 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, - 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, - 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, - 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, - 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, - 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, - 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, - 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, - 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, - 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, - 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, - 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, - 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, - 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, - 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, - 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, - 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, - 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, - 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, - 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, - 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, - 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, - 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, - 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, - 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, - 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, - 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, - 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, - 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, - 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, - 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, - 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, - 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, - 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, - 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, - 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, - 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, - 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; - MAKE_INIT_FUNCTION(pkinit_openssl_init); static krb5_error_code oerr(krb5_context context, krb5_error_code code, @@ -631,7 +1053,8 @@ pkinit_init_req_crypto(pkinit_req_crypto_context *cryptoctx) goto out; memset(ctx, 0, sizeof(*ctx)); - ctx->dh = NULL; + ctx->client_pkey = NULL; + ctx->received_params = NULL; ctx->received_cert = NULL; *cryptoctx = ctx; @@ -652,10 +1075,9 @@ pkinit_fini_req_crypto(pkinit_req_crypto_context req_cryptoctx) return; pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, req_cryptoctx); - if (req_cryptoctx->dh != NULL) - DH_free(req_cryptoctx->dh); - if (req_cryptoctx->received_cert != NULL) - X509_free(req_cryptoctx->received_cert); + EVP_PKEY_free(req_cryptoctx->client_pkey); + EVP_PKEY_free(req_cryptoctx->received_params); + X509_free(req_cryptoctx->received_cert); free(req_cryptoctx); } @@ -856,54 +1278,20 @@ pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ctx) ASN1_OBJECT_free(ctx->id_kp_serverAuth); } -/* Construct an OpenSSL DH object for an Oakley group. */ -static DH * -make_oakley_dh(uint8_t *prime, size_t len) -{ - DH *dh = NULL; - BIGNUM *p = NULL, *q = NULL, *g = NULL; - - p = BN_bin2bn(prime, len, NULL); - if (p == NULL) - goto cleanup; - q = BN_new(); - if (q == NULL) - goto cleanup; - if (!BN_rshift1(q, p)) - goto cleanup; - g = BN_new(); - if (g == NULL) - goto cleanup; - if (!BN_set_word(g, DH_GENERATOR_2)) - goto cleanup; - - dh = DH_new(); - if (dh == NULL) - goto cleanup; - DH_set0_pqg(dh, p, q, g); - p = g = q = NULL; - -cleanup: - BN_free(p); - BN_free(q); - BN_free(g); - return dh; -} - static krb5_error_code pkinit_init_dh_params(pkinit_plg_crypto_context plgctx) { krb5_error_code retval = ENOMEM; - plgctx->dh_1024 = make_oakley_dh(oakley_1024, sizeof(oakley_1024)); + plgctx->dh_1024 = decode_dh_params(&oakley_1024); if (plgctx->dh_1024 == NULL) goto cleanup; - plgctx->dh_2048 = make_oakley_dh(oakley_2048, sizeof(oakley_2048)); + plgctx->dh_2048 = decode_dh_params(&oakley_2048); if (plgctx->dh_2048 == NULL) goto cleanup; - plgctx->dh_4096 = make_oakley_dh(oakley_4096, sizeof(oakley_4096)); + plgctx->dh_4096 = decode_dh_params(&oakley_4096); if (plgctx->dh_4096 == NULL) goto cleanup; @@ -919,13 +1307,9 @@ cleanup: static void pkinit_fini_dh_params(pkinit_plg_crypto_context plgctx) { - if (plgctx->dh_1024 != NULL) - DH_free(plgctx->dh_1024); - if (plgctx->dh_2048 != NULL) - DH_free(plgctx->dh_2048); - if (plgctx->dh_4096 != NULL) - DH_free(plgctx->dh_4096); - + EVP_PKEY_free(plgctx->dh_1024); + EVP_PKEY_free(plgctx->dh_2048); + EVP_PKEY_free(plgctx->dh_4096); plgctx->dh_1024 = plgctx->dh_2048 = plgctx->dh_4096 = NULL; } @@ -1008,7 +1392,7 @@ pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx) ctx->p11 = NULL; } if (ctx->p11_module != NULL) { - pkinit_C_UnloadModule(ctx->p11_module); + krb5int_close_plugin(ctx->p11_module); ctx->p11_module = NULL; } free(ctx->p11_module_name); @@ -1052,17 +1436,11 @@ create_contentinfo(krb5_context context, ASN1_OBJECT *oid, if (p7->type == NULL) goto oom; - if (OBJ_obj2nid(oid) == NID_pkcs7_data) { - /* Draft 9 uses id-pkcs7-data for signed data. For this type OpenSSL - * expects an octet string in d.data. */ - p7->d.data = ostr; - } else { - p7->d.other = ASN1_TYPE_new(); - if (p7->d.other == NULL) - goto oom; - p7->d.other->type = V_ASN1_OCTET_STRING; - p7->d.other->value.octet_string = ostr; - } + p7->d.other = ASN1_TYPE_new(); + if (p7->d.other == NULL) + goto oom; + p7->d.other->type = V_ASN1_OCTET_STRING; + p7->d.other->value.octet_string = ostr; *p7_out = p7; return 0; @@ -1126,7 +1504,6 @@ cms_signeddata_create(krb5_context context, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int cms_msg_type, - int include_certchain, unsigned char *data, unsigned int data_len, unsigned char **signed_data, @@ -1171,50 +1548,46 @@ cms_signeddata_create(krb5_context context, goto cleanup; if (id_cryptoctx->my_certs != NULL) { - /* create a cert chain that has at least the signer's certificate */ + X509_STORE *certstore = NULL; + X509_STORE_CTX *certctx; + STACK_OF(X509) *certstack = NULL; + char buf[DN_BUF_LEN]; + unsigned int i = 0, size = 0; + + /* create a cert chain */ if ((cert_stack = sk_X509_new_null()) == NULL) goto cleanup; cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index); - if (!include_certchain) { - pkiDebug("only including signer's certificate\n"); - sk_X509_push(cert_stack, X509_dup(cert)); - } else { - /* create a cert chain */ - X509_STORE *certstore = NULL; - X509_STORE_CTX *certctx; - STACK_OF(X509) *certstack = NULL; - char buf[DN_BUF_LEN]; - unsigned int i = 0, size = 0; - if ((certstore = X509_STORE_new()) == NULL) - goto cleanup; - pkiDebug("building certificate chain\n"); - X509_STORE_set_verify_cb(certstore, openssl_callback); - certctx = X509_STORE_CTX_new(); - if (certctx == NULL) - goto cleanup; - X509_STORE_CTX_init(certctx, certstore, cert, - id_cryptoctx->intermediateCAs); - X509_STORE_CTX_trusted_stack(certctx, id_cryptoctx->trustedCAs); - if (!X509_verify_cert(certctx)) { - retval = oerr_cert(context, 0, certctx, - _("Failed to verify own certificate")); - goto cleanup; - } - certstack = X509_STORE_CTX_get1_chain(certctx); - size = sk_X509_num(certstack); - pkiDebug("size of certificate chain = %d\n", size); - for(i = 0; i < size - 1; i++) { - X509 *x = sk_X509_value(certstack, i); - X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); - pkiDebug("cert #%d: %s\n", i, buf); - sk_X509_push(cert_stack, X509_dup(x)); - } - X509_STORE_CTX_free(certctx); - X509_STORE_free(certstore); - sk_X509_pop_free(certstack, X509_free); + certstore = X509_STORE_new(); + if (certstore == NULL) + goto cleanup; + pkiDebug("building certificate chain\n"); + X509_STORE_set_verify_cb(certstore, openssl_callback); + certctx = X509_STORE_CTX_new(); + if (certctx == NULL) + goto cleanup; + X509_STORE_CTX_init(certctx, certstore, cert, + id_cryptoctx->intermediateCAs); + X509_STORE_CTX_trusted_stack(certctx, id_cryptoctx->trustedCAs); + if (!X509_verify_cert(certctx)) { + retval = oerr_cert(context, 0, certctx, + _("Failed to verify own certificate")); + goto cleanup; } + certstack = X509_STORE_CTX_get1_chain(certctx); + size = sk_X509_num(certstack); + for (i = 0; i < size - 1; i++) { + X509 *x = sk_X509_value(certstack, i); + X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); + TRACE_PKINIT_CERT_CHAIN_NAME(context, (int)i, buf); + sk_X509_push(cert_stack, X509_dup(x)); + } + X509_STORE_CTX_free(certctx); + X509_STORE_free(certstore); + sk_X509_pop_free(certstack, X509_free); + p7s->cert = cert_stack; /* fill-in PKCS7_SIGNER_INFO */ @@ -1235,7 +1608,7 @@ cms_signeddata_create(krb5_context context, /* will not fill-out EVP_PKEY because it's on the smartcard */ /* Set digest algs */ - p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1); + p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha256); if (p7si->digest_alg->parameter != NULL) ASN1_TYPE_free(p7si->digest_alg->parameter); @@ -1246,56 +1619,53 @@ cms_signeddata_create(krb5_context context, /* Set sig algs */ if (p7si->digest_enc_alg->parameter != NULL) ASN1_TYPE_free(p7si->digest_enc_alg->parameter); - p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption); + p7si->digest_enc_alg->algorithm = + OBJ_nid2obj(NID_sha256WithRSAEncryption); if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new())) goto cleanup; p7si->digest_enc_alg->parameter->type = V_ASN1_NULL; - if (cms_msg_type == CMS_SIGN_DRAFT9){ - /* don't include signed attributes for pa-type 15 request */ - abuf = data; - alen = data_len; - } else { - /* add signed attributes */ - /* compute sha1 digest over the EncapsulatedContentInfo */ - ctx = EVP_MD_CTX_new(); - if (ctx == NULL) - goto cleanup; - EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); - EVP_DigestUpdate(ctx, data, data_len); - md_tmp = EVP_MD_CTX_md(ctx); - EVP_DigestFinal_ex(ctx, md_data, &md_len); - EVP_MD_CTX_free(ctx); + /* add signed attributes */ + /* compute sha256 digest over the EncapsulatedContentInfo */ + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) + goto cleanup; + EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); + EVP_DigestUpdate(ctx, data, data_len); + md_tmp = EVP_MD_CTX_get0_md(ctx); + EVP_DigestFinal_ex(ctx, md_data, &md_len); + EVP_MD_CTX_free(ctx); - /* create a message digest attr */ - digest_attr = ASN1_OCTET_STRING_new(); - ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len); - PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest, - V_ASN1_OCTET_STRING, (char *) digest_attr); + /* create a message digest attr */ + digest_attr = ASN1_OCTET_STRING_new(); + ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len); + PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest, + V_ASN1_OCTET_STRING, (char *)digest_attr); - /* create a content-type attr */ - oid_copy = OBJ_dup(oid); - if (oid_copy == NULL) - goto cleanup2; - PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, - V_ASN1_OBJECT, oid_copy); + /* create a content-type attr */ + oid_copy = OBJ_dup(oid); + if (oid_copy == NULL) + goto cleanup2; + PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, + V_ASN1_OBJECT, oid_copy); - /* create the signature over signed attributes. get DER encoded value */ - /* This is the place where smartcard signature needs to be calculated */ - sk = p7si->auth_attr; - alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, - ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); - if (abuf == NULL) - goto cleanup2; - } /* signed attributes */ + /* create the signature over signed attributes. get DER encoded value */ + /* This is the place where smartcard signature needs to be calculated */ + sk = p7si->auth_attr; + alen = ASN1_item_i2d((ASN1_VALUE *)sk, &abuf, + ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); + if (abuf == NULL) + goto cleanup2; #ifndef WITHOUT_PKCS11 - /* Some tokens can only do RSAEncryption without sha1 hash */ - /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash - * function and the hash value into an ASN.1 value of type DigestInfo - * DigestInfo::=SEQUENCE { - * digestAlgorithm AlgorithmIdentifier, - * digest OCTET STRING } + /* + * Some tokens can only do RSAEncryption without a hash. To compute + * sha256WithRSAEncryption, encode the algorithm ID for the hash + * function and the hash value into an ASN.1 value of type DigestInfo: + * DigestInfo ::= SEQUENCE { + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING + * } */ if (id_cryptoctx->pkcs11_method == 1 && id_cryptoctx->mech == CKM_RSA_PKCS) { @@ -1303,11 +1673,7 @@ cms_signeddata_create(krb5_context context, ctx = EVP_MD_CTX_new(); if (ctx == NULL) goto cleanup; - /* if this is not draft9 request, include digest signed attribute */ - if (cms_msg_type != CMS_SIGN_DRAFT9) - EVP_DigestInit_ex(ctx, md_tmp, NULL); - else - EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); + EVP_DigestInit_ex(ctx, md_tmp, NULL); EVP_DigestUpdate(ctx, abuf, alen); EVP_DigestFinal_ex(ctx, md_data2, &md_len2); EVP_MD_CTX_free(ctx); @@ -1315,7 +1681,7 @@ cms_signeddata_create(krb5_context context, alg = X509_ALGOR_new(); if (alg == NULL) goto cleanup2; - X509_ALGOR_set0(alg, OBJ_nid2obj(NID_sha1), V_ASN1_NULL, NULL); + X509_ALGOR_set0(alg, OBJ_nid2obj(NID_sha256), V_ASN1_NULL, NULL); alg_len = i2d_X509_ALGOR(alg, NULL); digest = ASN1_OCTET_STRING_new(); @@ -1344,15 +1710,14 @@ cms_signeddata_create(krb5_context context, #endif { pkiDebug("mech = %s\n", - id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS"); + id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA256_RSA_PKCS" : "FS"); retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen, &sig, &sig_len); } #ifdef DEBUG_SIG print_buffer(sig, sig_len); #endif - if (cms_msg_type != CMS_SIGN_DRAFT9 ) - free(abuf); + free(abuf); if (retval) goto cleanup2; @@ -1395,19 +1760,13 @@ cms_signeddata_create(krb5_context context, print_buffer_bin(*signed_data, *signed_data_len, "/tmp/client_pkcs7_signeddata"); } else { - if (cms_msg_type == CMS_SIGN_SERVER) { - print_buffer_bin(*signed_data, *signed_data_len, - "/tmp/kdc_pkcs7_signeddata"); - } else { - print_buffer_bin(*signed_data, *signed_data_len, - "/tmp/draft9_pkcs7_signeddata"); - } + print_buffer_bin(*signed_data, *signed_data_len, + "/tmp/kdc_pkcs7_signeddata"); } #endif cleanup2: if (p7si) { - if (cms_msg_type != CMS_SIGN_DRAFT9) #ifndef WITHOUT_PKCS11 if (id_cryptoctx->pkcs11_method == 1 && id_cryptoctx->mech == CKM_RSA_PKCS) { @@ -1501,7 +1860,7 @@ cms_signeddata_verify(krb5_context context, if (is_signed && !OBJ_cmp(type, oid)) { unsigned char *d; *is_signed = 0; - octets = pkinit_CMS_get0_content_data(cms); + octets = CMS_get0_content(cms); if (!octets || ((*octets)->type != V_ASN1_OCTET_STRING)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, @@ -1556,13 +1915,13 @@ cms_signeddata_verify(krb5_context context, goto cleanup; *is_signed = 0; /* We cannot use CMS_dataInit because there may be no digest */ - octets = pkinit_CMS_get0_content_signed(cms); + octets = CMS_get0_content(cms); if (octets) out = BIO_new_mem_buf((*octets)->data, (*octets)->length); if (out == NULL) goto cleanup; } else { - pkinit_CMS_SignerInfo_get_cert(cms, si, &x); + CMS_SignerInfo_get0_algs(si, NULL, &x, NULL, NULL); if (x == NULL) goto cleanup; @@ -1694,46 +2053,24 @@ cms_signeddata_verify(krb5_context context, #endif } else { /* retrieve verified certificate chain */ - if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9) + if (cms_msg_type == CMS_SIGN_CLIENT) verified_chain = X509_STORE_CTX_get1_chain(cert_ctx); } X509_STORE_CTX_free(cert_ctx); if (i <= 0) goto cleanup; out = BIO_new(BIO_s_mem()); - if (cms_msg_type == CMS_SIGN_DRAFT9) - flags |= CMS_NOATTR; if (CMS_verify(cms, NULL, store, NULL, out, flags) == 0) { - unsigned long err = ERR_peek_error(); - switch(ERR_GET_REASON(err)) { - case PKCS7_R_DIGEST_FAILURE: - retval = KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED; - break; - case PKCS7_R_SIGNATURE_FAILURE: - default: + if (ERR_peek_last_error() == CMS_R_VERIFICATION_FAILURE) retval = KRB5KDC_ERR_INVALID_SIG; - } + else + retval = KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED; (void)oerr(context, retval, _("Failed to verify CMS message")); goto cleanup; } } /* message was signed */ if (!OBJ_cmp(etype, oid)) valid_oid = 1; - else if (cms_msg_type == CMS_SIGN_DRAFT9) { - /* - * Various implementations of the pa-type 15 request use - * different OIDS. We check that the returned object - * has any of the acceptable OIDs - */ - ASN1_OBJECT *client_oid = NULL, *server_oid = NULL, *rsa_oid = NULL; - client_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_CLIENT); - server_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_SERVER); - rsa_oid = pkinit_pkcs7type2oid(plgctx, CMS_ENVEL_SERVER); - if (!OBJ_cmp(etype, client_oid) || - !OBJ_cmp(etype, server_oid) || - !OBJ_cmp(etype, rsa_oid)) - valid_oid = 1; - } if (valid_oid) pkiDebug("CMS Verification successful\n"); @@ -1763,7 +2100,7 @@ cms_signeddata_verify(krb5_context context, reqctx->received_cert = X509_dup(x); /* generate authorization data */ - if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9) { + if (cms_msg_type == CMS_SIGN_CLIENT) { if (authz_data == NULL || authz_data_len == NULL) goto out; @@ -1804,11 +2141,11 @@ cleanup: X509_STORE_free(store); if (cms != NULL) { if (signerCerts != NULL) - pkinit_CMS_free1_certs(signerCerts); + sk_X509_pop_free(signerCerts, X509_free); if (idctx->intermediateCAs != NULL && signerCerts) sk_X509_free(intermediateCAs); if (signerRevoked != NULL) - pkinit_CMS_free1_crls(signerRevoked); + sk_X509_CRL_pop_free(signerRevoked, X509_CRL_free); if (idctx->revoked != NULL && signerRevoked) sk_X509_CRL_free(revoked); CMS_ContentInfo_free(cms); @@ -1829,7 +2166,6 @@ cms_envelopeddata_create(krb5_context context, pkinit_req_crypto_context reqctx, pkinit_identity_crypto_context idctx, krb5_preauthtype pa_type, - int include_certchain, unsigned char *key_pack, unsigned int key_pack_len, unsigned char **out, @@ -1843,24 +2179,11 @@ cms_envelopeddata_create(krb5_context context, int signed_data_len = 0, enc_data_len = 0, flags = PKCS7_BINARY; STACK_OF(X509) *encerts = NULL; const EVP_CIPHER *cipher = NULL; - int cms_msg_type; - - /* create the PKCS7 SignedData portion of the PKCS7 EnvelopedData */ - switch ((int)pa_type) { - case KRB5_PADATA_PK_AS_REQ_OLD: - case KRB5_PADATA_PK_AS_REP_OLD: - cms_msg_type = CMS_SIGN_DRAFT9; - break; - case KRB5_PADATA_PK_AS_REQ: - cms_msg_type = CMS_ENVEL_SERVER; - break; - default: - goto cleanup; - } retval = cms_signeddata_create(context, plgctx, reqctx, idctx, - cms_msg_type, include_certchain, key_pack, key_pack_len, - &signed_data, (unsigned int *)&signed_data_len); + CMS_ENVEL_SERVER, key_pack, key_pack_len, + &signed_data, + (unsigned int *)&signed_data_len); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; @@ -1876,26 +2199,11 @@ cms_envelopeddata_create(krb5_context context, cipher = EVP_des_ede3_cbc(); in = BIO_new(BIO_s_mem()); - switch (pa_type) { - case KRB5_PADATA_PK_AS_REQ: - prepare_enc_data(signed_data, signed_data_len, &enc_data, - &enc_data_len); - retval = BIO_write(in, enc_data, enc_data_len); - if (retval != enc_data_len) { - pkiDebug("BIO_write only wrote %d\n", retval); - goto cleanup; - } - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - retval = BIO_write(in, signed_data, signed_data_len); - if (retval != signed_data_len) { - pkiDebug("BIO_write only wrote %d\n", retval); - goto cleanup; - } - break; - default: - retval = -1; + prepare_enc_data(signed_data, signed_data_len, &enc_data, + &enc_data_len); + retval = BIO_write(in, enc_data, enc_data_len); + if (retval != enc_data_len) { + pkiDebug("BIO_write only wrote %d\n", retval); goto cleanup; } @@ -1904,20 +2212,7 @@ cms_envelopeddata_create(krb5_context context, retval = oerr(context, 0, _("Failed to encrypt PKCS7 object")); goto cleanup; } - switch (pa_type) { - case KRB5_PADATA_PK_AS_REQ: - p7->d.enveloped->enc_data->content_type = - OBJ_nid2obj(NID_pkcs7_signed); - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - p7->d.enveloped->enc_data->content_type = - OBJ_nid2obj(NID_pkcs7_data); - break; - break; - break; - break; - } + p7->d.enveloped->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_signed); *out_len = i2d_PKCS7(p7, NULL); if (!*out_len || (p = *out = malloc(*out_len)) == NULL) { @@ -1962,13 +2257,9 @@ cms_envelopeddata_verify(krb5_context context, { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; PKCS7 *p7 = NULL; - BIO *out = NULL; - int i = 0; - unsigned int size = 0; const unsigned char *p = enveloped_data; unsigned int tmp_buf_len = 0, tmp_buf2_len = 0, vfy_buf_len = 0; unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL, *vfy_buf = NULL; - int msg_type = 0; #ifdef DEBUG_ASN1 print_buffer_bin(enveloped_data, enveloped_data_len, @@ -1989,70 +2280,32 @@ cms_envelopeddata_verify(krb5_context context, } /* decrypt received PKCS7 message */ - out = BIO_new(BIO_s_mem()); - if (pkcs7_decrypt(context, id_cryptoctx, p7, out)) { + if (pkcs7_decrypt(context, id_cryptoctx, p7, &tmp_buf, &tmp_buf_len)) { pkiDebug("PKCS7 decryption successful\n"); } else { retval = oerr(context, 0, _("Failed to decrypt PKCS7 message")); goto cleanup; } - /* transfer the decoded PKCS7 SignedData message into a separate buffer */ - for (;;) { - if ((tmp_buf = realloc(tmp_buf, size + 1024 * 10)) == NULL) - goto cleanup; - i = BIO_read(out, &(tmp_buf[size]), 1024 * 10); - if (i <= 0) - break; - else - size += i; - } - tmp_buf_len = size; - #ifdef DEBUG_ASN1 print_buffer_bin(tmp_buf, tmp_buf_len, "/tmp/client_enc_keypack"); #endif /* verify PKCS7 SignedData message */ - switch (pa_type) { - case KRB5_PADATA_PK_AS_REP: - msg_type = CMS_ENVEL_SERVER; - - break; - case KRB5_PADATA_PK_AS_REP_OLD: - msg_type = CMS_SIGN_DRAFT9; - break; - default: - pkiDebug("%s: unrecognized pa_type = %d\n", __FUNCTION__, pa_type); - retval = KRB5KDC_ERR_PREAUTH_FAILED; + /* Wrap the signed data to make decoding easier in the verify routine. */ + retval = wrap_signeddata(tmp_buf, tmp_buf_len, &tmp_buf2, &tmp_buf2_len); + if (retval) { + pkiDebug("failed to encode signeddata\n"); goto cleanup; } - /* - * If this is the RFC style, wrap the signed data to make - * decoding easier in the verify routine. - * For draft9-compatible, we don't do anything because it - * is already wrapped. - */ - if (msg_type == CMS_ENVEL_SERVER) { - retval = wrap_signeddata(tmp_buf, tmp_buf_len, - &tmp_buf2, &tmp_buf2_len); - if (retval) { - pkiDebug("failed to encode signeddata\n"); - goto cleanup; - } - vfy_buf = tmp_buf2; - vfy_buf_len = tmp_buf2_len; - - } else { - vfy_buf = tmp_buf; - vfy_buf_len = tmp_buf_len; - } + vfy_buf = tmp_buf2; + vfy_buf_len = tmp_buf2_len; #ifdef DEBUG_ASN1 print_buffer_bin(vfy_buf, vfy_buf_len, "/tmp/client_enc_keypack2"); #endif retval = cms_signeddata_verify(context, plg_cryptoctx, req_cryptoctx, - id_cryptoctx, msg_type, + id_cryptoctx, CMS_ENVEL_SERVER, require_crl_checking, vfy_buf, vfy_buf_len, data, data_len, NULL, NULL, NULL); @@ -2070,8 +2323,6 @@ cleanup: if (p7 != NULL) PKCS7_free(p7); - if (out != NULL) - BIO_free(out); free(tmp_buf); free(tmp_buf2); @@ -2083,17 +2334,16 @@ crypto_retrieve_X509_sans(krb5_context context, pkinit_plg_crypto_context plgctx, pkinit_req_crypto_context reqctx, X509 *cert, - krb5_principal **princs_ret, - krb5_principal **upn_ret, + krb5_principal **princs_ret, char ***upn_ret, unsigned char ***dns_ret) { krb5_error_code retval = EINVAL; char buf[DN_BUF_LEN]; int p = 0, u = 0, d = 0, ret = 0, l; krb5_principal *princs = NULL; - krb5_principal *upns = NULL; + char **upns = NULL; unsigned char **dnss = NULL; - unsigned int i, num_found = 0, num_sans = 0; + unsigned int i, num_sans = 0; X509_EXTENSION *ext = NULL; GENERAL_NAMES *ialt = NULL; GENERAL_NAME *gen = NULL; @@ -2117,21 +2367,17 @@ crypto_retrieve_X509_sans(krb5_context context, X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); - pkiDebug("%s: looking for SANs in cert = %s\n", __FUNCTION__, buf); l = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); if (l < 0) return 0; if (!(ext = X509_get_ext(cert, l)) || !(ialt = X509V3_EXT_d2i(ext))) { - pkiDebug("%s: found no subject alt name extensions\n", __FUNCTION__); + TRACE_PKINIT_SAN_CERT_NONE(context, buf); goto cleanup; } num_sans = sk_GENERAL_NAME_num(ialt); - pkiDebug("%s: found %d subject alt name extension(s)\n", __FUNCTION__, - num_sans); - /* OK, we're likely returning something. Allocate return values */ if (princs_ret != NULL) { princs = calloc(num_sans + 1, sizeof(krb5_principal)); @@ -2141,7 +2387,7 @@ crypto_retrieve_X509_sans(krb5_context context, } } if (upn_ret != NULL) { - upns = calloc(num_sans + 1, sizeof(krb5_principal)); + upns = calloc(num_sans + 1, sizeof(*upns)); if (upns == NULL) { retval = ENOMEM; goto cleanup; @@ -2176,7 +2422,6 @@ crypto_retrieve_X509_sans(krb5_context context, __FUNCTION__); } else { p++; - num_found++; } } else if (upns != NULL && OBJ_cmp(plgctx->id_ms_san_upn, @@ -2184,16 +2429,10 @@ crypto_retrieve_X509_sans(krb5_context context, /* Prevent abuse of embedded null characters. */ if (memchr(name.data, '\0', name.length)) break; - ret = krb5_parse_name_flags(context, name.data, - KRB5_PRINCIPAL_PARSE_ENTERPRISE, - &upns[u]); - if (ret) { - pkiDebug("%s: failed parsing ms-upn san value\n", - __FUNCTION__); - } else { - u++; - num_found++; - } + upns[u] = k5memdup0(name.data, name.length, &ret); + if (upns[u] == NULL) + goto cleanup; + u++; } else { pkiDebug("%s: unrecognized othername oid in SAN\n", __FUNCTION__); @@ -2215,7 +2454,6 @@ crypto_retrieve_X509_sans(krb5_context context, __FUNCTION__); } else { d++; - num_found++; } } break; @@ -2226,6 +2464,8 @@ crypto_retrieve_X509_sans(krb5_context context, } sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free); + TRACE_PKINIT_SAN_CERT_COUNT(context, (int)num_sans, p, u, d, buf); + retval = 0; if (princs != NULL && *princs != NULL) { *princs_ret = princs; @@ -2245,7 +2485,7 @@ cleanup: krb5_free_principal(context, princs[i]); free(princs); for (i = 0; upns != NULL && upns[i] != NULL; i++) - krb5_free_principal(context, upns[i]); + free(upns[i]); free(upns); for (i = 0; dnss != NULL && dnss[i] != NULL; i++) free(dnss[i]); @@ -2269,8 +2509,7 @@ crypto_retrieve_cert_sans(krb5_context context, pkinit_plg_crypto_context plgctx, pkinit_req_crypto_context reqctx, pkinit_identity_crypto_context idctx, - krb5_principal **princs_ret, - krb5_principal **upn_ret, + krb5_principal **princs_ret, char ***upn_ret, unsigned char ***dns_ret) { krb5_error_code retval = EINVAL; @@ -2374,22 +2613,28 @@ pkinit_octetstring2key(krb5_context context, unsigned char counter; size_t keybytes, keylength, offset; krb5_data random_data; + EVP_MD_CTX *sha1_ctx = NULL; - if ((buf = malloc(dh_key_len)) == NULL) { - retval = ENOMEM; + buf = k5alloc(dh_key_len, &retval); + if (buf == NULL) + goto cleanup; + + sha1_ctx = EVP_MD_CTX_new(); + if (sha1_ctx == NULL) { + retval = KRB5_CRYPTO_INTERNAL; goto cleanup; } - memset(buf, 0, dh_key_len); counter = 0; offset = 0; do { - SHA_CTX c; - - SHA1_Init(&c); - SHA1_Update(&c, &counter, 1); - SHA1_Update(&c, key, dh_key_len); - SHA1_Final(md, &c); + if (!EVP_DigestInit(sha1_ctx, EVP_sha1()) || + !EVP_DigestUpdate(sha1_ctx, &counter, 1) || + !EVP_DigestUpdate(sha1_ctx, key, dh_key_len) || + !EVP_DigestFinal(sha1_ctx, md, NULL)) { + retval = KRB5_CRYPTO_INTERNAL; + goto cleanup; + } if (dh_key_len - offset < sizeof(md)) memcpy(buf + offset, md, dh_key_len - offset); @@ -2408,11 +2653,9 @@ pkinit_octetstring2key(krb5_context context, goto cleanup; key_block->length = keylength; - key_block->contents = malloc(keylength); - if (key_block->contents == NULL) { - retval = ENOMEM; + key_block->contents = k5alloc(keylength, &retval); + if (key_block->contents == NULL) goto cleanup; - } random_data.length = keybytes; random_data.data = (char *)buf; @@ -2420,6 +2663,7 @@ pkinit_octetstring2key(krb5_context context, retval = krb5_c_random_to_key(context, etype, &random_data, key_block); cleanup: + EVP_MD_CTX_free(sha1_ctx); free(buf); /* If this is an error return, free the allocated keyblock, if any */ if (retval) { @@ -2430,231 +2674,217 @@ cleanup: } -/** - * Given an algorithm_identifier, this function returns the hash length - * and EVP function associated with that algorithm. - */ -static krb5_error_code -pkinit_alg_values(krb5_context context, - const krb5_data *alg_id, - size_t *hash_bytes, - const EVP_MD *(**func)(void)) +/* Return the OpenSSL descriptor for the given RFC 5652 OID specified in RFC + * 8636. RFC 8636 defines a SHA384 variant, but we don't use it. */ +static const EVP_MD * +algid_to_md(const krb5_data *alg_id) { - *hash_bytes = 0; - *func = NULL; - if ((alg_id->length == krb5_pkinit_sha1_oid_len) && - (0 == memcmp(alg_id->data, &krb5_pkinit_sha1_oid, - krb5_pkinit_sha1_oid_len))) { - *hash_bytes = 20; - *func = &EVP_sha1; - return 0; - } else if ((alg_id->length == krb5_pkinit_sha256_oid_len) && - (0 == memcmp(alg_id->data, krb5_pkinit_sha256_oid, - krb5_pkinit_sha256_oid_len))) { - *hash_bytes = 32; - *func = &EVP_sha256; - return 0; - } else if ((alg_id->length == krb5_pkinit_sha512_oid_len) && - (0 == memcmp(alg_id->data, krb5_pkinit_sha512_oid, - krb5_pkinit_sha512_oid_len))) { - *hash_bytes = 64; - *func = &EVP_sha512; - return 0; - } else { - krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS, - "Bad algorithm ID passed to PK-INIT KDF."); - return KRB5_ERR_BAD_S2K_PARAMS; - } -} /* pkinit_alg_values() */ + if (data_eq(*alg_id, sha1_id)) + return EVP_sha1(); + if (data_eq(*alg_id, sha256_id)) + return EVP_sha256(); + if (data_eq(*alg_id, sha512_id)) + return EVP_sha512(); + return NULL; +} +#if OPENSSL_VERSION_NUMBER >= 0x30000000L -/* pkinit_alg_agility_kdf() -- - * This function generates a key using the KDF described in - * draft_ietf_krb_wg_pkinit_alg_agility-04.txt. The algorithm is - * described as follows: - * - * 1. reps = keydatalen (K) / hash length (H) - * - * 2. Initialize a 32-bit, big-endian bit string counter as 1. - * - * 3. For i = 1 to reps by 1, do the following: - * - * - Compute Hashi = H(counter || Z || OtherInfo). - * - * - Increment counter (modulo 2^32) - * - * 4. Set key = Hash1 || Hash2 || ... so that length of key is K bytes. - */ -krb5_error_code -pkinit_alg_agility_kdf(krb5_context context, - krb5_data *secret, - krb5_data *alg_oid, - krb5_const_principal party_u_info, - krb5_const_principal party_v_info, - krb5_enctype enctype, - krb5_data *as_req, - krb5_data *pk_as_rep, - krb5_keyblock *key_block) +#define sskdf openssl_sskdf +static krb5_error_code +openssl_sskdf(krb5_context context, const EVP_MD *md, krb5_data *key, + krb5_data *info, size_t len, krb5_data *out) { - krb5_error_code retval = 0; - - unsigned int reps = 0; - uint32_t counter = 1; /* Does this type work on Windows? */ - size_t offset = 0; - size_t hash_len = 0; - size_t rand_len = 0; - size_t key_len = 0; - krb5_data random_data; - krb5_sp80056a_other_info other_info_fields; - krb5_pkinit_supp_pub_info supp_pub_info_fields; - krb5_data *other_info = NULL; - krb5_data *supp_pub_info = NULL; - krb5_algorithm_identifier alg_id; - EVP_MD_CTX *ctx = NULL; - const EVP_MD *(*EVP_func)(void); + krb5_error_code ret; + EVP_KDF *kdf = NULL; + EVP_KDF_CTX *kctx = NULL; + OSSL_PARAM params[4], *p = params; - /* initialize random_data here to make clean-up safe */ - random_data.length = 0; - random_data.data = NULL; + ret = alloc_data(out, len); + if (ret) + goto cleanup; - /* allocate and initialize the key block */ - key_block->magic = 0; - key_block->enctype = enctype; - if (0 != (retval = krb5_c_keylengths(context, enctype, &rand_len, - &key_len))) + kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL); + if (kdf == NULL) { + ret = oerr(context, KRB5_CRYPTO_INTERNAL, _("Failed to fetch SSKDF")); goto cleanup; + } - random_data.length = rand_len; - key_block->length = key_len; + kctx = EVP_KDF_CTX_new(kdf); + if (!kctx) { + ret = oerr(context, KRB5_CRYPTO_INTERNAL, + _("Failed to instantiate SSKDF")); + goto cleanup; + } - if (NULL == (key_block->contents = malloc(key_block->length))) { - retval = ENOMEM; + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(md), 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, + key->data, key->length); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, + info->data, info->length); + *p = OSSL_PARAM_construct_end(); + if (EVP_KDF_derive(kctx, (uint8_t *)out->data, len, params) <= 0) { + ret = oerr(context, KRB5_CRYPTO_INTERNAL, + _("Failed to derive key using SSKDF")); goto cleanup; } - memset (key_block->contents, 0, key_block->length); + ret = 0; - /* If this is anonymous pkinit, use the anonymous principle for party_u_info */ - if (party_u_info && krb5_principal_compare_any_realm(context, party_u_info, - krb5_anonymous_principal())) - party_u_info = (krb5_principal)krb5_anonymous_principal(); +cleanup: + EVP_KDF_free(kdf); + EVP_KDF_CTX_free(kctx); + return ret; +} - if (0 != (retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func))) - goto cleanup; +#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + +#define sskdf builtin_sskdf +static krb5_error_code +builtin_sskdf(krb5_context context, const EVP_MD *md, krb5_data *key, + krb5_data *info, size_t len, krb5_data *out) +{ + krb5_error_code ret; + uint32_t counter = 1, reps; + uint8_t be_counter[4], *outptr; + EVP_MD_CTX *ctx = NULL; + unsigned int s, hash_len; - /* 1. reps = keydatalen (K) / hash length (H) */ - reps = key_block->length/hash_len; + hash_len = EVP_MD_size(md); - /* ... and round up, if necessary */ - if (key_block->length > (reps * hash_len)) - reps++; + /* 1. reps = keydatalen (K) / hash length (H) rounded up. */ + reps = (len + hash_len - 1) / hash_len; /* Allocate enough space in the random data buffer to hash directly into * it, even if the last hash will make it bigger than the key length. */ - if (NULL == (random_data.data = malloc(reps * hash_len))) { - retval = ENOMEM; - goto cleanup; - } - - /* Encode the ASN.1 octet string for "SuppPubInfo" */ - supp_pub_info_fields.enctype = enctype; - supp_pub_info_fields.as_req = *as_req; - supp_pub_info_fields.pk_as_rep = *pk_as_rep; - if (0 != ((retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields, - &supp_pub_info)))) - goto cleanup; - - /* Now encode the ASN.1 octet string for "OtherInfo" */ - memset(&alg_id, 0, sizeof alg_id); - alg_id.algorithm = *alg_oid; /*alias*/ - - other_info_fields.algorithm_identifier = alg_id; - other_info_fields.party_u_info = (krb5_principal) party_u_info; - other_info_fields.party_v_info = (krb5_principal) party_v_info; - other_info_fields.supp_pub_info = *supp_pub_info; - if (0 != (retval = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info))) + ret = alloc_data(out, reps * hash_len); + if (ret) goto cleanup; + out->length = len; - /* 2. Initialize a 32-bit, big-endian bit string counter as 1. + /* + * 2. Initialize a 32-bit, big-endian bit string counter as 1. * 3. For i = 1 to reps by 1, do the following: * - Compute Hashi = H(counter || Z || OtherInfo). * - Increment counter (modulo 2^32) + * 4. Set key = Hash1 || Hash2 || ... so that length of key is K + * bytes. */ + outptr = (uint8_t *)out->data; for (counter = 1; counter <= reps; counter++) { - uint s = 0; - uint32_t be_counter = htonl(counter); + store_32_be(counter, be_counter); ctx = EVP_MD_CTX_new(); if (ctx == NULL) { - retval = KRB5_CRYPTO_INTERNAL; + ret = KRB5_CRYPTO_INTERNAL; goto cleanup; } /* - Compute Hashi = H(counter || Z || OtherInfo). */ - if (!EVP_DigestInit(ctx, EVP_func())) { - krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL, - "Call to OpenSSL EVP_DigestInit() returned an error."); - retval = KRB5_CRYPTO_INTERNAL; + if (!EVP_DigestInit(ctx, md) || + !EVP_DigestUpdate(ctx, be_counter, 4) || + !EVP_DigestUpdate(ctx, key->data, key->length) || + !EVP_DigestUpdate(ctx, info->data, info->length) || + !EVP_DigestFinal(ctx, outptr, &s)) { + ret = oerr(context, KRB5_CRYPTO_INTERNAL, + _("Failed to compute digest")); goto cleanup; } - if (!EVP_DigestUpdate(ctx, &be_counter, 4) || - !EVP_DigestUpdate(ctx, secret->data, secret->length) || - !EVP_DigestUpdate(ctx, other_info->data, other_info->length)) { - krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL, - "Call to OpenSSL EVP_DigestUpdate() returned an error."); - retval = KRB5_CRYPTO_INTERNAL; - goto cleanup; - } - - /* 4. Set key = Hash1 || Hash2 || ... so that length of key is K bytes. */ - if (!EVP_DigestFinal(ctx, (uint8_t *)random_data.data + offset, &s)) { - krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL, - "Call to OpenSSL EVP_DigestUpdate() returned an error."); - retval = KRB5_CRYPTO_INTERNAL; - goto cleanup; - } - offset += s; assert(s == hash_len); + outptr += s; EVP_MD_CTX_free(ctx); ctx = NULL; } - retval = krb5_c_random_to_key(context, enctype, &random_data, - key_block); - cleanup: EVP_MD_CTX_free(ctx); + return ret; +} - /* If this has been an error, free the allocated key_block, if any */ - if (retval) { - krb5_free_keyblock_contents(context, key_block); - } +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ - /* free other allocated resources, either way */ - if (random_data.data) - free(random_data.data); - krb5_free_data(context, other_info); - krb5_free_data(context, supp_pub_info); +/* id-pkinit-kdf family, as specified by RFC 8636. */ +krb5_error_code +pkinit_alg_agility_kdf(krb5_context context, krb5_data *secret, + krb5_data *alg_oid, krb5_const_principal party_u_info, + krb5_const_principal party_v_info, + krb5_enctype enctype, krb5_data *as_req, + krb5_data *pk_as_rep, krb5_keyblock *key_block) +{ + krb5_error_code ret; + size_t rand_len = 0, key_len = 0; + const EVP_MD *md; + krb5_sp80056a_other_info other_info_fields; + krb5_pkinit_supp_pub_info supp_pub_info_fields; + krb5_data *other_info = NULL, *supp_pub_info = NULL; + krb5_data random_data = empty_data(); + krb5_algorithm_identifier alg_id; + char *hash_name = NULL; - return retval; -} /*pkinit_alg_agility_kdf() */ + ret = krb5_c_keylengths(context, enctype, &rand_len, &key_len); + if (ret) + goto cleanup; -/* Call DH_compute_key() and ensure that we left-pad short results instead of - * leaving junk bytes at the end of the buffer. */ -static void -compute_dh(unsigned char *buf, int size, BIGNUM *server_pub_key, DH *dh) -{ - int len, pad; + /* Allocate and initialize the key block. */ + key_block->magic = 0; + key_block->enctype = enctype; + key_block->length = key_len; + key_block->contents = k5calloc(key_block->length, 1, &ret); + if (key_block->contents == NULL) + goto cleanup; + + /* If this is anonymous pkinit, use the anonymous principle for + * party_u_info. */ + if (party_u_info && + krb5_principal_compare_any_realm(context, party_u_info, + krb5_anonymous_principal())) { + party_u_info = (krb5_principal)krb5_anonymous_principal(); + } - len = DH_compute_key(buf, server_pub_key, dh); - assert(len >= 0 && len <= size); - if (len < size) { - pad = size - len; - memmove(buf + pad, buf, len); - memset(buf, 0, pad); + md = algid_to_md(alg_oid); + if (md == NULL) { + krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS, + "Bad algorithm ID passed to PK-INIT KDF."); + return KRB5_ERR_BAD_S2K_PARAMS; } + + /* Encode the ASN.1 octet string for "SuppPubInfo". */ + supp_pub_info_fields.enctype = enctype; + supp_pub_info_fields.as_req = *as_req; + supp_pub_info_fields.pk_as_rep = *pk_as_rep; + ret = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields, + &supp_pub_info); + if (ret) + goto cleanup; + + /* Now encode the ASN.1 octet string for "OtherInfo". */ + memset(&alg_id, 0, sizeof(alg_id)); + alg_id.algorithm = *alg_oid; + other_info_fields.algorithm_identifier = alg_id; + other_info_fields.party_u_info = (krb5_principal)party_u_info; + other_info_fields.party_v_info = (krb5_principal)party_v_info; + other_info_fields.supp_pub_info = *supp_pub_info; + ret = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info); + if (ret) + goto cleanup; + + ret = sskdf(context, md, secret, other_info, rand_len, &random_data); + if (ret) + goto cleanup; + + ret = krb5_c_random_to_key(context, enctype, &random_data, key_block); + +cleanup: + if (ret) + krb5_free_keyblock_contents(context, key_block); + free(hash_name); + zapfree(random_data.data, random_data.length); + krb5_free_data(context, other_info); + krb5_free_data(context, supp_pub_info); + return ret; } krb5_error_code @@ -2662,99 +2892,38 @@ client_create_dh(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context cryptoctx, pkinit_identity_crypto_context id_cryptoctx, - int dh_size, - unsigned char **dh_params, - unsigned int *dh_params_len, - unsigned char **dh_pubkey, - unsigned int *dh_pubkey_len) + int dh_size, krb5_data *spki_out) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; - unsigned char *buf = NULL; - int dh_err = 0; - ASN1_INTEGER *pub_key = NULL; - const BIGNUM *pubkey_bn, *p, *q, *g; - - if (cryptoctx->dh == NULL) { - if (dh_size == 1024) - cryptoctx->dh = make_oakley_dh(oakley_1024, sizeof(oakley_1024)); - else if (dh_size == 2048) - cryptoctx->dh = make_oakley_dh(oakley_2048, sizeof(oakley_2048)); - else if (dh_size == 4096) - cryptoctx->dh = make_oakley_dh(oakley_4096, sizeof(oakley_4096)); - if (cryptoctx->dh == NULL) - goto cleanup; - } - - DH_generate_key(cryptoctx->dh); - DH_get0_key(cryptoctx->dh, &pubkey_bn, NULL); + EVP_PKEY *params = NULL, *pkey = NULL; - DH_check(cryptoctx->dh, &dh_err); - if (dh_err != 0) { - pkiDebug("Warning: dh_check failed with %d\n", dh_err); - if (dh_err & DH_CHECK_P_NOT_PRIME) - pkiDebug("p value is not prime\n"); - if (dh_err & DH_CHECK_P_NOT_SAFE_PRIME) - pkiDebug("p value is not a safe prime\n"); - if (dh_err & DH_UNABLE_TO_CHECK_GENERATOR) - pkiDebug("unable to check the generator value\n"); - if (dh_err & DH_NOT_SUITABLE_GENERATOR) - pkiDebug("the g value is not a generator\n"); - } -#ifdef DEBUG_DH - print_dh(cryptoctx->dh, "client's DH params\n"); - print_pubkey(cryptoctx->dh->pub_key, "client's pub_key="); -#endif + *spki_out = empty_data(); - DH_check_pub_key(cryptoctx->dh, pubkey_bn, &dh_err); - if (dh_err != 0) { - pkiDebug("dh_check_pub_key failed with %d\n", dh_err); + if (cryptoctx->received_params != NULL) + params = cryptoctx->received_params; + else if (dh_size == 1024) + params = plg_cryptoctx->dh_1024; + else if (dh_size == 2048) + params = plg_cryptoctx->dh_2048; + else if (dh_size == 4096) + params = plg_cryptoctx->dh_4096; + else goto cleanup; - } - /* pack DHparams */ - /* aglo: usually we could just call i2d_DHparams to encode DH params - * however, PKINIT requires RFC3279 encoding and openssl does pkcs#3. - */ - DH_get0_pqg(cryptoctx->dh, &p, &q, &g); - retval = pkinit_encode_dh_params(p, g, q, dh_params, dh_params_len); - if (retval) + pkey = generate_dh_pkey(params); + if (pkey == NULL) goto cleanup; - /* pack DH public key */ - /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this - * encoding shall be used as the contents (the value) of the - * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo - * data element - */ - pub_key = BN_to_ASN1_INTEGER(pubkey_bn, NULL); - if (pub_key == NULL) { - retval = ENOMEM; - goto cleanup; - } - *dh_pubkey_len = i2d_ASN1_INTEGER(pub_key, NULL); - if ((buf = *dh_pubkey = malloc(*dh_pubkey_len)) == NULL) { - retval = ENOMEM; + retval = encode_spki(pkey, spki_out); + if (retval) goto cleanup; - } - i2d_ASN1_INTEGER(pub_key, &buf); - if (pub_key != NULL) - ASN1_INTEGER_free(pub_key); - - retval = 0; - return retval; + EVP_PKEY_free(cryptoctx->client_pkey); + cryptoctx->client_pkey = pkey; + pkey = NULL; cleanup: - if (cryptoctx->dh != NULL) - DH_free(cryptoctx->dh); - cryptoctx->dh = NULL; - free(*dh_params); - *dh_params = NULL; - free(*dh_pubkey); - *dh_pubkey = NULL; - if (pub_key != NULL) - ASN1_INTEGER_free(pub_key); - + EVP_PKEY_free(pkey); return retval; } @@ -2765,77 +2934,56 @@ client_process_dh(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *subjectPublicKey_data, unsigned int subjectPublicKey_length, - unsigned char **client_key, - unsigned int *client_key_len) + unsigned char **client_key_out, + unsigned int *client_key_len_out) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; - BIGNUM *server_pub_key = NULL; - ASN1_INTEGER *pub_key = NULL; - const unsigned char *p = NULL; + EVP_PKEY *server_pkey = NULL; + uint8_t *client_key = NULL; + unsigned int client_key_len; - *client_key_len = DH_size(cryptoctx->dh); - if ((*client_key = malloc(*client_key_len)) == NULL) { - retval = ENOMEM; - goto cleanup; - } - p = subjectPublicKey_data; - pub_key = d2i_ASN1_INTEGER(NULL, &p, (long)subjectPublicKey_length); - if (pub_key == NULL) + *client_key_out = NULL; + *client_key_len_out = 0; + + server_pkey = compose_dh_pkey(cryptoctx->client_pkey, + subjectPublicKey_data, + subjectPublicKey_length); + if (server_pkey == NULL) goto cleanup; - if ((server_pub_key = ASN1_INTEGER_to_BN(pub_key, NULL)) == NULL) + + if (!dh_result(cryptoctx->client_pkey, server_pkey, + &client_key, &client_key_len)) goto cleanup; - compute_dh(*client_key, *client_key_len, server_pub_key, cryptoctx->dh); #ifdef DEBUG_DH print_pubkey(server_pub_key, "server's pub_key="); - pkiDebug("client computed key (%d)= ", *client_key_len); - print_buffer(*client_key, *client_key_len); + pkiDebug("client computed key (%d)= ", client_key_len); + print_buffer(client_key, client_key_len); #endif - retval = 0; - if (server_pub_key != NULL) - BN_free(server_pub_key); - if (pub_key != NULL) - ASN1_INTEGER_free(pub_key); + *client_key_out = client_key; + *client_key_len_out = client_key_len; + client_key = NULL; - return retval; + retval = 0; cleanup: - free(*client_key); - *client_key = NULL; - if (pub_key != NULL) - ASN1_INTEGER_free(pub_key); - + EVP_PKEY_free(server_pkey); + free(client_key); return retval; } /* Return 1 if dh is a permitted well-known group, otherwise return 0. */ static int -check_dh_wellknown(pkinit_plg_crypto_context cryptoctx, DH *dh, int nbits) +check_dh_wellknown(pkinit_plg_crypto_context cryptoctx, EVP_PKEY *pkey, + int nbits) { - - switch (nbits) { - case 1024: - /* Oakley MODP group 2 */ - if (pkinit_check_dh_params(cryptoctx->dh_1024, dh) == 0) - return 1; - break; - - case 2048: - /* Oakley MODP group 14 */ - if (pkinit_check_dh_params(cryptoctx->dh_2048, dh) == 0) - return 1; - break; - - case 4096: - /* Oakley MODP group 16 */ - if (pkinit_check_dh_params(cryptoctx->dh_4096, dh) == 0) - return 1; - break; - - default: - break; - } + if (nbits == 1024) + return EVP_PKEY_parameters_eq(cryptoctx->dh_1024, pkey) == 1; + else if (nbits == 2048) + return EVP_PKEY_parameters_eq(cryptoctx->dh_2048, pkey) == 1; + else if (nbits == 4096) + return EVP_PKEY_parameters_eq(cryptoctx->dh_4096, pkey) == 1; return 0; } @@ -2844,150 +2992,82 @@ server_check_dh(krb5_context context, pkinit_plg_crypto_context cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, - krb5_data *dh_params, + const krb5_data *client_spki, int minbits) { - DH *dh = NULL; - const BIGNUM *p; + EVP_PKEY *client_pkey = NULL; int dh_prime_bits; krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; - dh = decode_dh_params((uint8_t *)dh_params->data, dh_params->length); - if (dh == NULL) { + client_pkey = decode_spki(client_spki); + if (client_pkey == NULL) { pkiDebug("failed to decode dhparams\n"); goto cleanup; } /* KDC SHOULD check to see if the key parameters satisfy its policy */ - DH_get0_pqg(dh, &p, NULL, NULL); - dh_prime_bits = BN_num_bits(p); + dh_prime_bits = EVP_PKEY_get_bits(client_pkey); if (minbits && dh_prime_bits < minbits) { pkiDebug("client sent dh params with %d bits, we require %d\n", dh_prime_bits, minbits); goto cleanup; } - if (check_dh_wellknown(cryptoctx, dh, dh_prime_bits)) + if (check_dh_wellknown(cryptoctx, client_pkey, dh_prime_bits)) retval = 0; cleanup: if (retval == 0) - req_cryptoctx->dh = dh; + req_cryptoctx->client_pkey = client_pkey; else - DH_free(dh); + EVP_PKEY_free(client_pkey); return retval; } -/* Duplicate a DH handle (parameters only, not public or private key). */ -static DH * -dup_dh_params(const DH *src) -{ - const BIGNUM *oldp, *oldq, *oldg; - BIGNUM *p = NULL, *q = NULL, *g = NULL; - DH *dh; - - DH_get0_pqg(src, &oldp, &oldq, &oldg); - p = BN_dup(oldp); - q = BN_dup(oldq); - g = BN_dup(oldg); - dh = DH_new(); - if (p == NULL || q == NULL || g == NULL || dh == NULL) { - BN_free(p); - BN_free(q); - BN_free(g); - DH_free(dh); - return NULL; - } - DH_set0_pqg(dh, p, q, g); - return dh; -} - /* kdc's dh function */ krb5_error_code server_process_dh(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context cryptoctx, pkinit_identity_crypto_context id_cryptoctx, - unsigned char *data, - unsigned int data_len, - unsigned char **dh_pubkey, - unsigned int *dh_pubkey_len, - unsigned char **server_key, - unsigned int *server_key_len) + unsigned char **dh_pubkey_out, + unsigned int *dh_pubkey_len_out, + unsigned char **server_key_out, + unsigned int *server_key_len_out) { krb5_error_code retval = ENOMEM; - DH *dh = NULL, *dh_server = NULL; - unsigned char *p = NULL; - ASN1_INTEGER *pub_key = NULL; - BIGNUM *client_pubkey = NULL; - const BIGNUM *server_pubkey; - - *dh_pubkey = *server_key = NULL; - *dh_pubkey_len = *server_key_len = 0; + EVP_PKEY *server_pkey = NULL; + unsigned char *dh_pubkey = NULL, *server_key = NULL; + unsigned int dh_pubkey_len = 0, server_key_len = 0; - /* get client's received DH parameters that we saved in server_check_dh */ - dh = cryptoctx->dh; - dh_server = dup_dh_params(dh); - if (dh_server == NULL) - goto cleanup; + *dh_pubkey_out = *server_key_out = NULL; + *dh_pubkey_len_out = *server_key_len_out = 0; - /* decode client's public key */ - p = data; - pub_key = d2i_ASN1_INTEGER(NULL, (const unsigned char **)&p, (int)data_len); - if (pub_key == NULL) - goto cleanup; - client_pubkey = ASN1_INTEGER_to_BN(pub_key, NULL); - if (client_pubkey == NULL) + /* Generate a server DH key with the same parameters as the client key. */ + server_pkey = generate_dh_pkey(cryptoctx->client_pkey); + if (server_pkey == NULL) goto cleanup; - ASN1_INTEGER_free(pub_key); - if (!DH_generate_key(dh_server)) + if (!dh_result(server_pkey, cryptoctx->client_pkey, &server_key, + &server_key_len)) goto cleanup; - DH_get0_key(dh_server, &server_pubkey, NULL); - /* generate DH session key */ - *server_key_len = DH_size(dh_server); - if ((*server_key = malloc(*server_key_len)) == NULL) + if (!dh_pubkey_der(server_pkey, &dh_pubkey, &dh_pubkey_len)) goto cleanup; - compute_dh(*server_key, *server_key_len, client_pubkey, dh_server); - -#ifdef DEBUG_DH - print_dh(dh_server, "client&server's DH params\n"); - print_pubkey(client_pubkey, "client's pub_key="); - print_pubkey(server_pubkey, "server's pub_key="); - pkiDebug("server computed key="); - print_buffer(*server_key, *server_key_len); -#endif - /* KDC reply */ - /* pack DH public key */ - /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this - * encoding shall be used as the contents (the value) of the - * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo - * data element - */ - pub_key = BN_to_ASN1_INTEGER(server_pubkey, NULL); - if (pub_key == NULL) - goto cleanup; - *dh_pubkey_len = i2d_ASN1_INTEGER(pub_key, NULL); - if ((p = *dh_pubkey = malloc(*dh_pubkey_len)) == NULL) - goto cleanup; - i2d_ASN1_INTEGER(pub_key, &p); - if (pub_key != NULL) - ASN1_INTEGER_free(pub_key); + *dh_pubkey_out = dh_pubkey; + *dh_pubkey_len_out = dh_pubkey_len; + *server_key_out = server_key; + *server_key_len_out = server_key_len; + dh_pubkey = server_key = NULL; retval = 0; - if (dh_server != NULL) - DH_free(dh_server); - return retval; - cleanup: - BN_free(client_pubkey); - DH_free(dh_server); - free(*dh_pubkey); - free(*server_key); + EVP_PKEY_free(server_pkey); + free(dh_pubkey); + free(server_key); return retval; } @@ -3002,191 +3082,6 @@ pkinit_openssl_init() } static krb5_error_code -pkinit_encode_dh_params(const BIGNUM *p, const BIGNUM *g, const BIGNUM *q, - uint8_t **buf, unsigned int *buf_len) -{ - krb5_error_code retval = ENOMEM; - int bufsize = 0, r = 0; - unsigned char *tmp = NULL; - ASN1_INTEGER *ap = NULL, *ag = NULL, *aq = NULL; - - if ((ap = BN_to_ASN1_INTEGER(p, NULL)) == NULL) - goto cleanup; - if ((ag = BN_to_ASN1_INTEGER(g, NULL)) == NULL) - goto cleanup; - if ((aq = BN_to_ASN1_INTEGER(q, NULL)) == NULL) - goto cleanup; - bufsize = i2d_ASN1_INTEGER(ap, NULL); - bufsize += i2d_ASN1_INTEGER(ag, NULL); - bufsize += i2d_ASN1_INTEGER(aq, NULL); - - r = ASN1_object_size(1, bufsize, V_ASN1_SEQUENCE); - - tmp = *buf = malloc((size_t) r); - if (tmp == NULL) - goto cleanup; - - ASN1_put_object(&tmp, 1, bufsize, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); - - i2d_ASN1_INTEGER(ap, &tmp); - i2d_ASN1_INTEGER(ag, &tmp); - i2d_ASN1_INTEGER(aq, &tmp); - - *buf_len = r; - - retval = 0; - -cleanup: - if (ap != NULL) - ASN1_INTEGER_free(ap); - if (ag != NULL) - ASN1_INTEGER_free(ag); - if (aq != NULL) - ASN1_INTEGER_free(aq); - - return retval; -} - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - -/* - * We need to decode DomainParameters from RFC 3279 section 2.3.3. We would - * like to just call d2i_DHxparams(), but Microsoft's implementation may omit - * the q value in violation of the RFC. Instead we must copy the internal - * structures and sequence declarations from dh_asn1.c, modified to make the q - * field optional. - */ - -typedef struct { - ASN1_BIT_STRING *seed; - BIGNUM *counter; -} int_dhvparams; - -typedef struct { - BIGNUM *p; - BIGNUM *q; - BIGNUM *g; - BIGNUM *j; - int_dhvparams *vparams; -} int_dhx942_dh; - -ASN1_SEQUENCE(DHvparams) = { - ASN1_SIMPLE(int_dhvparams, seed, ASN1_BIT_STRING), - ASN1_SIMPLE(int_dhvparams, counter, BIGNUM) -} static_ASN1_SEQUENCE_END_name(int_dhvparams, DHvparams) - -ASN1_SEQUENCE(DHxparams) = { - ASN1_SIMPLE(int_dhx942_dh, p, BIGNUM), - ASN1_SIMPLE(int_dhx942_dh, g, BIGNUM), - ASN1_OPT(int_dhx942_dh, q, BIGNUM), - ASN1_OPT(int_dhx942_dh, j, BIGNUM), - ASN1_OPT(int_dhx942_dh, vparams, DHvparams), -} static_ASN1_SEQUENCE_END_name(int_dhx942_dh, DHxparams) - -static DH * -decode_dh_params(const uint8_t *p, unsigned int len) -{ - int_dhx942_dh *params; - DH *dh; - - dh = DH_new(); - if (dh == NULL) - return NULL; - - params = (int_dhx942_dh *)ASN1_item_d2i(NULL, &p, len, - ASN1_ITEM_rptr(DHxparams)); - if (params == NULL) { - DH_free(dh); - return NULL; - } - - /* Steal the p, q, and g values from dhparams for dh. Ignore j and - * vparams. */ - DH_set0_pqg(dh, params->p, params->q, params->g); - params->p = params->q = params->g = NULL; - ASN1_item_free((ASN1_VALUE *)params, ASN1_ITEM_rptr(DHxparams)); - return dh; -} - -#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */ - -/* - * Do the same decoding (except without decoding j and vparams or checking the - * sequence length) using the pre-OpenSSL-1.1 asn1_mac.h. Define an internal - * function in the form demanded by the macros, then wrap it for caller - * convenience. - */ - -static DH * -decode_dh_params_int(DH ** a, uint8_t **pp, unsigned int len) -{ - ASN1_INTEGER ai, *aip = NULL; - long length = (long) len; - - M_ASN1_D2I_vars(a, DH *, DH_new); - - M_ASN1_D2I_Init(); - M_ASN1_D2I_start_sequence(); - aip = &ai; - ai.data = NULL; - ai.length = 0; - M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER); - if (aip == NULL) - return NULL; - else { - ret->p = ASN1_INTEGER_to_BN(aip, NULL); - if (ret->p == NULL) - return NULL; - if (ai.data != NULL) { - OPENSSL_free(ai.data); - ai.data = NULL; - ai.length = 0; - } - } - M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER); - if (aip == NULL) - return NULL; - else { - ret->g = ASN1_INTEGER_to_BN(aip, NULL); - if (ret->g == NULL) - return NULL; - if (ai.data != NULL) { - OPENSSL_free(ai.data); - ai.data = NULL; - ai.length = 0; - } - - } - M_ASN1_D2I_get_opt(aip, d2i_ASN1_INTEGER, V_ASN1_INTEGER); - if (aip == NULL || ai.data == NULL) - ret->q = NULL; - else { - ret->q = ASN1_INTEGER_to_BN(aip, NULL); - if (ret->q == NULL) - return NULL; - if (ai.data != NULL) { - OPENSSL_free(ai.data); - ai.data = NULL; - ai.length = 0; - } - - } - M_ASN1_D2I_end_sequence(); - M_ASN1_D2I_Finish(a, DH_free, 0); - -} - -static DH * -decode_dh_params(const uint8_t *p, unsigned int len) -{ - uint8_t *ptr = (uint8_t *)p; - - return decode_dh_params_int(NULL, &ptr, len); -} - -#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ - -static krb5_error_code pkinit_create_sequence_of_principal_identifiers( krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, @@ -3297,151 +3192,50 @@ pkinit_create_td_dh_parameters(krb5_context context, pkinit_plg_opts *opts, krb5_pa_data ***e_data_out) { - krb5_error_code retval = ENOMEM; - unsigned int buf1_len = 0, buf2_len = 0, buf3_len = 0, i = 0; - unsigned char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL; + krb5_error_code ret; + int i; krb5_pa_data **pa_data = NULL; - krb5_data *encoded_algId = NULL; - krb5_algorithm_identifier **algId = NULL; - const BIGNUM *p, *q, *g; + krb5_data *der_alglist = NULL; + krb5_algorithm_identifier alg_1024 = { dh_oid, oakley_1024 }; + krb5_algorithm_identifier alg_2048 = { dh_oid, oakley_2048 }; + krb5_algorithm_identifier alg_4096 = { dh_oid, oakley_4096 }; + krb5_algorithm_identifier *alglist[4]; - if (opts->dh_min_bits > 4096) + if (opts->dh_min_bits > 4096) { + ret = KRB5KRB_ERR_GENERIC; goto cleanup; - - if (opts->dh_min_bits <= 1024) { - DH_get0_pqg(plg_cryptoctx->dh_1024, &p, &q, &g); - retval = pkinit_encode_dh_params(p, g, q, &buf1, &buf1_len); - if (retval) - goto cleanup; - } - if (opts->dh_min_bits <= 2048) { - DH_get0_pqg(plg_cryptoctx->dh_2048, &p, &q, &g); - retval = pkinit_encode_dh_params(p, g, q, &buf2, &buf2_len); - if (retval) - goto cleanup; } - DH_get0_pqg(plg_cryptoctx->dh_4096, &p, &q, &g); - retval = pkinit_encode_dh_params(p, g, q, &buf3, &buf3_len); - if (retval) - goto cleanup; - if (opts->dh_min_bits <= 1024) { - algId = malloc(4 * sizeof(krb5_algorithm_identifier *)); - if (algId == NULL) - goto cleanup; - algId[3] = NULL; - algId[0] = malloc(sizeof(krb5_algorithm_identifier)); - if (algId[0] == NULL) - goto cleanup; - algId[0]->parameters.data = malloc(buf2_len); - if (algId[0]->parameters.data == NULL) - goto cleanup; - memcpy(algId[0]->parameters.data, buf2, buf2_len); - algId[0]->parameters.length = buf2_len; - algId[0]->algorithm = dh_oid; - - algId[1] = malloc(sizeof(krb5_algorithm_identifier)); - if (algId[1] == NULL) - goto cleanup; - algId[1]->parameters.data = malloc(buf3_len); - if (algId[1]->parameters.data == NULL) - goto cleanup; - memcpy(algId[1]->parameters.data, buf3, buf3_len); - algId[1]->parameters.length = buf3_len; - algId[1]->algorithm = dh_oid; + i = 0; + if (opts->dh_min_bits <= 2048) + alglist[i++] = &alg_2048; + alglist[i++] = &alg_4096; + if (opts->dh_min_bits <= 1024) + alglist[i++] = &alg_1024; + alglist[i] = NULL; - algId[2] = malloc(sizeof(krb5_algorithm_identifier)); - if (algId[2] == NULL) - goto cleanup; - algId[2]->parameters.data = malloc(buf1_len); - if (algId[2]->parameters.data == NULL) - goto cleanup; - memcpy(algId[2]->parameters.data, buf1, buf1_len); - algId[2]->parameters.length = buf1_len; - algId[2]->algorithm = dh_oid; - - } else if (opts->dh_min_bits <= 2048) { - algId = malloc(3 * sizeof(krb5_algorithm_identifier *)); - if (algId == NULL) - goto cleanup; - algId[2] = NULL; - algId[0] = malloc(sizeof(krb5_algorithm_identifier)); - if (algId[0] == NULL) - goto cleanup; - algId[0]->parameters.data = malloc(buf2_len); - if (algId[0]->parameters.data == NULL) - goto cleanup; - memcpy(algId[0]->parameters.data, buf2, buf2_len); - algId[0]->parameters.length = buf2_len; - algId[0]->algorithm = dh_oid; - - algId[1] = malloc(sizeof(krb5_algorithm_identifier)); - if (algId[1] == NULL) - goto cleanup; - algId[1]->parameters.data = malloc(buf3_len); - if (algId[1]->parameters.data == NULL) - goto cleanup; - memcpy(algId[1]->parameters.data, buf3, buf3_len); - algId[1]->parameters.length = buf3_len; - algId[1]->algorithm = dh_oid; - - } else if (opts->dh_min_bits <= 4096) { - algId = malloc(2 * sizeof(krb5_algorithm_identifier *)); - if (algId == NULL) - goto cleanup; - algId[1] = NULL; - algId[0] = malloc(sizeof(krb5_algorithm_identifier)); - if (algId[0] == NULL) - goto cleanup; - algId[0]->parameters.data = malloc(buf3_len); - if (algId[0]->parameters.data == NULL) - goto cleanup; - memcpy(algId[0]->parameters.data, buf3, buf3_len); - algId[0]->parameters.length = buf3_len; - algId[0]->algorithm = dh_oid; - - } - retval = k5int_encode_krb5_td_dh_parameters((krb5_algorithm_identifier *const *)algId, &encoded_algId); - if (retval) + ret = k5int_encode_krb5_td_dh_parameters(alglist, &der_alglist); + if (ret) goto cleanup; -#ifdef DEBUG_ASN1 - print_buffer_bin((unsigned char *)encoded_algId->data, - encoded_algId->length, "/tmp/kdc_td_dh_params"); -#endif - pa_data = malloc(2 * sizeof(krb5_pa_data *)); - if (pa_data == NULL) { - retval = ENOMEM; + + pa_data = k5calloc(2, sizeof(*pa_data), &ret); + if (pa_data == NULL) goto cleanup; - } pa_data[1] = NULL; - pa_data[0] = malloc(sizeof(krb5_pa_data)); + pa_data[0] = k5alloc(sizeof(*pa_data[0]), &ret); if (pa_data[0] == NULL) { free(pa_data); - retval = ENOMEM; goto cleanup; } pa_data[0]->pa_type = TD_DH_PARAMETERS; - pa_data[0]->length = encoded_algId->length; - pa_data[0]->contents = (krb5_octet *)encoded_algId->data; + pa_data[0]->length = der_alglist->length; + pa_data[0]->contents = (krb5_octet *)der_alglist->data; + der_alglist->data = NULL; *e_data_out = pa_data; - retval = 0; -cleanup: - - free(buf1); - free(buf2); - free(buf3); - free(encoded_algId); - - if (algId != NULL) { - while(algId[i] != NULL) { - free(algId[i]->parameters.data); - free(algId[i]); - i++; - } - free(algId); - } - return retval; +cleanup: + krb5_free_data(context, der_alglist); + return ret; } krb5_error_code @@ -3478,26 +3272,6 @@ pkinit_check_kdc_pkid(krb5_context context, return 0; } -/* Check parameters against a well-known DH group. */ -static int -pkinit_check_dh_params(DH *dh1, DH *dh2) -{ - const BIGNUM *p1, *p2, *g1, *g2; - - DH_get0_pqg(dh1, &p1, NULL, &g1); - DH_get0_pqg(dh2, &p2, NULL, &g2); - if (BN_cmp(p1, p2) != 0) { - pkiDebug("p is not well-known group dhparameter\n"); - return -1; - } - if (BN_cmp(g1, g2) != 0) { - pkiDebug("bad g dhparameter\n"); - return -1; - } - pkiDebug("good %d dhparams\n", BN_num_bits(p1)); - return 0; -} - krb5_error_code pkinit_process_td_dh_params(krb5_context context, pkinit_plg_crypto_context cryptoctx, @@ -3507,60 +3281,55 @@ pkinit_process_td_dh_params(krb5_context context, int *new_dh_size) { krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; - int i = 0, use_sent_dh = 0, ok = 0; + EVP_PKEY *params = NULL; + int i, dh_prime_bits, old_dh_size; pkiDebug("dh parameters\n"); - while (algId[i] != NULL) { - DH *dh = NULL; - const BIGNUM *p; - int dh_prime_bits = 0; + EVP_PKEY_free(req_cryptoctx->received_params); + req_cryptoctx->received_params = NULL; + + old_dh_size = *new_dh_size; + for (i = 0; algId[i] != NULL; i++) { + /* Free any parameters from the previous iteration. */ + EVP_PKEY_free(params); + params = NULL; + + /* Skip any parameters for algorithms other than DH. */ if (algId[i]->algorithm.length != dh_oid.length || memcmp(algId[i]->algorithm.data, dh_oid.data, dh_oid.length)) - goto cleanup; + continue; - dh = decode_dh_params((uint8_t *)algId[i]->parameters.data, - algId[i]->parameters.length); - if (dh == NULL) - goto cleanup; - DH_get0_pqg(dh, &p, NULL, NULL); - dh_prime_bits = BN_num_bits(p); + params = decode_dh_params(&algId[i]->parameters); + if (params == NULL) + continue; + dh_prime_bits = EVP_PKEY_get_bits(params); + /* Skip any parameters shorter than the previous size. */ + if (dh_prime_bits < old_dh_size) + continue; pkiDebug("client sent %d DH bits server prefers %d DH bits\n", *new_dh_size, dh_prime_bits); - ok = check_dh_wellknown(cryptoctx, dh, dh_prime_bits); - if (ok) { + + /* If this is one of our well-known groups, just save the new size; we + * will use our own copy of the parameters. */ + if (check_dh_wellknown(cryptoctx, params, dh_prime_bits)) { *new_dh_size = dh_prime_bits; + retval = 0; + goto cleanup; } - if (!ok) { - DH_check(dh, &retval); - if (retval != 0) { - pkiDebug("DH parameters provided by server are unacceptable\n"); - retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; - } - else { - use_sent_dh = 1; - ok = 1; - } - } - if (!use_sent_dh) - DH_free(dh); - if (ok) { - if (req_cryptoctx->dh != NULL) { - DH_free(req_cryptoctx->dh); - req_cryptoctx->dh = NULL; - } - if (use_sent_dh) - req_cryptoctx->dh = dh; - break; + + /* If the parameters aren't well-known but check out, save them. */ + if (params_valid(params)) { + req_cryptoctx->received_params = params; + params = NULL; + retval = 0; + goto cleanup; } - i++; } - if (ok) - retval = 0; - cleanup: + EVP_PKEY_free(params); return retval; } @@ -3596,8 +3365,6 @@ pkinit_pkcs7type2oid(pkinit_plg_crypto_context cryptoctx, int pkcs7_type) switch (pkcs7_type) { case CMS_SIGN_CLIENT: return cryptoctx->id_pkinit_authData; - case CMS_SIGN_DRAFT9: - return OBJ_nid2obj(NID_pkcs7_data); case CMS_SIGN_SERVER: return cryptoctx->id_pkinit_DHKeyData; case CMS_ENVEL_SERVER: @@ -3673,33 +3440,48 @@ prepare_enc_data(const uint8_t *indata, int indata_len, uint8_t **outdata, } #ifndef WITHOUT_PKCS11 -static void * -pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p) +static struct plugin_file_handle * +load_pkcs11_module(krb5_context context, const char *modname, + CK_FUNCTION_LIST_PTR_PTR p11p) { - void *handle; + struct plugin_file_handle *handle = NULL; CK_RV (*getflist)(CK_FUNCTION_LIST_PTR_PTR); + struct errinfo einfo = EMPTY_ERRINFO; + const char *errmsg = NULL; + void (*sym)(); + long err; + CK_RV rv; - pkiDebug("loading module \"%s\"... ", modname); - handle = dlopen(modname, RTLD_NOW); - if (handle == NULL) { - pkiDebug("not found\n"); - return NULL; + TRACE_PKINIT_PKCS11_OPEN(context, modname); + err = krb5int_open_plugin(modname, &handle, &einfo); + if (err) { + errmsg = k5_get_error(&einfo, err); + TRACE_PKINIT_PKCS11_OPEN_FAILED(context, errmsg); + goto error; } - getflist = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(handle, "C_GetFunctionList"); - if (getflist == NULL || (*getflist)(p11p) != CKR_OK) { - dlclose(handle); - pkiDebug("failed\n"); - return NULL; + + err = krb5int_get_plugin_func(handle, "C_GetFunctionList", &sym, &einfo); + if (err) { + errmsg = k5_get_error(&einfo, err); + TRACE_PKINIT_PKCS11_GETSYM_FAILED(context, errmsg); + goto error; + } + + getflist = (CK_RV (*)())sym; + rv = (*getflist)(p11p); + if (rv != CKR_OK) { + TRACE_PKINIT_PKCS11_GETFLIST_FAILED(context, pkcs11err(rv)); + goto error; } - pkiDebug("ok\n"); + return handle; -} -static CK_RV -pkinit_C_UnloadModule(void *handle) -{ - dlclose(handle); - return CKR_OK; +error: + k5_free_error(&einfo, errmsg); + k5_clear_error(&einfo); + if (handle != NULL) + krb5int_close_plugin(handle); + return NULL; } static krb5_error_code @@ -3756,7 +3538,7 @@ pkinit_login(krb5_context context, (u_char *) rdat.data, rdat.length); if (r != CKR_OK) { - pkiDebug("C_Login: %s\n", pkinit_pkcs11_code_to_text(r)); + TRACE_PKINIT_PKCS11_LOGIN_FAILED(context, pkcs11err(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; } } @@ -3769,40 +3551,46 @@ static krb5_error_code pkinit_open_session(krb5_context context, pkinit_identity_crypto_context cctx) { - CK_ULONG i, r; + CK_ULONG i, pret; unsigned char *cp; size_t label_len; CK_ULONG count = 0; - CK_SLOT_ID_PTR slotlist; + CK_SLOT_ID_PTR slotlist = NULL; CK_TOKEN_INFO tinfo; - char *p11name; + char *p11name = NULL; const char *password; + krb5_error_code ret; if (cctx->p11_module != NULL) return 0; /* session already open */ /* Load module */ - cctx->p11_module = - pkinit_C_LoadModule(cctx->p11_module_name, &cctx->p11); + cctx->p11_module = load_pkcs11_module(context, cctx->p11_module_name, + &cctx->p11); if (cctx->p11_module == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; /* Init */ - if ((r = cctx->p11->C_Initialize(NULL)) != CKR_OK) { - pkiDebug("C_Initialize: %s\n", pkinit_pkcs11_code_to_text(r)); + pret = cctx->p11->C_Initialize(NULL); + if (pret != CKR_OK) { + pkiDebug("C_Initialize: %s\n", pkcs11err(pret)); return KRB5KDC_ERR_PREAUTH_FAILED; } /* Get the list of available slots */ if (cctx->p11->C_GetSlotList(TRUE, NULL, &count) != CKR_OK) return KRB5KDC_ERR_PREAUTH_FAILED; - if (count == 0) + if (count == 0) { + TRACE_PKINIT_PKCS11_NO_TOKEN(context); return KRB5KDC_ERR_PREAUTH_FAILED; + } slotlist = calloc(count, sizeof(CK_SLOT_ID)); if (slotlist == NULL) return ENOMEM; - if (cctx->p11->C_GetSlotList(TRUE, slotlist, &count) != CKR_OK) - return KRB5KDC_ERR_PREAUTH_FAILED; + if (cctx->p11->C_GetSlotList(TRUE, slotlist, &count) != CKR_OK) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } /* Look for the given token label, or if none given take the first one */ for (i = 0; i < count; i++) { @@ -3811,16 +3599,20 @@ pkinit_open_session(krb5_context context, continue; /* Open session */ - if ((r = cctx->p11->C_OpenSession(slotlist[i], CKF_SERIAL_SESSION, - NULL, NULL, &cctx->session)) != CKR_OK) { - pkiDebug("C_OpenSession: %s\n", pkinit_pkcs11_code_to_text(r)); - return KRB5KDC_ERR_PREAUTH_FAILED; + pret = cctx->p11->C_OpenSession(slotlist[i], CKF_SERIAL_SESSION, + NULL, NULL, &cctx->session); + if (pret != CKR_OK) { + pkiDebug("C_OpenSession: %s\n", pkcs11err(pret)); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; } /* Get token info */ - if ((r = cctx->p11->C_GetTokenInfo(slotlist[i], &tinfo)) != CKR_OK) { - pkiDebug("C_GetTokenInfo: %s\n", pkinit_pkcs11_code_to_text(r)); - return KRB5KDC_ERR_PREAUTH_FAILED; + pret = cctx->p11->C_GetTokenInfo(slotlist[i], &tinfo); + if (pret != CKR_OK) { + pkiDebug("C_GetTokenInfo: %s\n", pkcs11err(pret)); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; } /* tinfo.label is zero-filled but not necessarily zero-terminated. @@ -3831,8 +3623,8 @@ pkinit_open_session(krb5_context context, } label_len = cp - tinfo.label; - pkiDebug("open_session: slotid %d token \"%.*s\"\n", - (int)slotlist[i], (int)label_len, tinfo.label); + TRACE_PKINIT_PKCS11_SLOT(context, (int)slotlist[i], (int)label_len, + tinfo.label); if (cctx->token_label == NULL || (strlen(cctx->token_label) == label_len && memcmp(cctx->token_label, tinfo.label, label_len) == 0)) @@ -3840,12 +3632,11 @@ pkinit_open_session(krb5_context context, cctx->p11->C_CloseSession(cctx->session); } if (i >= count) { - free(slotlist); - pkiDebug("open_session: no matching token found\n"); - return KRB5KDC_ERR_PREAUTH_FAILED; + TRACE_PKINIT_PKCS11_NO_MATCH_TOKEN(context); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; } cctx->slotid = slotlist[i]; - free(slotlist); pkiDebug("open_session: slotid %d (%lu of %d)\n", (int)cctx->slotid, i + 1, (int) count); @@ -3865,23 +3656,26 @@ pkinit_open_session(krb5_context context, (int)label_len, tinfo.label) < 0) p11name = NULL; } - } else { - p11name = NULL; } if (cctx->defer_id_prompt) { /* Supply the identity name to be passed to the responder. */ pkinit_set_deferred_id(&cctx->deferred_ids, p11name, tinfo.flags, NULL); - free(p11name); - return KRB5KRB_ERR_GENERIC; + ret = 0; + goto cleanup; } /* Look up a responder-supplied password for the token. */ password = pkinit_find_deferred_id(cctx->deferred_ids, p11name); - free(p11name); - r = pkinit_login(context, cctx, &tinfo, password); + ret = pkinit_login(context, cctx, &tinfo, password); + if (ret) + goto cleanup; } - return r; + ret = 0; +cleanup: + free(slotlist); + free(p11name); + return ret; } /* @@ -3950,13 +3744,13 @@ pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx, r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs); if (r != CKR_OK) { pkiDebug("krb5_pkinit_sign_data: C_FindObjectsInit: %s\n", - pkinit_pkcs11_code_to_text(r)); + pkcs11err(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session, objp, 1, &count); id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session); - pkiDebug("found %d private keys (%s)\n", (int) count, pkinit_pkcs11_code_to_text(r)); + pkiDebug("found %d private keys (%s)\n", (int)count, pkcs11err(r)); if (r != CKR_OK || count < 1) return KRB5KDC_ERR_PREAUTH_FAILED; return 0; @@ -3972,8 +3766,10 @@ pkinit_decode_data_fs(krb5_context context, X509 *cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index); EVP_PKEY *pkey = id_cryptoctx->my_key; - uint8_t *buf; - int buf_len, decrypt_len; + EVP_PKEY_CTX *ctx = NULL; + uint8_t *buf = NULL; + size_t buf_len = 0; + int ok; *decoded_data = NULL; *decoded_data_len = 0; @@ -3983,21 +3779,40 @@ pkinit_decode_data_fs(krb5_context context, return KRB5KDC_ERR_PREAUTH_FAILED; } - buf_len = EVP_PKEY_size(pkey); - buf = malloc(buf_len + 10); - if (buf == NULL) + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (ctx == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; - decrypt_len = EVP_PKEY_decrypt_old(buf, data, data_len, pkey); - if (decrypt_len <= 0) { - pkiDebug("unable to decrypt received data (len=%d)\n", data_len); - free(buf); - return KRB5KDC_ERR_PREAUTH_FAILED; + ok = EVP_PKEY_decrypt_init(ctx); + if (!ok) + goto cleanup; + + /* Get the length of the eventual output. */ + ok = EVP_PKEY_decrypt(ctx, NULL, &buf_len, data, data_len); + if (!ok) { + pkiDebug("unable to decrypt received data\n"); + goto cleanup; + } + + buf = malloc(buf_len); + if (buf == NULL) { + ok = 0; + goto cleanup; + } + + ok = EVP_PKEY_decrypt(ctx, buf, &buf_len, data, data_len); + if (!ok) { + pkiDebug("unable to decrypt received data\n"); + goto cleanup; } *decoded_data = buf; - *decoded_data_len = decrypt_len; - return 0; + *decoded_data_len = buf_len; + buf = NULL; +cleanup: + zapfree(buf, buf_len); + EVP_PKEY_CTX_free(ctx); + return ok ? 0 : KRB5KDC_ERR_PREAUTH_FAILED; } #ifndef WITHOUT_PKCS11 @@ -4069,7 +3884,7 @@ pkinit_decode_data_pkcs11(krb5_context context, r = pkinit_C_Decrypt(id_cryptoctx, (CK_BYTE_PTR) data, (CK_ULONG) data_len, cp, &len); if (r != CKR_OK) { - pkiDebug("C_Decrypt: %s\n", pkinit_pkcs11_code_to_text(r)); + pkiDebug("C_Decrypt: %s\n", pkcs11err(r)); if (r == CKR_BUFFER_TOO_SMALL) pkiDebug("decrypt %d needs %d\n", (int) data_len, (int) len); return KRB5KDC_ERR_PREAUTH_FAILED; @@ -4149,7 +3964,7 @@ pkinit_sign_data_pkcs11(krb5_context context, if ((r = id_cryptoctx->p11->C_SignInit(id_cryptoctx->session, &mech, obj)) != CKR_OK) { - pkiDebug("C_SignInit: %s\n", pkinit_pkcs11_code_to_text(r)); + pkiDebug("C_SignInit: %s\n", pkcs11err(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } @@ -4172,7 +3987,7 @@ pkinit_sign_data_pkcs11(krb5_context context, (CK_ULONG) data_len, cp, &len); } if (r != CKR_OK) { - pkiDebug("C_Sign: %s\n", pkinit_pkcs11_code_to_text(r)); + pkiDebug("C_Sign: %s\n", pkcs11err(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } pkiDebug("sign %d -> %d\n", (int) data_len, (int) len); @@ -4219,7 +4034,7 @@ create_signature(unsigned char **sig, unsigned int *sig_len, ctx = EVP_MD_CTX_new(); if (ctx == NULL) return ENOMEM; - EVP_SignInit(ctx, EVP_sha1()); + EVP_SignInit(ctx, EVP_sha256()); EVP_SignUpdate(ctx, data, data_len); *sig_len = EVP_PKEY_size(pkey); if ((*sig = malloc(*sig_len)) == NULL) @@ -4434,17 +4249,18 @@ pkinit_load_fs_cert_and_key(krb5_context context, /* Load the certificate. */ retval = get_cert(certname, &x); - if (retval != 0 || x == NULL) { - retval = oerr(context, 0, _("Cannot read certificate file '%s'"), + if (retval) { + retval = oerr(context, retval, _("Cannot read certificate file '%s'"), certname); - goto cleanup; } + if (retval || x == NULL) + goto cleanup; /* Load the key. */ retval = get_key(context, id_cryptoctx, keyname, fsname, &y, password); - if (retval != 0 || y == NULL) { - retval = oerr(context, 0, _("Cannot read key file '%s'"), fsname); + if (retval) + retval = oerr(context, retval, _("Cannot read key file '%s'"), fsname); + if (retval || y == NULL) goto cleanup; - } id_cryptoctx->creds[cindex] = malloc(sizeof(struct _pkinit_cred_info)); if (id_cryptoctx->creds[cindex] == NULL) { @@ -4590,7 +4406,6 @@ reassemble_pkcs11_name(pkinit_identity_opts *idopts) { struct k5buf buf; int n = 0; - char *ret; k5_buf_init_dynamic(&buf); k5_buf_add(&buf, "PKCS11:"); @@ -4615,11 +4430,89 @@ reassemble_pkcs11_name(pkinit_identity_opts *idopts) k5_buf_add_fmt(&buf, "%sslotid=%ld", n++ ? ":" : "", (long)idopts->slotid); } - if (k5_buf_status(&buf) == 0) - ret = strdup(buf.data); - else - ret = NULL; - k5_buf_free(&buf); + return k5_buf_cstring(&buf); +} + +static krb5_error_code +load_one_cert(CK_FUNCTION_LIST_PTR p11, CK_SESSION_HANDLE session, + pkinit_identity_opts *idopts, pkinit_cred_info *cred_out) +{ + krb5_error_code ret; + CK_ATTRIBUTE attrs[2]; + CK_BYTE_PTR cert = NULL, cert_id = NULL; + CK_RV pret; + const unsigned char *cp; + CK_OBJECT_HANDLE obj; + CK_ULONG count; + X509 *x = NULL; + pkinit_cred_info cred; + + *cred_out = NULL; + + /* Look for X.509 cert. */ + pret = p11->C_FindObjects(session, &obj, 1, &count); + if (pret != CKR_OK || count <= 0) + return 0; + + /* Get cert and id len. */ + attrs[0].type = CKA_VALUE; + attrs[0].pValue = NULL; + attrs[0].ulValueLen = 0; + attrs[1].type = CKA_ID; + attrs[1].pValue = NULL; + attrs[1].ulValueLen = 0; + pret = p11->C_GetAttributeValue(session, obj, attrs, 2); + if (pret != CKR_OK && pret != CKR_BUFFER_TOO_SMALL) { + pkiDebug("C_GetAttributeValue: %s\n", pkcs11err(pret)); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } + + /* Allocate buffers and read the cert and id. */ + cert = k5alloc(attrs[0].ulValueLen + 1, &ret); + if (cert == NULL) + goto cleanup; + cert_id = k5alloc(attrs[1].ulValueLen + 1, &ret); + if (cert_id == NULL) + goto cleanup; + attrs[0].type = CKA_VALUE; + attrs[0].pValue = cert; + attrs[1].type = CKA_ID; + attrs[1].pValue = cert_id; + pret = p11->C_GetAttributeValue(session, obj, attrs, 2); + if (pret != CKR_OK) { + pkiDebug("C_GetAttributeValue: %s\n", pkcs11err(pret)); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } + + pkiDebug("cert: size %d, id %d, idlen %d\n", (int)attrs[0].ulValueLen, + (int)cert_id[0], (int)attrs[1].ulValueLen); + + cp = (unsigned char *)cert; + x = d2i_X509(NULL, &cp, (int)attrs[0].ulValueLen); + if (x == NULL) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } + + cred = k5alloc(sizeof(struct _pkinit_cred_info), &ret); + if (cred == NULL) + goto cleanup; + + cred->name = reassemble_pkcs11_name(idopts); + cred->cert = x; + cred->key = NULL; + cred->cert_id = cert_id; + cred->cert_id_len = attrs[1].ulValueLen; + + *cred_out = cred; + cert_id = NULL; + ret = 0; + +cleanup: + free(cert); + free(cert_id); return ret; } @@ -4631,20 +4524,13 @@ pkinit_get_certs_pkcs11(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ) { -#ifdef PKINIT_USE_MECH_LIST - CK_MECHANISM_TYPE_PTR mechp; - CK_MECHANISM_INFO info; -#endif CK_OBJECT_CLASS cls; - CK_OBJECT_HANDLE obj; CK_ATTRIBUTE attrs[4]; - CK_ULONG count; CK_CERTIFICATE_TYPE certtype; - CK_BYTE_PTR cert = NULL, cert_id; - const unsigned char *cp; - int i, r; + int i; unsigned int nattrs; - X509 *x = NULL; + krb5_error_code ret; + CK_RV pret; /* Copy stuff from idopts -> id_cryptoctx */ if (idopts->p11_module_name != NULL) { @@ -4665,27 +4551,20 @@ pkinit_get_certs_pkcs11(krb5_context context, } /* Convert the ascii cert_id string into a binary blob */ if (idopts->cert_id_string != NULL) { - BIGNUM *bn = NULL; - BN_hex2bn(&bn, idopts->cert_id_string); - if (bn == NULL) - return ENOMEM; - id_cryptoctx->cert_id_len = BN_num_bytes(bn); - id_cryptoctx->cert_id = malloc((size_t) id_cryptoctx->cert_id_len); - if (id_cryptoctx->cert_id == NULL) { - BN_free(bn); - return ENOMEM; + ret = k5_hex_decode(idopts->cert_id_string, &id_cryptoctx->cert_id, + &id_cryptoctx->cert_id_len); + if (ret) { + pkiDebug("Failed to convert certid string [%s]\n", + idopts->cert_id_string); + return ret; } - BN_bn2bin(bn, id_cryptoctx->cert_id); - BN_free(bn); } id_cryptoctx->slotid = idopts->slotid; id_cryptoctx->pkcs11_method = 1; - if (pkinit_open_session(context, id_cryptoctx)) { - pkiDebug("can't open pkcs11 session\n"); - if (!id_cryptoctx->defer_id_prompt) - return KRB5KDC_ERR_PREAUTH_FAILED; - } + ret = pkinit_open_session(context, id_cryptoctx); + if (ret) + return ret; if (id_cryptoctx->defer_id_prompt) { /* * We need to reset all of the PKCS#11 state, so that the next time we @@ -4697,56 +4576,24 @@ pkinit_get_certs_pkcs11(krb5_context context, return 0; } -#ifndef PKINIT_USE_MECH_LIST /* - * We'd like to use CKM_SHA1_RSA_PKCS for signing if it's available, but - * many cards seems to be confused about whether they are capable of - * this or not. The safe thing seems to be to ignore the mechanism list, - * always use CKM_RSA_PKCS and calculate the sha1 digest ourselves. + * We'd like to use CKM_SHA256_RSA_PKCS for signing if it's available, but + * historically many cards seem to be confused about whether they are + * capable of mechanisms or not. The safe thing seems to be to ignore the + * mechanism list, always use CKM_RSA_PKCS and calculate the sha256 digest + * ourselves. */ - id_cryptoctx->mech = CKM_RSA_PKCS; -#else - if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid, NULL, - &count)) != CKR_OK || count <= 0) { - pkiDebug("C_GetMechanismList: %s\n", pkinit_pkcs11_code_to_text(r)); - return KRB5KDC_ERR_PREAUTH_FAILED; - } - mechp = malloc(count * sizeof (CK_MECHANISM_TYPE)); - if (mechp == NULL) - return ENOMEM; - if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid, - mechp, &count)) != CKR_OK) - return KRB5KDC_ERR_PREAUTH_FAILED; - for (i = 0; i < count; i++) { - if ((r = id_cryptoctx->p11->C_GetMechanismInfo(id_cryptoctx->slotid, - mechp[i], &info)) != CKR_OK) - return KRB5KDC_ERR_PREAUTH_FAILED; -#ifdef DEBUG_MECHINFO - pkiDebug("mech %x flags %x\n", (int) mechp[i], (int) info.flags); - if ((info.flags & (CKF_SIGN|CKF_DECRYPT)) == (CKF_SIGN|CKF_DECRYPT)) - pkiDebug(" this mech is good for sign & decrypt\n"); -#endif - if (mechp[i] == CKM_RSA_PKCS) { - /* This seems backwards... */ - id_cryptoctx->mech = - (info.flags & CKF_SIGN) ? CKM_SHA1_RSA_PKCS : CKM_RSA_PKCS; - } - } - free(mechp); - - pkiDebug("got %d mechs from card\n", (int) count); -#endif cls = CKO_CERTIFICATE; attrs[0].type = CKA_CLASS; attrs[0].pValue = &cls; - attrs[0].ulValueLen = sizeof cls; + attrs[0].ulValueLen = sizeof(cls); certtype = CKC_X_509; attrs[1].type = CKA_CERTIFICATE_TYPE; attrs[1].pValue = &certtype; - attrs[1].ulValueLen = sizeof certtype; + attrs[1].ulValueLen = sizeof(certtype); nattrs = 2; @@ -4764,80 +4611,33 @@ pkinit_get_certs_pkcs11(krb5_context context, nattrs++; } - r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs); - if (r != CKR_OK) { - pkiDebug("C_FindObjectsInit: %s\n", pkinit_pkcs11_code_to_text(r)); + pret = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, + nattrs); + if (pret != CKR_OK) { + pkiDebug("C_FindObjectsInit: %s\n", pkcs11err(pret)); return KRB5KDC_ERR_PREAUTH_FAILED; } - for (i = 0; ; i++) { - if (i >= MAX_CREDS_ALLOWED) - return KRB5KDC_ERR_PREAUTH_FAILED; - - /* Look for x.509 cert */ - if ((r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session, - &obj, 1, &count)) != CKR_OK || count <= 0) { - id_cryptoctx->creds[i] = NULL; - break; - } - - /* Get cert and id len */ - attrs[0].type = CKA_VALUE; - attrs[0].pValue = NULL; - attrs[0].ulValueLen = 0; - - attrs[1].type = CKA_ID; - attrs[1].pValue = NULL; - attrs[1].ulValueLen = 0; - - if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session, - obj, attrs, 2)) != CKR_OK && r != CKR_BUFFER_TOO_SMALL) { - pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r)); - return KRB5KDC_ERR_PREAUTH_FAILED; - } - cert = (CK_BYTE_PTR) malloc((size_t) attrs[0].ulValueLen + 1); - cert_id = (CK_BYTE_PTR) malloc((size_t) attrs[1].ulValueLen + 1); - if (cert == NULL || cert_id == NULL) - return ENOMEM; - - /* Read the cert and id off the card */ - - attrs[0].type = CKA_VALUE; - attrs[0].pValue = cert; - - attrs[1].type = CKA_ID; - attrs[1].pValue = cert_id; - - if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session, - obj, attrs, 2)) != CKR_OK) { - pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r)); - return KRB5KDC_ERR_PREAUTH_FAILED; - } - - pkiDebug("cert %d size %d id %d idlen %d\n", i, - (int) attrs[0].ulValueLen, (int) cert_id[0], - (int) attrs[1].ulValueLen); - - cp = (unsigned char *) cert; - x = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen); - if (x == NULL) - return KRB5KDC_ERR_PREAUTH_FAILED; - id_cryptoctx->creds[i] = malloc(sizeof(struct _pkinit_cred_info)); + for (i = 0; i < MAX_CREDS_ALLOWED; i++) { + ret = load_one_cert(id_cryptoctx->p11, id_cryptoctx->session, idopts, + &id_cryptoctx->creds[i]); + if (ret) + return ret; if (id_cryptoctx->creds[i] == NULL) - return KRB5KDC_ERR_PREAUTH_FAILED; - id_cryptoctx->creds[i]->name = reassemble_pkcs11_name(idopts); - id_cryptoctx->creds[i]->cert = x; - id_cryptoctx->creds[i]->key = NULL; - id_cryptoctx->creds[i]->cert_id = cert_id; - id_cryptoctx->creds[i]->cert_id_len = attrs[1].ulValueLen; - free(cert); + break; } + if (i == MAX_CREDS_ALLOWED) + return KRB5KDC_ERR_PREAUTH_FAILED; + id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session); - if (cert == NULL) + + /* Check if we found no certs. */ + if (id_cryptoctx->creds[0] == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; return 0; } -#endif + +#endif /* !WITHOUT_PKCS11 */ static void @@ -5060,6 +4860,9 @@ crypto_cert_free_matching_data(krb5_context context, for (i = 0; md->sans != NULL && md->sans[i] != NULL; i++) krb5_free_principal(context, md->sans[i]); free(md->sans); + for (i = 0; md->upns != NULL && md->upns[i] != NULL; i++) + free(md->upns[i]); + free(md->upns); free(md); } @@ -5088,8 +4891,6 @@ get_matching_data(krb5_context context, { krb5_error_code ret = ENOMEM; pkinit_cert_matching_data *md = NULL; - krb5_principal *pkinit_sans = NULL, *upn_sans = NULL; - size_t i, j; *md_out = NULL; @@ -5106,40 +4907,10 @@ get_matching_data(krb5_context context, /* Get the SAN data. */ ret = crypto_retrieve_X509_sans(context, plg_cryptoctx, req_cryptoctx, - cert, &pkinit_sans, &upn_sans, NULL); + cert, &md->sans, &md->upns, NULL); if (ret) goto cleanup; - j = 0; - if (pkinit_sans != NULL) { - for (i = 0; pkinit_sans[i] != NULL; i++) - j++; - } - if (upn_sans != NULL) { - for (i = 0; upn_sans[i] != NULL; i++) - j++; - } - if (j != 0) { - md->sans = calloc((size_t)j+1, sizeof(*md->sans)); - if (md->sans == NULL) { - ret = ENOMEM; - goto cleanup; - } - j = 0; - if (pkinit_sans != NULL) { - for (i = 0; pkinit_sans[i] != NULL; i++) - md->sans[j++] = pkinit_sans[i]; - free(pkinit_sans); - } - if (upn_sans != NULL) { - for (i = 0; upn_sans[i] != NULL; i++) - md->sans[j++] = upn_sans[i]; - free(upn_sans); - } - md->sans[j] = NULL; - } else - md->sans = NULL; - /* Get the KU and EKU data. */ ret = crypto_retrieve_X509_key_usage(context, plg_cryptoctx, req_cryptoctx, cert, &md->ku_bits, @@ -5508,12 +5279,12 @@ crypto_load_cas_and_crls(krb5_context context, { switch (idtype) { case IDTYPE_FILE: - TRACE_PKINIT_LOAD_FROM_FILE(context); + TRACE_PKINIT_LOAD_FROM_FILE(context, id); return load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, catype, id); break; case IDTYPE_DIR: - TRACE_PKINIT_LOAD_FROM_DIR(context); + TRACE_PKINIT_LOAD_FROM_DIR(context, id); return load_cas_and_crls_dir(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, catype, id); break; @@ -5651,37 +5422,38 @@ create_krb5_supportedCMSTypes(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, - krb5_algorithm_identifier ***oids) + krb5_algorithm_identifier ***algs_out) { + krb5_error_code ret; + krb5_algorithm_identifier **algs = NULL; + size_t i, count; - krb5_error_code retval = ENOMEM; - krb5_algorithm_identifier **loids = NULL; - krb5_data des3oid = {0, 8, "\x2A\x86\x48\x86\xF7\x0D\x03\x07" }; + *algs_out = NULL; - *oids = NULL; - loids = malloc(2 * sizeof(krb5_algorithm_identifier *)); - if (loids == NULL) - goto cleanup; - loids[1] = NULL; - loids[0] = malloc(sizeof(krb5_algorithm_identifier)); - if (loids[0] == NULL) { - free(loids); - goto cleanup; - } - retval = pkinit_copy_krb5_data(&loids[0]->algorithm, &des3oid); - if (retval) { - free(loids[0]); - free(loids); + /* Count supported OIDs and allocate list (including null terminator). */ + for (count = 0; supported_cms_algs[count] != NULL; count++); + algs = k5calloc(count + 1, sizeof(*algs), &ret); + if (algs == NULL) goto cleanup; + + /* Add an algorithm identifier for each OID, with no parameters. */ + for (i = 0; i < count; i++) { + algs[i] = k5alloc(sizeof(*algs[i]), &ret); + if (algs[i] == NULL) + goto cleanup; + ret = krb5int_copy_data_contents(context, supported_cms_algs[i], + &algs[i]->algorithm); + if (ret) + goto cleanup; + algs[i]->parameters = empty_data(); } - loids[0]->parameters.length = 0; - loids[0]->parameters.data = NULL; - *oids = loids; - retval = 0; -cleanup: + *algs_out = algs; + algs = NULL; - return retval; +cleanup: + free_krb5_algorithm_identifiers(&algs); + return ret; } krb5_error_code @@ -5742,39 +5514,6 @@ cleanup: return retval; } -static int -pkcs7_decrypt(krb5_context context, - pkinit_identity_crypto_context id_cryptoctx, - PKCS7 *p7, - BIO *data) -{ - BIO *tmpmem = NULL; - int retval = 0, i = 0; - char buf[4096]; - - if(p7 == NULL) - return 0; - - if(!PKCS7_type_is_enveloped(p7)) { - pkiDebug("wrong pkcs7 content type\n"); - return 0; - } - - if(!(tmpmem = pkcs7_dataDecode(context, id_cryptoctx, p7))) { - pkiDebug("unable to decrypt pkcs7 object\n"); - return 0; - } - - for(;;) { - i = BIO_read(tmpmem, buf, sizeof(buf)); - if (i <= 0) break; - BIO_write(data, buf, i); - BIO_free_all(tmpmem); - return 1; - } - return retval; -} - krb5_error_code pkinit_process_td_trusted_certifiers( krb5_context context, @@ -5855,118 +5594,86 @@ cleanup: return retval; } -static BIO * -pkcs7_dataDecode(krb5_context context, - pkinit_identity_crypto_context id_cryptoctx, - PKCS7 *p7) +/* Originally based on OpenSSL's PKCS7_dataDecode(), now modified to remove the + * use of BIO objects and to fit the PKINIT internal interfaces. */ +static int +pkcs7_decrypt(krb5_context context, + pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7, + unsigned char **data_out, unsigned int *len_out) { - unsigned int eklen=0, tkeylen=0; - BIO *out=NULL,*etmp=NULL,*bio=NULL; - unsigned char *ek=NULL, *tkey=NULL; - ASN1_OCTET_STRING *data_body=NULL; - const EVP_CIPHER *evp_cipher=NULL; - EVP_CIPHER_CTX *evp_ctx=NULL; - X509_ALGOR *enc_alg=NULL; - STACK_OF(PKCS7_RECIP_INFO) *rsk=NULL; - PKCS7_RECIP_INFO *ri=NULL; + krb5_error_code ret; + int ok = 0, plaintext_len = 0, final_len; + unsigned int keylen = 0, eklen = 0, blocksize; + unsigned char *ek = NULL, *tkey = NULL, *plaintext = NULL, *use_key; + ASN1_OCTET_STRING *data_body = p7->d.enveloped->enc_data->enc_data; + const EVP_CIPHER *evp_cipher; + EVP_CIPHER_CTX *evp_ctx = NULL; + X509_ALGOR *enc_alg = p7->d.enveloped->enc_data->algorithm; + STACK_OF(PKCS7_RECIP_INFO) *rsk = p7->d.enveloped->recipientinfo; + PKCS7_RECIP_INFO *ri = NULL; - p7->state=PKCS7_S_HEADER; + *data_out = NULL; + *len_out = 0; - rsk=p7->d.enveloped->recipientinfo; - enc_alg=p7->d.enveloped->enc_data->algorithm; - data_body=p7->d.enveloped->enc_data->enc_data; - evp_cipher=EVP_get_cipherbyobj(enc_alg->algorithm); - if (evp_cipher == NULL) { - PKCS7err(PKCS7_F_PKCS7_DATADECODE,PKCS7_R_UNSUPPORTED_CIPHER_TYPE); - goto cleanup; - } - - if ((etmp=BIO_new(BIO_f_cipher())) == NULL) { - PKCS7err(PKCS7_F_PKCS7_DATADECODE,ERR_R_BIO_LIB); - goto cleanup; - } - - /* It was encrypted, we need to decrypt the secret key - * with the private key */ + p7->state = PKCS7_S_HEADER; /* RFC 4556 section 3.2.3.2 requires that there be exactly one * recipientInfo. */ if (sk_PKCS7_RECIP_INFO_num(rsk) != 1) { pkiDebug("invalid number of EnvelopedData RecipientInfos\n"); - goto cleanup; + return 0; } - ri = sk_PKCS7_RECIP_INFO_value(rsk, 0); - (void)pkinit_decode_data(context, id_cryptoctx, - ASN1_STRING_get0_data(ri->enc_key), - ASN1_STRING_length(ri->enc_key), &ek, &eklen); - evp_ctx=NULL; - BIO_get_cipher_ctx(etmp,&evp_ctx); - if (EVP_CipherInit_ex(evp_ctx,evp_cipher,NULL,NULL,NULL,0) <= 0) + evp_cipher = EVP_get_cipherbyobj(enc_alg->algorithm); + if (evp_cipher == NULL) + goto cleanup; + keylen = EVP_CIPHER_key_length(evp_cipher); + blocksize = EVP_CIPHER_block_size(evp_cipher); + + evp_ctx = EVP_CIPHER_CTX_new(); + if (evp_ctx == NULL) goto cleanup; - if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0) + if (!EVP_DecryptInit(evp_ctx, evp_cipher, NULL, NULL) || + EVP_CIPHER_asn1_to_param(evp_ctx, enc_alg->parameter) <= 0) goto cleanup; /* Generate a random symmetric key to avoid exposing timing data if RSA * decryption fails the padding check. */ - tkeylen = EVP_CIPHER_CTX_key_length(evp_ctx); - tkey = OPENSSL_malloc(tkeylen); - if (tkey == NULL) - goto cleanup; - if (EVP_CIPHER_CTX_rand_key(evp_ctx, tkey) <= 0) + tkey = malloc(keylen); + if (tkey == NULL || !EVP_CIPHER_CTX_rand_key(evp_ctx, tkey)) goto cleanup; - if (ek == NULL) { - ek = tkey; - eklen = tkeylen; - tkey = NULL; - } - if (eklen != (unsigned)EVP_CIPHER_CTX_key_length(evp_ctx)) { - /* Some S/MIME clients don't use the same key - * and effective key length. The key length is - * determined by the size of the decrypted RSA key. - */ - if (!EVP_CIPHER_CTX_set_key_length(evp_ctx, (int)eklen)) { - ek = tkey; - eklen = tkeylen; - tkey = NULL; - } - } - if (EVP_CipherInit_ex(evp_ctx,NULL,NULL,ek,NULL,0) <= 0) - goto cleanup; + /* Decrypt the secret key with the private key. */ + ret = pkinit_decode_data(context, id_cryptoctx, + ASN1_STRING_get0_data(ri->enc_key), + ASN1_STRING_length(ri->enc_key), &ek, &eklen); + use_key = (ret || eklen != keylen) ? tkey : ek; - if (out == NULL) - out=etmp; - else - BIO_push(out,etmp); - etmp=NULL; + /* Allocate a plaintext buffer and decrypt data_body into it. */ + plaintext = malloc(data_body->length + blocksize); + if (plaintext == NULL) + goto cleanup; + if (!EVP_DecryptInit(evp_ctx, NULL, use_key, NULL)) + goto cleanup; + if (!EVP_DecryptUpdate(evp_ctx, plaintext, &plaintext_len, + data_body->data, data_body->length)) + goto cleanup; + if (!EVP_DecryptFinal(evp_ctx, plaintext + plaintext_len, &final_len)) + goto cleanup; + plaintext_len += final_len; - if (data_body->length > 0) - bio = BIO_new_mem_buf(data_body->data, data_body->length); - else { - bio=BIO_new(BIO_s_mem()); - BIO_set_mem_eof_return(bio,0); - } - BIO_push(out,bio); - bio=NULL; + *len_out = plaintext_len; + *data_out = plaintext; + plaintext = NULL; + ok = 1; - if (0) { - cleanup: - if (out != NULL) BIO_free_all(out); - if (etmp != NULL) BIO_free_all(etmp); - if (bio != NULL) BIO_free_all(bio); - out=NULL; - } - if (ek != NULL) { - OPENSSL_cleanse(ek, eklen); - OPENSSL_free(ek); - } - if (tkey != NULL) { - OPENSSL_cleanse(tkey, tkeylen); - OPENSSL_free(tkey); - } - return(out); +cleanup: + EVP_CIPHER_CTX_free(evp_ctx); + zapfree(plaintext, plaintext_len); + zapfree(ek, eklen); + zapfree(tkey, keylen); + return ok; } #ifdef DEBUG_DH @@ -6009,19 +5716,18 @@ print_pubkey(BIGNUM * key, char *msg) } #endif -static char * -pkinit_pkcs11_code_to_text(int err) +static const char * +pkcs11err(int err) { int i; - static char uc[32]; for (i = 0; pkcs11_errstrings[i].text != NULL; i++) if (pkcs11_errstrings[i].code == err) break; if (pkcs11_errstrings[i].text != NULL) return (pkcs11_errstrings[i].text); - snprintf(uc, sizeof(uc), _("unknown code 0x%x"), err); - return (uc); + + return "unknown PKCS11 error"; } /* diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h index 7411348fab8c..c807f044ac48 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h +++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h @@ -45,11 +45,11 @@ #include <openssl/sha.h> #include <openssl/asn1.h> #include <openssl/pem.h> - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L #include <openssl/asn1t.h> -#else -#include <openssl/asn1_mac.h> +#include <openssl/cms.h> +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include <openssl/core_names.h> +#include <openssl/decoder.h> #endif #define DN_BUF_LEN 256 @@ -84,11 +84,11 @@ struct _pkinit_identity_crypto_context { char *token_label; char *cert_label; /* These are crypto-specific */ - void *p11_module; + struct plugin_file_handle *p11_module; CK_SESSION_HANDLE session; CK_FUNCTION_LIST_PTR p11; - CK_BYTE_PTR cert_id; - int cert_id_len; + uint8_t *cert_id; + size_t cert_id_len; CK_MECHANISM_TYPE mech; #endif krb5_boolean defer_id_prompt; @@ -96,9 +96,9 @@ struct _pkinit_identity_crypto_context { }; struct _pkinit_plg_crypto_context { - DH *dh_1024; - DH *dh_2048; - DH *dh_4096; + EVP_PKEY *dh_1024; + EVP_PKEY *dh_2048; + EVP_PKEY *dh_4096; ASN1_OBJECT *id_pkinit_authData; ASN1_OBJECT *id_pkinit_DHKeyData; ASN1_OBJECT *id_pkinit_rkeyData; @@ -112,7 +112,8 @@ struct _pkinit_plg_crypto_context { struct _pkinit_req_crypto_context { X509 *received_cert; - DH *dh; + EVP_PKEY *client_pkey; + EVP_PKEY *received_params; }; #endif /* _PKINIT_CRYPTO_OPENSSL_H */ diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c index e8997c9351a8..a5a979f279aa 100644 --- a/src/plugins/preauth/pkinit/pkinit_identity.c +++ b/src/plugins/preauth/pkinit/pkinit_identity.c @@ -29,15 +29,8 @@ * SUCH DAMAGES. */ -#include <errno.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <dlfcn.h> -#include <unistd.h> -#include <dirent.h> - #include "pkinit.h" +#include <dirent.h> static void free_list(char **list) @@ -317,29 +310,40 @@ parse_fs_options(krb5_context context, const char *residual) { char *certname, *keyname, *save; + char *copy = NULL, *cert_filename = NULL, *key_filename = NULL; krb5_error_code retval = ENOMEM; - if (residual == NULL || residual[0] == '\0') - return 0; + if (residual == NULL || residual[0] == '\0' || residual[0] == ',') + return EINVAL; - certname = strdup(residual); - if (certname == NULL) + copy = strdup(residual); + if (copy == NULL) goto cleanup; - certname = strtok_r(certname, ",", &save); + certname = strtok_r(copy, ",", &save); + if (certname == NULL) + goto cleanup; keyname = strtok_r(NULL, ",", &save); - idopts->cert_filename = strdup(certname); - if (idopts->cert_filename == NULL) + cert_filename = strdup(certname); + if (cert_filename == NULL) goto cleanup; - idopts->key_filename = strdup(keyname ? keyname : certname); - if (idopts->key_filename == NULL) + key_filename = strdup((keyname != NULL) ? keyname : certname); + if (key_filename == NULL) goto cleanup; + free(idopts->cert_filename); + free(idopts->key_filename); + idopts->cert_filename = cert_filename; + idopts->key_filename = key_filename; + cert_filename = key_filename = NULL; retval = 0; + cleanup: - free(certname); + free(copy); + free(cert_filename); + free(key_filename); return retval; } @@ -353,10 +357,12 @@ parse_pkcs12_options(krb5_context context, if (residual == NULL || residual[0] == '\0') return 0; + free(idopts->cert_filename); idopts->cert_filename = strdup(residual); if (idopts->cert_filename == NULL) goto cleanup; + free(idopts->key_filename); idopts->key_filename = strdup(residual); if (idopts->key_filename == NULL) goto cleanup; @@ -375,14 +381,13 @@ process_option_identity(krb5_context context, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, - const char *value) + krb5_principal princ, const char *value) { const char *residual; int idtype; krb5_error_code retval = 0; - pkiDebug("%s: processing value '%s'\n", - __FUNCTION__, value ? value : "NULL"); + TRACE_PKINIT_IDENTITY_OPTION(context, value); if (value == NULL) return EINVAL; @@ -421,7 +426,8 @@ process_option_identity(krb5_context context, switch (idtype) { case IDTYPE_ENVVAR: return process_option_identity(context, plg_cryptoctx, req_cryptoctx, - idopts, id_cryptoctx, getenv(residual)); + idopts, id_cryptoctx, princ, + secure_getenv(residual)); break; case IDTYPE_FILE: retval = parse_fs_options(context, idopts, residual); @@ -435,6 +441,7 @@ process_option_identity(krb5_context context, break; #endif case IDTYPE_DIR: + free(idopts->cert_filename); idopts->cert_filename = strdup(residual); if (idopts->cert_filename == NULL) retval = ENOMEM; @@ -446,7 +453,16 @@ process_option_identity(krb5_context context, retval = EINVAL; break; } - return retval; + if (retval) + return retval; + + retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, idopts, + id_cryptoctx, princ, TRUE); + if (retval) + return retval; + + crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); + return 0; } static krb5_error_code @@ -521,12 +537,13 @@ pkinit_identity_initialize(krb5_context context, if (idopts->identity != NULL) { retval = process_option_identity(context, plg_cryptoctx, req_cryptoctx, idopts, - id_cryptoctx, idopts->identity); + id_cryptoctx, princ, + idopts->identity); } else if (idopts->identity_alt != NULL) { for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) { retval = process_option_identity(context, plg_cryptoctx, req_cryptoctx, idopts, - id_cryptoctx, + id_cryptoctx, princ, idopts->identity_alt[i]); } } else { @@ -536,13 +553,6 @@ pkinit_identity_initialize(krb5_context context, pkiDebug("%s: no user identity options specified\n", __FUNCTION__); goto errout; } - if (retval) - goto errout; - - retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, - idopts, id_cryptoctx, princ, TRUE); - if (retval) - goto errout; } else { /* We're the anonymous principal. */ retval = 0; @@ -569,8 +579,9 @@ pkinit_identity_prompt(krb5_context context, int do_matching, krb5_principal princ) { - krb5_error_code retval = EINVAL; + krb5_error_code retval = 0; const char *signer_identity; + krb5_boolean valid; int i; pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx); @@ -623,22 +634,36 @@ pkinit_identity_prompt(krb5_context context, goto errout; } /* Not anonymous principal */ + /* Require at least one successful anchor if any are specified. */ + valid = FALSE; for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) { retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, idopts->anchors[i], CATYPE_ANCHORS); - if (retval) - goto errout; + if (!retval) + valid = TRUE; } + if (retval && !valid) + goto errout; + krb5_clear_error_message(context); + retval = 0; + + /* Require at least one successful intermediate if any are specified. */ + valid = FALSE; for (i = 0; idopts->intermediates != NULL && idopts->intermediates[i] != NULL; i++) { retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, idopts->intermediates[i], CATYPE_INTERMEDIATES); - if (retval) - goto errout; + if (!retval) + valid = TRUE; } + if (retval && !valid) + goto errout; + krb5_clear_error_message(context); + retval = 0; + for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) { retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, idopts->crls[i], diff --git a/src/plugins/preauth/pkinit/pkinit_kdf_constants.c b/src/plugins/preauth/pkinit/pkinit_kdf_constants.c deleted file mode 100644 index 1604f1670ac7..000000000000 --- a/src/plugins/preauth/pkinit/pkinit_kdf_constants.c +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ -/* plugins/preauth/pkinit/pkinit_kdf_constants.c */ -/* - * Copyright (C) 2011 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. - */ - -/* - * pkinit_kdf_test.c -- Structures and constants for implementation of - * pkinit algorithm agility. Includes definitions of algorithm identifiers - * for SHA-1, SHA-256 and SHA-512. - */ - -#include "pkinit.h" - -/* statically declare OID constants for all three algorithms */ -const krb5_octet krb5_pkinit_sha1_oid[8] = -{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x01}; -const size_t krb5_pkinit_sha1_oid_len = 8; -const krb5_octet krb5_pkinit_sha256_oid[8] = -{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x02}; -const size_t krb5_pkinit_sha256_oid_len = 8; -const krb5_octet krb5_pkinit_sha512_oid [8] = -{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x03}; -const size_t krb5_pkinit_sha512_oid_len = 8; - -#define oid_as_data(var, oid_base) \ - const krb5_data var = \ - {0, sizeof oid_base, (char *)oid_base} -oid_as_data(sha1_id, krb5_pkinit_sha1_oid); -oid_as_data(sha256_id, krb5_pkinit_sha256_oid); -oid_as_data(sha512_id, krb5_pkinit_sha512_oid); -#undef oid_as_data - -krb5_data const * const supported_kdf_alg_ids[] = { - &sha256_id, - &sha1_id, - &sha512_id, - NULL -}; diff --git a/src/plugins/preauth/pkinit/pkinit_kdf_test.c b/src/plugins/preauth/pkinit/pkinit_kdf_test.c index 7acbd0d2875f..7f38e84910aa 100644 --- a/src/plugins/preauth/pkinit/pkinit_kdf_test.c +++ b/src/plugins/preauth/pkinit/pkinit_kdf_test.c @@ -112,6 +112,10 @@ main(int argc, char **argv) goto cleanup; } + /* The test vectors in RFC 8636 implicitly use NT-PRINCIPAL names. */ + u_principal->type = KRB5_NT_PRINCIPAL; + v_principal->type = KRB5_NT_PRINCIPAL; + /* set-up the as_req and and pk_as_rep data */ memset(twenty_as, 0xaa, sizeof(twenty_as)); memset(eighteen_bs, 0xbb, sizeof(eighteen_bs)); @@ -123,8 +127,7 @@ main(int argc, char **argv) /* TEST 1: SHA-1/AES */ /* set up algorithm id */ - alg_id.algorithm.data = (char *)krb5_pkinit_sha1_oid; - alg_id.algorithm.length = krb5_pkinit_sha1_oid_len; + alg_id.algorithm = sha1_id; enctype = enctype_aes; @@ -134,7 +137,7 @@ main(int argc, char **argv) u_principal, v_principal, enctype, &as_req, &pk_as_rep, &key_block))) { - printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d", + printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n", retval); goto cleanup; } @@ -155,8 +158,7 @@ main(int argc, char **argv) /* TEST 2: SHA-256/AES */ /* set up algorithm id */ - alg_id.algorithm.data = (char *)krb5_pkinit_sha256_oid; - alg_id.algorithm.length = krb5_pkinit_sha256_oid_len; + alg_id.algorithm = sha256_id; enctype = enctype_aes; @@ -166,7 +168,7 @@ main(int argc, char **argv) u_principal, v_principal, enctype, &as_req, &pk_as_rep, &key_block))) { - printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d", + printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n", retval); goto cleanup; } @@ -187,8 +189,7 @@ main(int argc, char **argv) /* TEST 3: SHA-512/DES3 */ /* set up algorithm id */ - alg_id.algorithm.data = (char *)krb5_pkinit_sha512_oid; - alg_id.algorithm.length = krb5_pkinit_sha512_oid_len; + alg_id.algorithm = sha512_id; enctype = enctype_des3; @@ -198,7 +199,7 @@ main(int argc, char **argv) u_principal, v_principal, enctype, &as_req, &pk_as_rep, &key_block))) { - printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d", + printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n", retval); goto cleanup; } @@ -221,5 +222,6 @@ cleanup: krb5_free_principal(context, u_principal); krb5_free_principal(context, v_principal); krb5_free_keyblock_contents(context, &key_block); - exit(retval); + krb5_free_context(context); + return retval ? 1 : 0; } diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c index 2f88545da841..4c3d46bf5ade 100644 --- a/src/plugins/preauth/pkinit/pkinit_lib.c +++ b/src/plugins/preauth/pkinit/pkinit_lib.c @@ -82,6 +82,8 @@ pkinit_init_plg_opts(pkinit_plg_opts **plgopts) opts->dh_or_rsa = DH_PROTOCOL; opts->allow_upn = 0; opts->require_crl_checking = 0; + opts->require_freshness = 0; + opts->disable_freshness = 0; opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; @@ -109,15 +111,6 @@ free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in) } void -free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in) -{ - if (*in == NULL) return; - free((*in)->signedAuthPack.data); - free((*in)->kdcCert.data); - free(*in); -} - -void free_krb5_reply_key_pack(krb5_reply_key_pack **in) { if (*in == NULL) return; @@ -127,24 +120,12 @@ free_krb5_reply_key_pack(krb5_reply_key_pack **in) } void -free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in) -{ - if (*in == NULL) return; - free((*in)->replyKey.contents); - free(*in); -} - -void free_krb5_auth_pack(krb5_auth_pack **in) { if ((*in) == NULL) return; - if ((*in)->clientPublicValue != NULL) { - free((*in)->clientPublicValue->algorithm.algorithm.data); - free((*in)->clientPublicValue->algorithm.parameters.data); - free((*in)->clientPublicValue->subjectPublicKey.data); - free((*in)->clientPublicValue); - } + krb5_free_data_contents(NULL, &(*in)->clientPublicValue); free((*in)->pkAuthenticator.paChecksum.contents); + krb5_free_data(NULL, (*in)->pkAuthenticator.freshnessToken); if ((*in)->supportedCMSTypes != NULL) free_krb5_algorithm_identifiers(&((*in)->supportedCMSTypes)); if ((*in)->supportedKDFs) { @@ -158,15 +139,6 @@ free_krb5_auth_pack(krb5_auth_pack **in) } void -free_krb5_auth_pack_draft9(krb5_context context, - krb5_auth_pack_draft9 **in) -{ - if ((*in) == NULL) return; - krb5_free_principal(context, (*in)->pkAuthenticator.kdcName); - free(*in); -} - -void free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in) { if (*in == NULL) return; @@ -185,14 +157,6 @@ free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in) } void -free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in) -{ - if (*in == NULL) return; - free((*in)->u.encKeyPack.data); - free(*in); -} - -void free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in) { int i = 0; @@ -230,15 +194,6 @@ free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in) } void -free_krb5_subject_pk_info(krb5_subject_pk_info **in) -{ - if ((*in) == NULL) return; - free((*in)->algorithm.parameters.data); - free((*in)->subjectPublicKey.data); - free(*in); -} - -void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in) { if (*in == NULL) return; @@ -259,17 +214,6 @@ init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in) } void -init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in) -{ - (*in) = malloc(sizeof(krb5_pa_pk_as_req_draft9)); - if ((*in) == NULL) return; - (*in)->signedAuthPack.data = NULL; - (*in)->signedAuthPack.length = 0; - (*in)->kdcCert.data = NULL; - (*in)->kdcCert.length = 0; -} - -void init_krb5_reply_key_pack(krb5_reply_key_pack **in) { (*in) = malloc(sizeof(krb5_reply_key_pack)); @@ -281,15 +225,6 @@ init_krb5_reply_key_pack(krb5_reply_key_pack **in) } void -init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in) -{ - (*in) = malloc(sizeof(krb5_reply_key_pack_draft9)); - if ((*in) == NULL) return; - (*in)->replyKey.contents = NULL; - (*in)->replyKey.length = 0; -} - -void init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in) { (*in) = malloc(sizeof(krb5_pa_pk_as_rep)); @@ -303,28 +238,6 @@ init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in) (*in)->u.dh_Info.kdfID = NULL; } -void -init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in) -{ - (*in) = malloc(sizeof(krb5_pa_pk_as_rep_draft9)); - if ((*in) == NULL) return; - (*in)->u.dhSignedData.length = 0; - (*in)->u.dhSignedData.data = NULL; - (*in)->u.encKeyPack.length = 0; - (*in)->u.encKeyPack.data = NULL; -} - -void -init_krb5_subject_pk_info(krb5_subject_pk_info **in) -{ - (*in) = malloc(sizeof(krb5_subject_pk_info)); - if ((*in) == NULL) return; - (*in)->algorithm.parameters.data = NULL; - (*in)->algorithm.parameters.length = 0; - (*in)->subjectPublicKey.data = NULL; - (*in)->subjectPublicKey.length = 0; -} - krb5_error_code pkinit_copy_krb5_data(krb5_data *dst, const krb5_data *src) { diff --git a/src/plugins/preauth/pkinit/pkinit_matching.c b/src/plugins/preauth/pkinit/pkinit_matching.c index c1ce84b82641..b42485a50a79 100644 --- a/src/plugins/preauth/pkinit/pkinit_matching.c +++ b/src/plugins/preauth/pkinit/pkinit_matching.c @@ -296,7 +296,7 @@ parse_rule_component(krb5_context context, rc->kwval_type = kwval_type; /* - * Before procesing the value for this keyword, + * Before processing the value for this keyword, * (compiling the regular expression or processing the list) * we need to find the end of it. That means parsing for the * beginning of the next keyword (or the end of the rule). @@ -448,50 +448,53 @@ cleanup: } static int -regexp_match(krb5_context context, rule_component *rc, char *value) +regexp_match(krb5_context context, rule_component *rc, char *value, int idx) { int code; - pkiDebug("%s: checking %s rule '%s' with value '%s'\n", - __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value); - code = regexec(&rc->regexp, value, 0, NULL, 0); - pkiDebug("%s: the result is%s a match\n", __FUNCTION__, - code == REG_NOMATCH ? " NOT" : ""); + if (code == 0) { + TRACE_PKINIT_REGEXP_MATCH(context, keyword2string(rc->kw_type), + rc->regsrc, value, idx); + } else { + TRACE_PKINIT_REGEXP_NOMATCH(context, keyword2string(rc->kw_type), + rc->regsrc, value, idx); + } return (code == 0 ? 1: 0); } static int -component_match(krb5_context context, - rule_component *rc, - pkinit_cert_matching_data *md) +component_match(krb5_context context, rule_component *rc, + pkinit_cert_matching_data *md, int idx) { int match = 0; int i; - krb5_principal p; char *princ_string; switch (rc->kwval_type) { case kwvaltype_regexp: switch (rc->kw_type) { case kw_subject: - match = regexp_match(context, rc, md->subject_dn); + match = regexp_match(context, rc, md->subject_dn, idx); break; case kw_issuer: - match = regexp_match(context, rc, md->issuer_dn); + match = regexp_match(context, rc, md->issuer_dn, idx); break; case kw_san: - if (md->sans == NULL) - break; - for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) { - krb5_unparse_name(context, p, &princ_string); - match = regexp_match(context, rc, princ_string); + for (i = 0; md->sans != NULL && md->sans[i] != NULL; i++) { + krb5_unparse_name(context, md->sans[i], &princ_string); + match = regexp_match(context, rc, princ_string, idx); krb5_free_unparsed_name(context, princ_string); if (match) break; } + for (i = 0; md->upns != NULL && md->upns[i] != NULL; i++) { + match = regexp_match(context, rc, md->upns[i], idx); + if (match) + break; + } break; default: pkiDebug("%s: keyword %s, keyword value %s mismatch\n", @@ -570,18 +573,9 @@ check_all_certs(krb5_context context, */ for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) { pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn); -#if 0 - pkiDebug("%s: issuer: '%s'\n", __FUNCTION__, md->subject_dn); - for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) { - char *san_string; - krb5_unparse_name(context, p, &san_string); - pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string); - krb5_free_unparsed_name(context, san_string); - } -#endif certs_checked++; for (rc = rs->crs; rc != NULL; rc = rc->next) { - comp_match = component_match(context, rc, md); + comp_match = component_match(context, rc, md, i); if (comp_match) { pkiDebug("%s: match for keyword type %s\n", __FUNCTION__, keyword2string(rc->kw_type)); @@ -607,8 +601,7 @@ check_all_certs(krb5_context context, nextcert: continue; } - pkiDebug("%s: After checking %d certs, we found %d matches\n", - __FUNCTION__, certs_checked, total_cert_matches); + TRACE_PKINIT_CERT_NUM_MATCHING(context, certs_checked, total_cert_matches); if (total_cert_matches == 1) { *match_found = 1; *match_index = save_index; @@ -649,7 +642,7 @@ pkinit_cert_matching(krb5_context context, /* parse each rule line one at a time and check all the certs against it */ for (x = 0; rules[x] != NULL; x++) { - pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]); + TRACE_PKINIT_CERT_RULE(context, rules[x]); /* Free rules from previous time through... */ if (rs != NULL) { @@ -659,8 +652,7 @@ pkinit_cert_matching(krb5_context context, retval = parse_rule_set(context, rules[x], &rs); if (retval) { if (retval == EINVAL) { - pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n", - __FUNCTION__, rules[x]); + TRACE_PKINIT_CERT_RULE_INVALID(context, rules[x]); continue; } goto cleanup; @@ -744,7 +736,7 @@ pkinit_client_cert_match(krb5_context context, goto cleanup; for (rc = rs->crs; rc != NULL; rc = rc->next) { - comp_match = component_match(context, rc, md); + comp_match = component_match(context, rc, md, 0); if ((comp_match && rs->relation == relation_or) || (!comp_match && rs->relation == relation_and)) { break; diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c index 4e9685885f80..1b3bf6d4d0ee 100644 --- a/src/plugins/preauth/pkinit/pkinit_srv.c +++ b/src/plugins/preauth/pkinit/pkinit_srv.c @@ -161,6 +161,10 @@ pkinit_server_get_edata(krb5_context context, if (plgctx == NULL) retval = EINVAL; + /* Send a freshness token if the client requested one. */ + if (!retval) + cb->send_freshness_token(context, rock); + (*respond)(arg, retval, NULL); } @@ -174,8 +178,9 @@ verify_client_san(krb5_context context, int *valid_san) { krb5_error_code retval; - krb5_principal *princs = NULL; - krb5_principal *upns = NULL; + krb5_principal *princs = NULL, upn; + krb5_boolean match; + char **upns = NULL; int i; #ifdef DEBUG_SAN_INFO char *client_string = NULL, *san_string; @@ -199,24 +204,6 @@ verify_client_san(krb5_context context, goto out; } - /* XXX Verify this is consistent with client side XXX */ -#if 0 - retval = call_san_checking_plugins(context, plgctx, reqctx, princs, - upns, NULL, &plugin_decision, &ignore); - pkiDebug("%s: call_san_checking_plugins() returned retval %d\n", - __FUNCTION__); - if (retval) { - retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; - goto cleanup; - } - pkiDebug("%s: call_san_checking_plugins() returned decision %d\n", - __FUNCTION__, plugin_decision); - if (plugin_decision != NO_DECISION) { - retval = plugin_decision; - goto out; - } -#endif - #ifdef DEBUG_SAN_INFO krb5_unparse_name(context, client, &client_string); #endif @@ -251,12 +238,18 @@ verify_client_san(krb5_context context, pkiDebug("%s: Checking upn sans\n", __FUNCTION__); for (i = 0; upns[i] != NULL; i++) { #ifdef DEBUG_SAN_INFO - krb5_unparse_name(context, upns[i], &san_string); pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n", - __FUNCTION__, client_string, san_string); - krb5_free_unparsed_name(context, san_string); + __FUNCTION__, client_string, upns[i]); #endif - if (cb->match_client(context, rock, upns[i])) { + retval = krb5_parse_name_flags(context, upns[i], + KRB5_PRINCIPAL_PARSE_ENTERPRISE, &upn); + if (retval) { + TRACE_PKINIT_SERVER_UPN_PARSE_FAIL(context, upns[i], retval); + continue; + } + match = cb->match_client(context, rock, upn); + krb5_free_principal(context, upn); + if (match) { TRACE_PKINIT_SERVER_MATCHING_UPN_FOUND(context); *valid_san = 1; retval = 0; @@ -265,15 +258,7 @@ verify_client_san(krb5_context context, } pkiDebug("%s: no upn san match found\n", __FUNCTION__); - /* We found no match */ - if (princs != NULL || upns != NULL) { - *valid_san = 0; - /* XXX ??? If there was one or more name in the cert, but - * none matched the client name, then return mismatch? */ - retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; - } retval = 0; - out: if (princs != NULL) { for (i = 0; princs[i] != NULL; i++) @@ -282,7 +267,7 @@ out: } if (upns != NULL) { for (i = 0; upns[i] != NULL; i++) - krb5_free_principal(context, upns[i]); + free(upns[i]); free(upns); } #ifdef DEBUG_SAN_INFO @@ -335,12 +320,12 @@ static krb5_error_code authorize_cert(krb5_context context, certauth_handle *certauth_modules, pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_principal client) + krb5_principal client, krb5_boolean *hwauth_out) { krb5_error_code ret; certauth_handle h; struct certauth_req_opts opts; - krb5_boolean accepted = FALSE; + krb5_boolean accepted = FALSE, hwauth = FALSE; uint8_t *cert; size_t i, cert_len; void *db_ent = NULL; @@ -362,9 +347,10 @@ authorize_cert(krb5_context context, certauth_handle *certauth_modules, /* * Check the certificate against each certauth module. For the certificate - * to be authorized at least one module must return 0, and no module can an - * error code other than KRB5_PLUGIN_NO_HANDLE (pass). Add indicators from - * modules that return 0 or pass. + * to be authorized at least one module must return 0 or + * KRB5_CERTAUTH_HWAUTH, and no module can return an error code other than + * KRB5_PLUGIN_NO_HANDLE (pass) or KRB5_CERTAUTH_HWAUTH_PASS (pass but + * set hw-authent). Add indicators from all modules. */ ret = KRB5_PLUGIN_NO_HANDLE; for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) { @@ -374,6 +360,10 @@ authorize_cert(krb5_context context, certauth_handle *certauth_modules, &opts, db_ent, &ais); if (ret == 0) accepted = TRUE; + else if (ret == KRB5_CERTAUTH_HWAUTH) + accepted = hwauth = TRUE; + else if (ret == KRB5_CERTAUTH_HWAUTH_PASS) + hwauth = TRUE; else if (ret != KRB5_PLUGIN_NO_HANDLE) goto cleanup; @@ -389,6 +379,7 @@ authorize_cert(krb5_context context, certauth_handle *certauth_modules, } } + *hwauth_out = hwauth; ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH; cleanup: @@ -396,6 +387,31 @@ cleanup: return ret; } +/* Return an error if freshness tokens are required and one was not received. + * Log an appropriate message indicating whether a valid token was received. */ +static krb5_error_code +check_log_freshness(krb5_context context, pkinit_kdc_context plgctx, + krb5_kdc_req *request, krb5_boolean valid_freshness_token) +{ + krb5_error_code ret; + char *name = NULL; + + ret = krb5_unparse_name(context, request->client, &name); + if (ret) + return ret; + if (plgctx->opts->require_freshness && !valid_freshness_token) { + com_err("", 0, _("PKINIT: no freshness token, rejecting auth from %s"), + name); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + } else if (valid_freshness_token) { + com_err("", 0, _("PKINIT: freshness token received from %s"), name); + } else { + com_err("", 0, _("PKINIT: no freshness token received from %s"), name); + } + krb5_free_unparsed_name(context, name); + return ret; +} + static void pkinit_server_verify_padata(krb5_context context, krb5_data *req_pkt, @@ -411,17 +427,16 @@ pkinit_server_verify_padata(krb5_context context, krb5_error_code retval = 0; krb5_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; krb5_pa_pk_as_req *reqp = NULL; - krb5_pa_pk_as_req_draft9 *reqp9 = NULL; krb5_auth_pack *auth_pack = NULL; - krb5_auth_pack_draft9 *auth_pack9 = NULL; pkinit_kdc_context plgctx = NULL; pkinit_kdc_req_context reqctx = NULL; krb5_checksum cksum = {0, 0, 0, NULL}; krb5_data *der_req = NULL; - krb5_data k5data; + krb5_data k5data, *ftoken; int is_signed = 1; krb5_pa_data **e_data = NULL; krb5_kdcpreauth_modreq modreq = NULL; + krb5_boolean valid_freshness_token = FALSE, hwauth = FALSE; char **sp; pkiDebug("pkinit_verify_padata: entered!\n"); @@ -453,65 +468,39 @@ pkinit_server_verify_padata(krb5_context context, PADATA_TO_KRB5DATA(data, &k5data); - switch ((int)data->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - TRACE_PKINIT_SERVER_PADATA_VERIFY(context); - retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); - if (retval) { - pkiDebug("decode_krb5_pa_pk_as_req failed\n"); - goto cleanup; - } -#ifdef DEBUG_ASN1 - print_buffer_bin(reqp->signedAuthPack.data, - reqp->signedAuthPack.length, - "/tmp/kdc_signed_data"); -#endif - retval = cms_signeddata_verify(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT, - plgctx->opts->require_crl_checking, - (unsigned char *) - reqp->signedAuthPack.data, reqp->signedAuthPack.length, - (unsigned char **)&authp_data.data, - &authp_data.length, - (unsigned char **)&krb5_authz.data, - &krb5_authz.length, &is_signed); - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - TRACE_PKINIT_SERVER_PADATA_VERIFY_OLD(context); - retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9); - if (retval) { - pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n"); - goto cleanup; - } -#ifdef DEBUG_ASN1 - print_buffer_bin(reqp9->signedAuthPack.data, - reqp9->signedAuthPack.length, - "/tmp/kdc_signed_data_draft9"); -#endif - - retval = cms_signeddata_verify(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, - plgctx->opts->require_crl_checking, - (unsigned char *) - reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, - (unsigned char **)&authp_data.data, - &authp_data.length, - (unsigned char **)&krb5_authz.data, - &krb5_authz.length, NULL); - break; - default: + if (data->pa_type != KRB5_PADATA_PK_AS_REQ) { pkiDebug("unrecognized pa_type = %d\n", data->pa_type); retval = EINVAL; goto cleanup; } + + TRACE_PKINIT_SERVER_PADATA_VERIFY(context); + retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); + if (retval) { + pkiDebug("decode_krb5_pa_pk_as_req failed\n"); + goto cleanup; + } +#ifdef DEBUG_ASN1 + print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length, + "/tmp/kdc_signed_data"); +#endif + retval = cms_signeddata_verify(context, plgctx->cryptoctx, + reqctx->cryptoctx, plgctx->idctx, + CMS_SIGN_CLIENT, + plgctx->opts->require_crl_checking, + (unsigned char *)reqp->signedAuthPack.data, + reqp->signedAuthPack.length, + (unsigned char **)&authp_data.data, + &authp_data.length, + (unsigned char **)&krb5_authz.data, + &krb5_authz.length, &is_signed); if (retval) { TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(context); goto cleanup; } if (is_signed) { retval = authorize_cert(context, moddata->certauth_modules, plgctx, - reqctx, cb, rock, request->client); + reqctx, cb, rock, request->client, &hwauth); if (retval) goto cleanup; @@ -530,108 +519,93 @@ pkinit_server_verify_padata(krb5_context context, #endif OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); - switch ((int)data->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); - if (retval) { - pkiDebug("failed to decode krb5_auth_pack\n"); - goto cleanup; - } - - retval = krb5_check_clockskew(context, - auth_pack->pkAuthenticator.ctime); - if (retval) - goto cleanup; + retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); + if (retval) { + pkiDebug("failed to decode krb5_auth_pack\n"); + goto cleanup; + } - /* check dh parameters */ - if (auth_pack->clientPublicValue != NULL) { - retval = server_check_dh(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, - &auth_pack->clientPublicValue->algorithm.parameters, - plgctx->opts->dh_min_bits); + retval = krb5_check_clockskew(context, auth_pack->pkAuthenticator.ctime); + if (retval) + goto cleanup; - if (retval) { - pkiDebug("bad dh parameters\n"); - goto cleanup; - } - } else if (!is_signed) { - /*Anonymous pkinit requires DH*/ - retval = KRB5KDC_ERR_PREAUTH_FAILED; - krb5_set_error_message(context, retval, - _("Anonymous pkinit without DH public " - "value not supported.")); - goto cleanup; - } - der_req = cb->request_body(context, rock); - retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, - 0, der_req, &cksum); + /* check dh parameters */ + if (auth_pack->clientPublicValue.length > 0) { + retval = server_check_dh(context, plgctx->cryptoctx, + reqctx->cryptoctx, plgctx->idctx, + &auth_pack->clientPublicValue, + plgctx->opts->dh_min_bits); if (retval) { - pkiDebug("unable to calculate AS REQ checksum\n"); + pkiDebug("bad dh parameters\n"); goto cleanup; } - if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || - k5_bcmp(cksum.contents, - auth_pack->pkAuthenticator.paChecksum.contents, - cksum.length) != 0) { - pkiDebug("failed to match the checksum\n"); + } else if (!is_signed) { + /*Anonymous pkinit requires DH*/ + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, + _("Anonymous pkinit without DH public " + "value not supported.")); + goto cleanup; + } + der_req = cb->request_body(context, rock); + retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req, + &cksum); + if (retval) { + pkiDebug("unable to calculate AS REQ checksum\n"); + goto cleanup; + } + if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || + k5_bcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents, + cksum.length) != 0) { + pkiDebug("failed to match the checksum\n"); #ifdef DEBUG_CKSUM - pkiDebug("calculating checksum on buf size (%d)\n", - req_pkt->length); - print_buffer(req_pkt->data, req_pkt->length); - pkiDebug("received checksum type=%d size=%d ", - auth_pack->pkAuthenticator.paChecksum.checksum_type, + pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); + print_buffer(req_pkt->data, req_pkt->length); + pkiDebug("received checksum type=%d size=%d ", + auth_pack->pkAuthenticator.paChecksum.checksum_type, + auth_pack->pkAuthenticator.paChecksum.length); + print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, auth_pack->pkAuthenticator.paChecksum.length); - print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, - auth_pack->pkAuthenticator.paChecksum.length); - pkiDebug("expected checksum type=%d size=%d ", - cksum.checksum_type, cksum.length); - print_buffer(cksum.contents, cksum.length); + pkiDebug("expected checksum type=%d size=%d ", + cksum.checksum_type, cksum.length); + print_buffer(cksum.contents, cksum.length); #endif - retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; - goto cleanup; - } + retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; + goto cleanup; + } - /* check if kdcPkId present and match KDC's subjectIdentifier */ - if (reqp->kdcPkId.data != NULL) { - int valid_kdcPkId = 0; - retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, - (unsigned char *)reqp->kdcPkId.data, - reqp->kdcPkId.length, &valid_kdcPkId); - if (retval) - goto cleanup; - if (!valid_kdcPkId) - pkiDebug("kdcPkId in AS_REQ does not match KDC's cert" - "RFC says to ignore and proceed\n"); + ftoken = auth_pack->pkAuthenticator.freshnessToken; + if (ftoken != NULL) { + retval = cb->check_freshness_token(context, rock, ftoken); + if (retval) + goto cleanup; + valid_freshness_token = TRUE; + } - } - /* remember the decoded auth_pack for verify_padata routine */ - reqctx->rcv_auth_pack = auth_pack; - auth_pack = NULL; - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9); - if (retval) { - pkiDebug("failed to decode krb5_auth_pack_draft9\n"); + /* check if kdcPkId present and match KDC's subjectIdentifier */ + if (reqp->kdcPkId.data != NULL) { + int valid_kdcPkId = 0; + retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, + reqctx->cryptoctx, plgctx->idctx, + (unsigned char *)reqp->kdcPkId.data, + reqp->kdcPkId.length, &valid_kdcPkId); + if (retval) goto cleanup; + if (!valid_kdcPkId) { + pkiDebug("kdcPkId in AS_REQ does not match KDC's cert; " + "RFC says to ignore and proceed\n"); } - if (auth_pack9->clientPublicValue != NULL) { - retval = server_check_dh(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, - &auth_pack9->clientPublicValue->algorithm.parameters, - plgctx->opts->dh_min_bits); + } + /* remember the decoded auth_pack for verify_padata routine */ + reqctx->rcv_auth_pack = auth_pack; + auth_pack = NULL; - if (retval) { - pkiDebug("bad dh parameters\n"); - goto cleanup; - } - } - /* remember the decoded auth_pack for verify_padata routine */ - reqctx->rcv_auth_pack9 = auth_pack9; - auth_pack9 = NULL; - break; + if (is_signed) { + retval = check_log_freshness(context, plgctx, request, + valid_freshness_token); + if (retval) + goto cleanup; } if (is_signed && plgctx->auth_indicators != NULL) { @@ -645,6 +619,8 @@ pkinit_server_verify_padata(krb5_context context, /* remember to set the PREAUTH flag in the reply */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; + if (hwauth) + enc_tkt_reply->flags |= TKT_FLG_HW_AUTH; modreq = (krb5_kdcpreauth_modreq)reqctx; reqctx = NULL; @@ -656,21 +632,13 @@ cleanup: pkiDebug("pkinit_create_edata failed\n"); } - switch ((int)data->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - free_krb5_pa_pk_as_req(&reqp); - free(cksum.contents); - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - free_krb5_pa_pk_as_req_draft9(&reqp9); - } + free_krb5_pa_pk_as_req(&reqp); + free(cksum.contents); free(authp_data.data); free(krb5_authz.data); if (reqctx != NULL) pkinit_fini_kdc_req_context(context, reqctx); free_krb5_auth_pack(&auth_pack); - free_krb5_auth_pack_draft9(context, &auth_pack9); (*respond)(arg, retval, modreq, e_data, NULL); } @@ -791,32 +759,26 @@ pkinit_server_return_padata(krb5_context context, krb5_error_code retval = 0; krb5_data scratch = {0, 0, NULL}; krb5_pa_pk_as_req *reqp = NULL; - krb5_pa_pk_as_req_draft9 *reqp9 = NULL; int i = 0; - unsigned char *subjectPublicKey = NULL; unsigned char *dh_pubkey = NULL, *server_key = NULL; - unsigned int subjectPublicKey_len = 0; unsigned int server_key_len = 0, dh_pubkey_len = 0; + krb5_keyblock reply_key = { 0 }; krb5_kdc_dh_key_info dhkey_info; krb5_data *encoded_dhkey_info = NULL; krb5_pa_pk_as_rep *rep = NULL; - krb5_pa_pk_as_rep_draft9 *rep9 = NULL; krb5_data *out_data = NULL; krb5_data secret; krb5_enctype enctype = -1; krb5_reply_key_pack *key_pack = NULL; - krb5_reply_key_pack_draft9 *key_pack9 = NULL; krb5_data *encoded_key_pack = NULL; pkinit_kdc_context plgctx; pkinit_kdc_req_context reqctx; - int fixed_keypack = 0; - *send_pa = NULL; if (padata->pa_type == KRB5_PADATA_PKINIT_KX) { return return_pkinit_kx(context, request, reply, @@ -839,12 +801,6 @@ pkinit_server_return_padata(krb5_context context, TRACE_PKINIT_SERVER_RETURN_PADATA(context); reqctx = (pkinit_kdc_req_context)modreq; - if (encrypting_key->contents) { - free(encrypting_key->contents); - encrypting_key->length = 0; - encrypting_key->contents = NULL; - } - for(i = 0; i < request->nktypes; i++) { enctype = request->ktype[i]; if (!krb5_c_valid_enctype(enctype)) @@ -860,62 +816,27 @@ pkinit_server_return_padata(krb5_context context, goto cleanup; } - switch((int)reqctx->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - init_krb5_pa_pk_as_rep(&rep); - if (rep == NULL) { - retval = ENOMEM; - goto cleanup; - } - /* let's assume it's RSA. we'll reset it to DH if needed */ - rep->choice = choice_pa_pk_as_rep_encKeyPack; - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - init_krb5_pa_pk_as_rep_draft9(&rep9); - if (rep9 == NULL) { - retval = ENOMEM; - goto cleanup; - } - rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; - break; - default: - retval = KRB5KDC_ERR_PREAUTH_FAILED; + init_krb5_pa_pk_as_rep(&rep); + if (rep == NULL) { + retval = ENOMEM; goto cleanup; } + /* let's assume it's RSA. we'll reset it to DH if needed */ + rep->choice = choice_pa_pk_as_rep_encKeyPack; if (reqctx->rcv_auth_pack != NULL && - reqctx->rcv_auth_pack->clientPublicValue != NULL) { - subjectPublicKey = (unsigned char *) - reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data; - subjectPublicKey_len = - reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length; + reqctx->rcv_auth_pack->clientPublicValue.length > 0) { rep->choice = choice_pa_pk_as_rep_dhInfo; - } else if (reqctx->rcv_auth_pack9 != NULL && - reqctx->rcv_auth_pack9->clientPublicValue != NULL) { - subjectPublicKey = (unsigned char *) - reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data; - subjectPublicKey_len = - reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length; - rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData; - } - /* if this DH, then process finish computing DH key */ - if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo || - rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) { pkiDebug("received DH key delivery AS REQ\n"); retval = server_process_dh(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, subjectPublicKey, - subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, + reqctx->cryptoctx, plgctx->idctx, + &dh_pubkey, &dh_pubkey_len, &server_key, &server_key_len); if (retval) { - pkiDebug("failed to process/create dh paramters\n"); + pkiDebug("failed to process/create dh parameters\n"); goto cleanup; } - } - if ((rep9 != NULL && - rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) || - (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) { /* * This is DH, so don't generate the key until after we @@ -940,143 +861,69 @@ pkinit_server_return_padata(krb5_context context, "/tmp/kdc_dh_key_info"); #endif - switch ((int)padata->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - retval = cms_signeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1, - (unsigned char *) - encoded_dhkey_info->data, - encoded_dhkey_info->length, - (unsigned char **) - &rep->u.dh_Info.dhSignedData.data, - &rep->u.dh_Info.dhSignedData.length); - if (retval) { - pkiDebug("failed to create pkcs7 signed data\n"); - goto cleanup; - } - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - retval = cms_signeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1, - (unsigned char *) - encoded_dhkey_info->data, - encoded_dhkey_info->length, - (unsigned char **) - &rep9->u.dhSignedData.data, - &rep9->u.dhSignedData.length); - if (retval) { - pkiDebug("failed to create pkcs7 signed data\n"); - goto cleanup; - } - break; + retval = cms_signeddata_create(context, plgctx->cryptoctx, + reqctx->cryptoctx, plgctx->idctx, + CMS_SIGN_SERVER, + (unsigned char *) + encoded_dhkey_info->data, + encoded_dhkey_info->length, + (unsigned char **) + &rep->u.dh_Info.dhSignedData.data, + &rep->u.dh_Info.dhSignedData.length); + if (retval) { + pkiDebug("failed to create pkcs7 signed data\n"); + goto cleanup; } } else { pkiDebug("received RSA key delivery AS REQ\n"); - retval = krb5_c_make_random_key(context, enctype, encrypting_key); + init_krb5_reply_key_pack(&key_pack); + if (key_pack == NULL) { + retval = ENOMEM; + goto cleanup; + } + + retval = krb5_c_make_random_key(context, enctype, &key_pack->replyKey); if (retval) { pkiDebug("unable to make a session key\n"); goto cleanup; } - /* check if PA_TYPE of KRB5_PADATA_AS_CHECKSUM (132) is present which - * means the client is requesting that a checksum is send back instead - * of the nonce. - */ - for (i = 0; request->padata[i] != NULL; i++) { - pkiDebug("%s: Checking pa_type 0x%08x\n", - __FUNCTION__, request->padata[i]->pa_type); - if (request->padata[i]->pa_type == KRB5_PADATA_AS_CHECKSUM) - fixed_keypack = 1; + retval = krb5_c_make_checksum(context, 0, &key_pack->replyKey, + KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, + req_pkt, &key_pack->asChecksum); + if (retval) { + pkiDebug("unable to calculate AS REQ checksum\n"); + goto cleanup; } - pkiDebug("%s: return checksum instead of nonce = %d\n", - __FUNCTION__, fixed_keypack); - - /* if this is an RFC reply or draft9 client requested a checksum - * in the reply instead of the nonce, create an RFC-style keypack - */ - if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) { - init_krb5_reply_key_pack(&key_pack); - if (key_pack == NULL) { - retval = ENOMEM; - goto cleanup; - } - - retval = krb5_c_make_checksum(context, 0, - encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, - req_pkt, &key_pack->asChecksum); - if (retval) { - pkiDebug("unable to calculate AS REQ checksum\n"); - goto cleanup; - } #ifdef DEBUG_CKSUM - pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); - print_buffer(req_pkt->data, req_pkt->length); - pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); - print_buffer(key_pack->asChecksum.contents, - key_pack->asChecksum.length); - pkiDebug("encrypting key (%d)\n", encrypting_key->length); - print_buffer(encrypting_key->contents, encrypting_key->length); + pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); + print_buffer(req_pkt->data, req_pkt->length); + pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); + print_buffer(key_pack->asChecksum.contents, + key_pack->asChecksum.length); + pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length); + print_buffer(key_pack->replyKey.contents, key_pack->replyKey.length); #endif - krb5_copy_keyblock_contents(context, encrypting_key, - &key_pack->replyKey); - - retval = k5int_encode_krb5_reply_key_pack(key_pack, - &encoded_key_pack); - if (retval) { - pkiDebug("failed to encode reply_key_pack\n"); - goto cleanup; - } + retval = k5int_encode_krb5_reply_key_pack(key_pack, + &encoded_key_pack); + if (retval) { + pkiDebug("failed to encode reply_key_pack\n"); + goto cleanup; } - switch ((int)padata->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - rep->choice = choice_pa_pk_as_rep_encKeyPack; - retval = cms_envelopeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, - (unsigned char *) - encoded_key_pack->data, - encoded_key_pack->length, - (unsigned char **) - &rep->u.encKeyPack.data, - &rep->u.encKeyPack.length); - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - /* if the request is from the broken draft9 client that - * expects back a nonce, create it now - */ - if (!fixed_keypack) { - init_krb5_reply_key_pack_draft9(&key_pack9); - if (key_pack9 == NULL) { - retval = ENOMEM; - goto cleanup; - } - key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce; - krb5_copy_keyblock_contents(context, encrypting_key, - &key_pack9->replyKey); - - retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9, - &encoded_key_pack); - if (retval) { - pkiDebug("failed to encode reply_key_pack\n"); - goto cleanup; - } - } - - rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; - retval = cms_envelopeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, - (unsigned char *) - encoded_key_pack->data, - encoded_key_pack->length, - (unsigned char **) - &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length); - break; - } + rep->choice = choice_pa_pk_as_rep_encKeyPack; + retval = cms_envelopeddata_create(context, plgctx->cryptoctx, + reqctx->cryptoctx, plgctx->idctx, + padata->pa_type, + (unsigned char *) + encoded_key_pack->data, + encoded_key_pack->length, + (unsigned char **) + &rep->u.encKeyPack.data, + &rep->u.encKeyPack.length); if (retval) { pkiDebug("failed to create pkcs7 enveloped data: %s\n", error_message(retval)); @@ -1086,23 +933,17 @@ pkinit_server_return_padata(krb5_context context, print_buffer_bin((unsigned char *)encoded_key_pack->data, encoded_key_pack->length, "/tmp/kdc_key_pack"); - switch ((int)padata->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - print_buffer_bin(rep->u.encKeyPack.data, - rep->u.encKeyPack.length, - "/tmp/kdc_enc_key_pack"); - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - print_buffer_bin(rep9->u.encKeyPack.data, - rep9->u.encKeyPack.length, - "/tmp/kdc_enc_key_pack"); - break; - } + print_buffer_bin(rep->u.encKeyPack.data, rep->u.encKeyPack.length, + "/tmp/kdc_enc_key_pack"); #endif + + retval = cb->replace_reply_key(context, rock, &key_pack->replyKey, + FALSE); + if (retval) + goto cleanup; } - if ((rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo) && + if (rep->choice == choice_pa_pk_as_rep_dhInfo && ((reqctx->rcv_auth_pack != NULL && reqctx->rcv_auth_pack->supportedKDFs != NULL))) { @@ -1121,15 +962,7 @@ pkinit_server_return_padata(krb5_context context, } } - switch ((int)padata->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); - break; - } + retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); if (retval) { pkiDebug("failed to encode AS_REP\n"); goto cleanup; @@ -1141,13 +974,11 @@ pkinit_server_return_padata(krb5_context context, #endif /* If this is DH, we haven't computed the key yet, so do it now. */ - if ((rep9 != NULL && - rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) || - (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) { + if (rep->choice == choice_pa_pk_as_rep_dhInfo) { - /* If we're not doing draft 9, and mutually supported KDFs were found, - * use the algorithm agility KDF. */ - if (rep != NULL && rep->u.dh_Info.kdfID) { + /* If mutually supported KDFs were found, use the algorithm agility + * KDF. */ + if (rep->u.dh_Info.kdfID) { secret.data = (char *)server_key; secret.length = server_key_len; @@ -1155,7 +986,7 @@ pkinit_server_return_padata(krb5_context context, rep->u.dh_Info.kdfID, request->client, request->server, enctype, req_pkt, out_data, - encrypting_key); + &reply_key); if (retval) { pkiDebug("pkinit_alg_agility_kdf failed: %s\n", error_message(retval)); @@ -1165,13 +996,16 @@ pkinit_server_return_padata(krb5_context context, /* Otherwise, use the older octetstring2key() function */ } else { retval = pkinit_octetstring2key(context, enctype, server_key, - server_key_len, encrypting_key); + server_key_len, &reply_key); if (retval) { pkiDebug("pkinit_octetstring2key failed: %s\n", error_message(retval)); goto cleanup; } } + retval = cb->replace_reply_key(context, rock, &reply_key, FALSE); + if (retval) + goto cleanup; } *send_pa = malloc(sizeof(krb5_pa_data)); @@ -1183,20 +1017,11 @@ pkinit_server_return_padata(krb5_context context, goto cleanup; } (*send_pa)->magic = KV5M_PA_DATA; - switch ((int)padata->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; - break; - case KRB5_PADATA_PK_AS_REQ_OLD: - case KRB5_PADATA_PK_AS_REP_OLD: - (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD; - break; - } + (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; (*send_pa)->length = out_data->length; (*send_pa)->contents = (krb5_octet *) out_data->data; cleanup: - pkinit_fini_kdc_req_context(context, reqctx); free(scratch.data); free(out_data); if (encoded_dhkey_info != NULL) @@ -1205,23 +1030,10 @@ cleanup: krb5_free_data(context, encoded_key_pack); free(dh_pubkey); free(server_key); - - switch ((int)padata->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - free_krb5_pa_pk_as_req(&reqp); - free_krb5_pa_pk_as_rep(&rep); - free_krb5_reply_key_pack(&key_pack); - break; - case KRB5_PADATA_PK_AS_REP_OLD: - case KRB5_PADATA_PK_AS_REQ_OLD: - free_krb5_pa_pk_as_req_draft9(&reqp9); - free_krb5_pa_pk_as_rep_draft9(&rep9); - if (!fixed_keypack) - free_krb5_reply_key_pack_draft9(&key_pack9); - else - free_krb5_reply_key_pack(&key_pack); - break; - } + free_krb5_pa_pk_as_req(&reqp); + free_krb5_pa_pk_as_rep(&rep); + free_krb5_reply_key_pack(&key_pack); + krb5_free_keyblock_contents(context, &reply_key); if (retval) pkiDebug("pkinit_verify_padata failure"); @@ -1234,13 +1046,13 @@ pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype) { if (patype == KRB5_PADATA_PKINIT_KX) return PA_INFO; - return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA; + /* PKINIT does not normally set the hw-authent ticket flag, but a + * certauth module can cause it to do so. */ + return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA | PA_HARDWARE; } static krb5_preauthtype supported_server_pa_types[] = { KRB5_PADATA_PK_AS_REQ, - KRB5_PADATA_PK_AS_REQ_OLD, - KRB5_PADATA_PK_AS_REP_OLD, KRB5_PADATA_PKINIT_KX, 0 }; @@ -1323,6 +1135,10 @@ pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING, 0, &plgctx->opts->require_crl_checking); + pkinit_kdcdefault_boolean(context, plgctx->realmname, + KRB5_CONF_PKINIT_REQUIRE_FRESHNESS, + 0, &plgctx->opts->require_freshness); + pkinit_kdcdefault_string(context, plgctx->realmname, KRB5_CONF_PKINIT_EKU_CHECKING, &eku_string); @@ -1584,7 +1400,8 @@ certauth_dbmatch_initvt(krb5_context context, int maj_ver, int min_ver, } static krb5_error_code -load_certauth_plugins(krb5_context context, certauth_handle **handle_out) +load_certauth_plugins(krb5_context context, const char *const *realmnames, + certauth_handle **handle_out) { krb5_error_code ret; krb5_plugin_initvt_fn *modules = NULL, *mod; @@ -1624,20 +1441,21 @@ load_certauth_plugins(krb5_context context, certauth_handle **handle_out) if (h == NULL) goto cleanup; - ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt); + ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&h->vt); if (ret) { TRACE_CERTAUTH_VTINIT_FAIL(context, ret); free(h); continue; } h->moddata = NULL; - if (h->vt.init != NULL) { + if (h->vt.init_ex != NULL) + ret = h->vt.init_ex(context, realmnames, &h->moddata); + else if (h->vt.init != NULL) ret = h->vt.init(context, &h->moddata); - if (ret) { - TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret); - free(h); - continue; - } + if (ret) { + TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret); + free(h); + continue; } list[count++] = h; list[count] = NULL; @@ -1700,7 +1518,7 @@ pkinit_server_plugin_init(krb5_context context, goto errout; } - retval = load_certauth_plugins(context, &certauth_modules); + retval = load_certauth_plugins(context, realmnames, &certauth_modules); if (retval) goto errout; @@ -1766,7 +1584,6 @@ pkinit_init_kdc_req_context(krb5_context context, pkinit_kdc_req_context *ctx) if (retval) goto cleanup; reqctx->rcv_auth_pack = NULL; - reqctx->rcv_auth_pack9 = NULL; pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); *ctx = reqctx; @@ -1792,12 +1609,17 @@ pkinit_fini_kdc_req_context(krb5_context context, void *ctx) pkinit_fini_req_crypto(reqctx->cryptoctx); if (reqctx->rcv_auth_pack != NULL) free_krb5_auth_pack(&reqctx->rcv_auth_pack); - if (reqctx->rcv_auth_pack9 != NULL) - free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9); free(reqctx); } +static void +pkinit_free_modreq(krb5_context context, krb5_kdcpreauth_moddata moddata, + krb5_kdcpreauth_modreq modreq) +{ + pkinit_fini_kdc_req_context(context, modreq); +} + krb5_error_code kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, krb5_plugin_vtable vtable); @@ -1819,5 +1641,6 @@ kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, vt->edata = pkinit_server_get_edata; vt->verify = pkinit_server_verify_padata; vt->return_padata = pkinit_server_return_padata; + vt->free_modreq = pkinit_free_modreq; return 0; } diff --git a/src/plugins/preauth/pkinit/pkinit_trace.h b/src/plugins/preauth/pkinit/pkinit_trace.h index d4eb39d88b95..259e95c6c2c2 100644 --- a/src/plugins/preauth/pkinit/pkinit_trace.h +++ b/src/plugins/preauth/pkinit/pkinit_trace.h @@ -41,14 +41,14 @@ TRACE(c, "PKINIT client found no acceptable EKU in KDC cert") #define TRACE_PKINIT_CLIENT_EKU_SKIP(c) \ TRACE(c, "PKINIT client skipping EKU check due to configuration") +#define TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(c) \ + TRACE(c, "PKINIT client received freshness token from KDC") #define TRACE_PKINIT_CLIENT_KDF_ALG(c, kdf, keyblock) \ TRACE(c, "PKINIT client used KDF {hexdata} to compute reply key " \ "{keyblock}", kdf, keyblock) #define TRACE_PKINIT_CLIENT_KDF_OS2K(c, keyblock) \ TRACE(c, "PKINIT client used octetstring2key to compute reply key " \ "{keyblock}", keyblock) -#define TRACE_PKINIT_CLIENT_NO_DRAFT9(c) \ - TRACE(c, "PKINIT client ignoring draft 9 offer from RFC 4556 KDC") #define TRACE_PKINIT_CLIENT_NO_IDENTITY(c) \ TRACE(c, "PKINIT client has no configured identity; giving up") #define TRACE_PKINIT_CLIENT_REP_CHECKSUM_FAIL(c, expected, received) \ @@ -72,9 +72,9 @@ #define TRACE_PKINIT_CLIENT_REQ_RSA(c) \ TRACE(c, "PKINIT client making RSA request") #define TRACE_PKINIT_CLIENT_SAN_CONFIG_DNSNAME(c, host) \ - TRACE(c, "PKINIT client config accepts KDC dNSName SAN {string}", host) + TRACE(c, "PKINIT client config accepts KDC dNSName SAN {str}", host) #define TRACE_PKINIT_CLIENT_SAN_MATCH_DNSNAME(c, host) \ - TRACE(c, "PKINIT client matched KDC hostname {string} against " \ + TRACE(c, "PKINIT client matched KDC hostname {str} against " \ "dNSName SAN; EKU check still required", host) #define TRACE_PKINIT_CLIENT_SAN_MATCH_NONE(c) \ TRACE(c, "PKINIT client found no acceptable SAN in KDC cert") @@ -84,7 +84,7 @@ #define TRACE_PKINIT_CLIENT_SAN_ERR(c) \ TRACE(c, "PKINIT client failed to decode SANs in KDC cert") #define TRACE_PKINIT_CLIENT_SAN_KDCCERT_DNSNAME(c, host) \ - TRACE(c, "PKINIT client found dNSName SAN in KDC cert: {string}", host) + TRACE(c, "PKINIT client found dNSName SAN in KDC cert: {str}", host) #define TRACE_PKINIT_CLIENT_SAN_KDCCERT_PRINC(c, princ) \ TRACE(c, "PKINIT client found id-pkinit-san in KDC cert: {princ}", princ) #define TRACE_PKINIT_CLIENT_TRYAGAIN(c) \ @@ -93,6 +93,25 @@ #define TRACE_PKINIT_OPENSSL_ERROR(c, msg) \ TRACE(c, "PKINIT OpenSSL error: {str}", msg) +#define TRACE_PKINIT_PKCS11_GETFLIST_FAILED(c, errstr) \ + TRACE(c, "PKINIT PKCS11 C_GetFunctionList failed: {str}", errstr) +#define TRACE_PKINIT_PKCS11_GETSYM_FAILED(c, errstr) \ + TRACE(c, "PKINIT unable to find PKCS11 plugin symbol " \ + "C_GetFunctionList: {str}", errstr) +#define TRACE_PKINIT_PKCS11_LOGIN_FAILED(c, errstr) \ + TRACE(c, "PKINIT PKCS11 C_Login failed: {str}", errstr) +#define TRACE_PKINIT_PKCS11_NO_MATCH_TOKEN(c) \ + TRACE(c, "PKINIT PKCS#11 module has no matching tokens") +#define TRACE_PKINIT_PKCS11_NO_TOKEN(c) \ + TRACE(c, "PKINIT PKCS#11 module shows no slots with tokens") +#define TRACE_PKINIT_PKCS11_OPEN(c, name) \ + TRACE(c, "PKINIT opening PKCS#11 module \"{str}\"", name) +#define TRACE_PKINIT_PKCS11_OPEN_FAILED(c, errstr) \ + TRACE(c, "PKINIT PKCS#11 module open failed: {str}", errstr) +#define TRACE_PKINIT_PKCS11_SLOT(c, slot, len, label) \ + TRACE(c, "PKINIT PKCS#11 slotid {int} token {lenstr}", \ + slot, len, label) + #define TRACE_PKINIT_SERVER_CERT_AUTH(c, modname) \ TRACE(c, "PKINIT server authorizing cert with module {str}", \ modname) @@ -113,25 +132,38 @@ TRACE(c, "PKINIT server found no SAN in client cert") #define TRACE_PKINIT_SERVER_PADATA_VERIFY(c) \ TRACE(c, "PKINIT server verifying KRB5_PADATA_PK_AS_REQ") -#define TRACE_PKINIT_SERVER_PADATA_VERIFY_OLD(c) \ - TRACE(c, "PKINIT server verifying KRB5_PADATA_PK_AS_REQ_OLD") #define TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(c) \ TRACE(c, "PKINIT server failed to verify PA data") #define TRACE_PKINIT_SERVER_RETURN_PADATA(c) \ TRACE(c, "PKINIT server returning PA data") #define TRACE_PKINIT_SERVER_SAN_REJECT(c) \ TRACE(c, "PKINIT server found no acceptable SAN in client cert") +#define TRACE_PKINIT_SERVER_UPN_PARSE_FAIL(c, upn, ret) \ + TRACE(c, "PKINIT server could not parse UPN \"{str}\": {kerr}", \ + upn, ret) + +#define TRACE_PKINIT_CERT_CHAIN_NAME(c, index, name) \ + TRACE(c, "PKINIT chain cert #{int}: {str}", index, name) +#define TRACE_PKINIT_CERT_NUM_MATCHING(c, total, nummatch) \ + TRACE(c, "PKINIT client checked {int} certs, found {int} matches", \ + total, nummatch) +#define TRACE_PKINIT_CERT_RULE(c, rule) \ + TRACE(c, "PKINIT client matching rule '{str}' against certificates", rule) +#define TRACE_PKINIT_CERT_RULE_INVALID(c, rule) \ + TRACE(c, "PKINIT client ignoring invalid rule '{str}'", rule) #define TRACE_PKINIT_EKU(c) \ TRACE(c, "PKINIT found acceptable EKU and digitalSignature KU") #define TRACE_PKINIT_EKU_NO_KU(c) \ TRACE(c, "PKINIT found acceptable EKU but no digitalSignature KU") +#define TRACE_PKINIT_IDENTITY_OPTION(c, name) \ + TRACE(c, "PKINIT loading identity {str}", name) #define TRACE_PKINIT_LOADED_CERT(c, name) \ TRACE(c, "PKINIT loaded cert and key for {str}", name) -#define TRACE_PKINIT_LOAD_FROM_FILE(c) \ - TRACE(c, "PKINIT loading CA certs and CRLs from FILE") -#define TRACE_PKINIT_LOAD_FROM_DIR(c) \ - TRACE(c, "PKINIT loading CA certs and CRLs from DIR") +#define TRACE_PKINIT_LOAD_FROM_FILE(c, name) \ + TRACE(c, "PKINIT loading CA certs and CRLs from FILE {str}", name) +#define TRACE_PKINIT_LOAD_FROM_DIR(c, name) \ + TRACE(c, "PKINIT loading CA certs and CRLs from DIR {str}", name) #define TRACE_PKINIT_NO_CA_ANCHOR(c, file) \ TRACE(c, "PKINIT no anchor CA in file {str}", file) #define TRACE_PKINIT_NO_CA_INTERMEDIATE(c, file) \ @@ -161,6 +193,18 @@ TRACE(c, "PKINIT second PKCS12_parse with password failed") #define TRACE_PKINIT_PKCS_PROMPT_FAIL(c) \ TRACE(c, "PKINIT failed to prompt for PKCS12 password") +#define TRACE_PKINIT_REGEXP_MATCH(c, keyword, comp, value, idx) \ + TRACE(c, "PKINIT matched {str} rule '{str}' with " \ + "value '{str}' in cert #{int}", keyword, comp, value, (idx) + 1) +#define TRACE_PKINIT_REGEXP_NOMATCH(c, keyword, comp, value, idx) \ + TRACE(c, "PKINIT didn't match {str} rule '{str}' with " \ + "value '{str}' in cert #{int}", keyword, comp, value, (idx) + 1) +#define TRACE_PKINIT_SAN_CERT_COUNT(c, count, princ, upns, dns, cert) \ + TRACE(c, "PKINIT client found {int} SANs ({int} princs, {int} " \ + "UPNs, {int} DNS names) in certificate {str}", count, princ, \ + upns, dns, cert) +#define TRACE_PKINIT_SAN_CERT_NONE(c, cert) \ + TRACE(c, "PKINIT client found no SANs in certificate {str}", cert) #define TRACE_CERTAUTH_VTINIT_FAIL(c, ret) \ TRACE(c, "certauth module failed to init vtable: {kerr}", ret) |
