Skip to content

Commit

Permalink
Remove PKINIT RSA support
Browse files Browse the repository at this point in the history
RSA mode is no longer needed for interoperability.  Reduce the attack
surface of clients and KDCs by removing support for it.

ticket: 9108 (new)
  • Loading branch information
greghudson committed Dec 1, 2023
1 parent 48ccd81 commit 401f584
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 882 deletions.
4 changes: 0 additions & 4 deletions doc/user/user_commands/kinit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,6 @@ OPTIONS
**X509_anchors**\ =\ *value*
specify where to find trusted X509 anchor information

**flag_RSA_PROTOCOL**\ [**=yes**]
specify use of RSA, rather than the default Diffie-Hellman
protocol

**disable_freshness**\ [**=yes**]
disable sending freshness tokens (for testing purposes only)

Expand Down
2 changes: 0 additions & 2 deletions src/plugins/preauth/pkinit/pkinit.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ typedef struct _pkinit_plg_opts {
int require_eku; /* require EKU checking (default is true) */
int accept_secondary_eku;/* accept secondary EKU (default is false) */
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 */
Expand All @@ -162,7 +161,6 @@ typedef struct _pkinit_req_opts {
int require_eku;
int accept_secondary_eku;
int allow_upn;
int dh_or_rsa;
int require_crl_checking;
int dh_size; /* initial request DH modulus size (default=1024) */
int require_hostname_match;
Expand Down
235 changes: 68 additions & 167 deletions src/plugins/preauth/pkinit/pkinit_clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ pkinit_as_req_create(krb5_context context,
krb5_auth_pack auth_pack;
krb5_pa_pk_as_req *req = NULL;
krb5_algorithm_identifier **cmstypes = NULL;
int protocol = reqctx->opts->dh_or_rsa;

pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type);

Expand All @@ -214,29 +213,14 @@ pkinit_as_req_create(krb5_context context,
if (retval)
goto cleanup;

switch(protocol) {
case DH_PROTOCOL:
TRACE_PKINIT_CLIENT_REQ_DH(context);
pkiDebug("as_req: DH key transport algorithm\n");
TRACE_PKINIT_CLIENT_REQ_DH(context);

/* create client-side DH keys */
retval = client_create_dh(context, plgctx->cryptoctx,
reqctx->cryptoctx, reqctx->idctx,
reqctx->opts->dh_size, &spki);
auth_pack.clientPublicValue = spki;
if (retval != 0) {
pkiDebug("failed to create dh parameters\n");
goto cleanup;
}
break;
case RSA_PROTOCOL:
TRACE_PKINIT_CLIENT_REQ_RSA(context);
pkiDebug("as_req: RSA key transport algorithm\n");
break;
default:
pkiDebug("as_req: unknown key transport protocol %d\n",
protocol);
retval = -1;
/* create client-side DH keys */
retval = client_create_dh(context, plgctx->cryptoctx, reqctx->cryptoctx,
reqctx->idctx, reqctx->opts->dh_size, &spki);
auth_pack.clientPublicValue = spki;
if (retval != 0) {
pkiDebug("failed to create dh parameters\n");
goto cleanup;
}

Expand Down Expand Up @@ -553,49 +537,34 @@ pkinit_as_rep_parse(krb5_context context,
return retval;
}

switch(kdc_reply->choice) {
case choice_pa_pk_as_rep_dhInfo:
pkiDebug("as_rep: DH key transport algorithm\n");
if (kdc_reply->choice != choice_pa_pk_as_rep_dhInfo) {
pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}

#ifdef DEBUG_ASN1
print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata");
print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
kdc_reply->u.dh_Info.dhSignedData.length,
"/tmp/client_kdc_signeddata");
#endif
if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx,
reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER,
reqctx->opts->require_crl_checking,
(unsigned char *)
kdc_reply->u.dh_Info.dhSignedData.data,
kdc_reply->u.dh_Info.dhSignedData.length,
(unsigned char **)&dh_data.data,
&dh_data.length,
NULL, NULL, NULL)) != 0) {
pkiDebug("failed to verify pkcs7 signed data\n");
TRACE_PKINIT_CLIENT_REP_DH_FAIL(context);
goto cleanup;
}
TRACE_PKINIT_CLIENT_REP_DH(context);
break;
case choice_pa_pk_as_rep_encKeyPack:
pkiDebug("as_rep: RSA key transport algorithm\n");
if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx,
reqctx->cryptoctx, reqctx->idctx, pa_type,
reqctx->opts->require_crl_checking,
(unsigned char *)
kdc_reply->u.encKeyPack.data,
kdc_reply->u.encKeyPack.length,
(unsigned char **)&dh_data.data,
&dh_data.length)) != 0) {
pkiDebug("failed to verify pkcs7 enveloped data\n");
TRACE_PKINIT_CLIENT_REP_RSA_FAIL(context);
goto cleanup;
}
TRACE_PKINIT_CLIENT_REP_RSA(context);
break;
default:
pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
retval = -1;
retval = cms_signeddata_verify(context, plgctx->cryptoctx,
reqctx->cryptoctx, reqctx->idctx,
CMS_SIGN_SERVER,
reqctx->opts->require_crl_checking,
(unsigned char *)
kdc_reply->u.dh_Info.dhSignedData.data,
kdc_reply->u.dh_Info.dhSignedData.length,
(unsigned char **)&dh_data.data,
&dh_data.length,
NULL, NULL, NULL);
if (retval) {
pkiDebug("failed to verify pkcs7 signed data\n");
TRACE_PKINIT_CLIENT_REP_DH_FAIL(context);
goto cleanup;
}
TRACE_PKINIT_CLIENT_REP_DH(context);

retval = krb5_build_principal_ext(context, &kdc_princ,
request->server->realm.length,
request->server->realm.data,
Expand Down Expand Up @@ -632,116 +601,54 @@ pkinit_as_rep_parse(krb5_context context,

OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);

switch(kdc_reply->choice) {
case choice_pa_pk_as_rep_dhInfo:
#ifdef DEBUG_ASN1
print_buffer_bin(dh_data.data, dh_data.length,
"/tmp/client_dh_key");
print_buffer_bin(dh_data.data, dh_data.length, "/tmp/client_dh_key");
#endif
if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data,
&kdc_dh)) != 0) {
pkiDebug("failed to decode kdc_dh_key_info\n");
goto cleanup;
}

/* client after KDC reply */
if ((retval = client_process_dh(context, plgctx->cryptoctx,
reqctx->cryptoctx, reqctx->idctx,
(unsigned char *)
kdc_dh->subjectPublicKey.data,
kdc_dh->subjectPublicKey.length,
&client_key, &client_key_len)) != 0) {
pkiDebug("failed to process dh params\n");
goto cleanup;
}

/* If we have a KDF algorithm ID, call the algorithm agility KDF... */
if (kdc_reply->u.dh_Info.kdfID) {
secret.length = client_key_len;
secret.data = (char *)client_key;

retval = pkinit_alg_agility_kdf(context, &secret,
kdc_reply->u.dh_Info.kdfID,
request->client, request->server,
etype, encoded_request,
(krb5_data *)as_rep, key_block);

if (retval) {
pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n",
error_message(retval));
goto cleanup;
}
TRACE_PKINIT_CLIENT_KDF_ALG(context, kdc_reply->u.dh_Info.kdfID,
key_block);
retval = k5int_decode_krb5_kdc_dh_key_info(&k5data, &kdc_dh);
if (retval) {
pkiDebug("failed to decode kdc_dh_key_info\n");
goto cleanup;
}

/* ...otherwise, use the older octetstring2key function. */
} else {
/* client after KDC reply */
retval = client_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx,
reqctx->idctx,
(unsigned char *)kdc_dh->subjectPublicKey.data,
kdc_dh->subjectPublicKey.length, &client_key,
&client_key_len);
if (retval) {
pkiDebug("failed to process dh params\n");
goto cleanup;
}

retval = pkinit_octetstring2key(context, etype, client_key,
client_key_len, key_block);
if (retval) {
pkiDebug("failed to create key pkinit_octetstring2key %s\n",
error_message(retval));
goto cleanup;
}
TRACE_PKINIT_CLIENT_KDF_OS2K(context, key_block);
}
/* If we have a KDF algorithm ID, call the algorithm agility KDF. */
if (kdc_reply->u.dh_Info.kdfID) {
secret.length = client_key_len;
secret.data = (char *)client_key;

break;
case choice_pa_pk_as_rep_encKeyPack:
#ifdef DEBUG_ASN1
print_buffer_bin(dh_data.data, dh_data.length,
"/tmp/client_key_pack");
#endif
retval = k5int_decode_krb5_reply_key_pack(&k5data, &key_pack);
retval = pkinit_alg_agility_kdf(context, &secret,
kdc_reply->u.dh_Info.kdfID,
request->client, request->server,
etype, encoded_request,
(krb5_data *)as_rep, key_block);
if (retval) {
pkiDebug("failed to decode reply_key_pack\n");
pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n",
error_message(retval));
goto cleanup;
}
retval = krb5_c_make_checksum(context,
key_pack->asChecksum.checksum_type,
&key_pack->replyKey,
KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
encoded_request, &cksum);
TRACE_PKINIT_CLIENT_KDF_ALG(context, kdc_reply->u.dh_Info.kdfID,
key_block);

} else {
/* Otherwise, use the older octetstring2key function. */
retval = pkinit_octetstring2key(context, etype, client_key,
client_key_len, key_block);
if (retval) {
pkiDebug("failed to make a checksum\n");
pkiDebug("failed to create key pkinit_octetstring2key %s\n",
error_message(retval));
goto cleanup;
}

if ((cksum.length != key_pack->asChecksum.length) ||
k5_bcmp(cksum.contents, key_pack->asChecksum.contents,
cksum.length) != 0) {
TRACE_PKINIT_CLIENT_REP_CHECKSUM_FAIL(context, &cksum,
&key_pack->asChecksum);
pkiDebug("failed to match the checksums\n");
#ifdef DEBUG_CKSUM
pkiDebug("calculating checksum on buf size (%d)\n",
encoded_request->length);
print_buffer(encoded_request->data, encoded_request->length);
pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length);
print_buffer(key_pack->replyKey.contents,
key_pack->replyKey.length);
pkiDebug("received checksum type=%d size=%d ",
key_pack->asChecksum.checksum_type,
key_pack->asChecksum.length);
print_buffer(key_pack->asChecksum.contents,
key_pack->asChecksum.length);
pkiDebug("expected checksum type=%d size=%d ",
cksum.checksum_type, cksum.length);
print_buffer(cksum.contents, cksum.length);
#endif
goto cleanup;
} else
pkiDebug("checksums match\n");

krb5_copy_keyblock_contents(context, &key_pack->replyKey,
key_block);
TRACE_PKINIT_CLIENT_REP_RSA_KEY(context, key_block, &cksum);

break;
default:
pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
goto cleanup;
TRACE_PKINIT_CLIENT_KDF_OS2K(context, key_block);
}

retval = 0;
Expand Down Expand Up @@ -1279,7 +1186,6 @@ pkinit_client_req_init(krb5_context context,

reqctx->opts->require_eku = plgctx->opts->require_eku;
reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku;
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;
Expand Down Expand Up @@ -1450,11 +1356,6 @@ handle_gic_opt(krb5_context context,
retval = add_string_to_array(context, &plgctx->idopts->anchors, value);
if (retval)
return retval;
} else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) {
if (strcmp(value, "yes") == 0) {
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;
Expand Down
39 changes: 0 additions & 39 deletions src/plugins/preauth/pkinit/pkinit_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,45 +181,6 @@ krb5_error_code cms_signeddata_verify
int *is_signed); /* OUT
receives whether message is signed */

/*
* this function creates a CMS message where eContentType is EnvelopedData
*/
krb5_error_code cms_envelopeddata_create
(krb5_context context, /* IN */
pkinit_plg_crypto_context plg_cryptoctx, /* IN */
pkinit_req_crypto_context req_cryptoctx, /* IN */
pkinit_identity_crypto_context id_cryptoctx, /* IN */
krb5_preauthtype pa_type, /* IN */
unsigned char *key_pack, /* IN
contains DER encoded ReplyKeyPack */
unsigned int key_pack_len, /* IN
contains length of key_pack */
unsigned char **envel_data, /* OUT
receives DER encoded encKeyPack */
unsigned int *envel_data_len); /* OUT
receives length of envel_data */

/*
* this function creates a CMS message where eContentType is EnvelopedData
*/
krb5_error_code cms_envelopeddata_verify
(krb5_context context, /* IN */
pkinit_plg_crypto_context plg_cryptoctx, /* IN */
pkinit_req_crypto_context req_cryptoctx, /* IN */
pkinit_identity_crypto_context id_cryptoctx, /* IN */
krb5_preauthtype pa_type, /* IN */
int require_crl_checking, /* IN
specifies whether CRL checking should be
strictly enforced */
unsigned char *envel_data, /* IN
contains DER encoded encKeyPack */
unsigned int envel_data_len, /* IN
contains length of envel_data */
unsigned char **signed_data, /* OUT
receives ReplyKeyPack */
unsigned int *signed_data_len); /* OUT
receives length of signed_data */

/*
* This function retrieves the signer's identity, in a form that could
* be passed back in to a future invocation of this module as a candidate
Expand Down

0 comments on commit 401f584

Please sign in to comment.