aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Coffman <kwc@citi.umich.edu>2007-05-17 18:15:14 +0000
committerKevin Coffman <kwc@citi.umich.edu>2007-05-17 18:15:14 +0000
commitff0091077ef03bf8010f61f4e1ee6190832448fb (patch)
tree2b1e407b9c29a09bab8c4f90ee208bc485236763
parent77bcc0f5058a7f7b14218490e86a3101e3fbc40d (diff)
downloadkrb5-ff0091077ef03bf8010f61f4e1ee6190832448fb.zip
krb5-ff0091077ef03bf8010f61f4e1ee6190832448fb.tar.gz
krb5-ff0091077ef03bf8010f61f4e1ee6190832448fb.tar.bz2
- Add new config file option pkinit_longhorn which indicates
we are talking to a Longhorn KDC and allows the necessary hacks to work with a Longhorn (beta 3) server - Add pkcs12 support (required for gssmonger testing) - Modify SAN and EKU processing so that it does not use pa_type and so that it allows checking with certificates containing more than one SAN - add new configuration file option, "pkinit_eku_checking" for the kdc, this can be specified as [kpClientAuth | scLogin | none] for the client, this can be specified as [kpKDC | kpServerAuth | none] - Restructure code to make DN and SAN matching possible. Still need the actual matching code. - Fix from Ken Renard re: using ENV: for identity processing - Create new header, pkinit_crypto.h separating out the crypto interface definition. git-svn-id: svn://anonsvn.mit.edu/krb5/users/coffman/pkinit@19549 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--src/plugins/preauth/pkinit/pkinit.h448
-rw-r--r--src/plugins/preauth/pkinit/pkinit_clnt.c328
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto.h453
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c1149
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.h57
-rw-r--r--src/plugins/preauth/pkinit/pkinit_lib.c26
-rw-r--r--src/plugins/preauth/pkinit/pkinit_profile.c46
-rw-r--r--src/plugins/preauth/pkinit/pkinit_srv.c216
8 files changed, 1654 insertions, 1069 deletions
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index f3af97c..f8e52bb 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -53,6 +53,8 @@
#define pkiDebug(args...)
#endif
+extern int longhorn; /* XXX Talking to a Longhorn server? */
+
/* Macros to deal with converting between various data types... */
#define PADATA_TO_KRB5DATA(pad, k5d) \
(k5d)->length = (pad)->length; (k5d)->data = (char *)(pad)->contents;
@@ -102,13 +104,12 @@ typedef struct _pkinit_identity_crypto_context *pkinit_identity_crypto_context;
*/
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 require_san; /* require SAN checking (default is true) */
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 princ_in_cert;
int dh_min_bits; /* minimum DH modulus size allowed */
- int allow_proxy_certs;
} pkinit_plg_opts;
/*
@@ -116,6 +117,7 @@ typedef struct _pkinit_plg_opts {
*/
typedef struct _pkinit_req_opts {
int require_eku;
+ int accept_secondary_eku;
int require_san;
int allow_upn;
int dh_or_rsa;
@@ -199,16 +201,8 @@ struct _pkinit_kdc_req_context {
typedef struct _pkinit_kdc_req_context *pkinit_kdc_req_context;
/*
- * Functions to initialize and cleanup various context
- */
-krb5_error_code pkinit_init_plg_crypto(pkinit_plg_crypto_context *);
-void pkinit_fini_plg_crypto(pkinit_plg_crypto_context);
-
-krb5_error_code pkinit_init_req_crypto(pkinit_req_crypto_context *);
-void pkinit_fini_req_crypto(pkinit_req_crypto_context);
-
-krb5_error_code pkinit_init_identity_crypto(pkinit_identity_crypto_context *);
-void pkinit_fini_identity_crypto(pkinit_identity_crypto_context);
+ * Functions in pkinit_lib.c
+ */
krb5_error_code pkinit_init_req_opts(pkinit_req_opts **);
void pkinit_fini_req_opts(pkinit_req_opts *);
@@ -221,418 +215,10 @@ void pkinit_fini_identity_opts(pkinit_identity_opts *idopts);
krb5_error_code pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
pkinit_identity_opts **dest_opts);
-
-/*
- * these describe the type of CMS message
- */
-enum cms_msg_types {
- CMS_SIGN_CLIENT,
- CMS_SIGN_DRAFT9,
- CMS_SIGN_SERVER,
- CMS_ENVEL_SERVER
-};
-
-/*
- * this function creates a CMS message where eContentType is SignedData
- */
-krb5_error_code cms_signeddata_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 */
- 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) */
- unsigned int auth_pack_len, /* IN
- contains length of auth_pack */
- unsigned char **signed_data, /* OUT
- for CMS_SIGN_CLIENT receives DER encoded
- SignedAuthPack (CMS_SIGN_CLIENT) or DER
- encoded DHInfo (CMS_SIGN_SERVER) */
- unsigned int *signed_data_len); /* OUT
- receives length of signed_data */
-
-/*
- * this function verifies a CMS message where eContentType is SignedData
- */
-krb5_error_code cms_signeddata_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 */
- int cms_msg_type, /* IN
- specifies CMS_SIGN_CLIENT for client-side
- CMS message and CMS_SIGN_SERVER for kdc-side */
- int require_crl_checking, /* IN
- specifies whether CRL checking should be
- strictly enforced, i.e. if no CRLs available
- for the CA then fail verification.
- note, if the value is 0, crls are still
- checked if present */
- unsigned char *signed_data, /* IN
- contains DER encoded SignedAuthPack (CMS_SIGN_CLIENT)
- or DER encoded DHInfo (CMS_SIGN_SERVER) */
- unsigned int signed_data_len, /* IN
- contains length of signed_data*/
- unsigned char **auth_pack, /* OUT
- receives DER encoded AuthPack (CMS_SIGN_CLIENT)
- or DER encoded DHRepInfo (CMS_SIGN_SERVER)*/
- unsigned int *auth_pack_len, /* OUT
- receives length of auth_pack */
- unsigned char **authz_data, /* OUT
- receives required authorization data that
- contains the verified certificate chain
- (only used by the KDC) */
- unsigned int *authz_data_len); /* OUT
- receives length of authz_data */
-
-/*
- * 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 */
- 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
- 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 looks for a SAN in the received certificate.
- * if it finds one, it retrieves and returns Kerberos principal
- * name encoded in the SAN
- */
-krb5_error_code verify_id_pkinit_san
- (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 allow_upn, /* IN
- specifies if Windows SANs are allowed */
- krb5_principal *identity_in_san, /* OUT
- receives Kerberos principal found in SAN */
- unsigned char **kdc_hostname, /* OUT
- contains dNSName SAN (win2k KDC hostname) */
- int *san_valid); /* OUT
- receives non-zero if a valid SAN was found */
-
-/*
- * this functions looks for an EKU in the received certificate.
- * if config opts specifies that EKU check should be ignored, then
- * the lack EKU in the received certificate is not treated as an error
- */
-krb5_error_code verify_id_pkinit_eku
- (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_eku, /* IN
- specifies if policy requires EKU checking */
- int *eku_valid); /* OUT
- receives non-zero if a valid EKU was found */
-
-/*
- * this functions takes in generated DH secret key and converts
- * it in to a kerberos session key. it takes into the account the
- * enc type and then follows the procedure specified in the RFC p 22.
- */
-krb5_error_code pkinit_octetstring2key
- (krb5_context context, /* IN */
- krb5_enctype etype, /* IN
- specifies the enc type */
- unsigned char *key, /* IN
- contains the DH secret key */
- unsigned int key_len, /* IN
- contains length of key */
- krb5_keyblock * krb5key); /* OUT
- receives kerberos session key */
-
-/*
- * this function implements clients first part of the DH protocol.
- * client selects its DH parameters and pub key
- */
-krb5_error_code client_create_dh
- (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 */
- 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 */
-
-/*
- * this function completes client's the DH protocol. client
- * processes received DH pub key from the KDC and computes
- * the DH secret key
- */
-krb5_error_code client_process_dh
- (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 */
- unsigned char *dh_pubkey, /* IN
- 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
- receives DH secret key */
- unsigned int *dh_session_key_len); /* OUT
- receives length of dh_session_key */
-
-/*
- * this function implements the KDC first part of the DH protocol.
- * it decodes the client's DH parameters and pub key and checks
- * if they are acceptable.
- */
-krb5_error_code server_check_dh
- (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_octet_data *dh_params, /* IN
- ???? */
- int minbits); /* IN
- the mininum number of key bits acceptable */
-
-/*
- * this function completes the KDC's DH protocol. The KDC generates
- * its DH pub key and computes the DH secret key
- */
-krb5_error_code server_process_dh
- (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 */
- 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
- receives KDC's DER encoded DH pub key */
- unsigned int *dh_pubkey_len, /* OUT
- receives length of dh_pubkey */
- unsigned char **server_key, /* OUT
- receives DH secret key */
- unsigned int *server_key_len); /* OUT
- receives length of server_key */
-
-/*
- * this functions takes in crypto specific representation of
- * trustedCertifiers and creates a list of
- * krb5_external_principal_identifier
- */
-krb5_error_code create_krb5_trustedCertifiers
- (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_external_principal_identifier ***trustedCertifiers); /* OUT */
-
-/*
- * this functions takes in crypto specific representation of
- * trustedCas (draft9) and creates a list of krb5_trusted_ca (draft9).
- * draft9 trustedCAs is a CHOICE. we only support choices for
- * [1] caName and [2] issuerAndSerial. there is no config
- * option available to select the choice yet. default = 1.
- */
-krb5_error_code create_krb5_trustedCas
- (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 */
- int flag, /* IN
- specifies the tag of the CHOICE */
- krb5_trusted_ca ***trustedCas); /* OUT */
-
-/*
- * this functions takes in crypto specific representation of the
- * KDC's certificate and creates a DER encoded kdcPKId
- */
-krb5_error_code create_issuerAndSerial
- (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 */
- unsigned char **kdcId_buf, /* OUT
- receives DER encoded kdcPKId */
- unsigned int *kdcId_len); /* OUT
- receives length of encoded kdcPKId */
-
-/*
- * process identity options specified via the command-line
- * or config file and populate the crypto-specific identity
- * information.
- */
krb5_error_code pkinit_initialize_identity
(krb5_context context, /* IN */
- pkinit_identity_opts *idopts, /* IN */
- pkinit_identity_crypto_context id_cryptoctx); /* IN/OUT */
-
-krb5_error_code pkinit_process_identity_option
- (krb5_context context, /* IN */
- int attr, /* IN */
- const char *value, /* IN */
- pkinit_identity_crypto_context id_cryptoctx); /* IN/OUT */
-
-krb5_error_code pkinit_get_client_cert
- (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- const char *principal, krb5_get_init_creds_opt *opt);
-
-krb5_error_code pkinit_get_kdc_cert
- (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- const char *principal, krb5_get_init_creds_opt *opt);
-
-krb5_error_code pkinit_get_trusted_cacerts
- (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_get_init_creds_opt *opt);
-
-krb5_error_code pkinit_get_intermediate_cacerts
- (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_get_init_creds_opt *opt);
-
-krb5_error_code pkinit_get_crls
- (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_get_init_creds_opt *opt);
-
-/*
- * this function creates edata that contains TD-DH-PARAMETERS
- */
-krb5_error_code pkinit_create_td_dh_parameters
- (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 */
- pkinit_plg_opts *opts, /* IN */
- krb5_data **edata); /* OUT */
-
-/*
- * this function processes edata that contains TD-DH-PARAMETERS.
- * the client processes the received acceptable by KDC DH
- * parameters and picks the first acceptable to it. it matches
- * them against the known DH parameters.
- */
-krb5_error_code pkinit_process_td_dh_params
- (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_algorithm_identifier **algId, /* IN */
- int *new_dh_size); /* OUT
- receives the new DH modulus to use in the new AS-REQ */
-
-/*
- * this function creates edata that contains TD-INVALID-CERTIFICATES
- */
-krb5_error_code pkinit_create_td_invalid_certificate
- (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_data **edata); /* OUT */
-
-/*
- * this function creates edata that contains TD-TRUSTED-CERTIFIERS
- */
-krb5_error_code pkinit_create_td_trusted_certifiers
- (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_data **edata); /* OUT */
-
-/*
- * this function processes edata that contains either
- * TD-TRUSTED-CERTIFICATES or TD-INVALID-CERTIFICATES.
- * current implementation only decodes the received message
- * but does not act on it
- */
-krb5_error_code pkinit_process_td_trusted_certifiers
- (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_external_principal_identifier **trustedCertifiers, /* IN */
- int td_type); /* IN */
-
-/*
- * this function checks if the received kdcPKId matches
- * the KDC's certificate
- */
-krb5_error_code pkinit_check_kdc_pkid
- (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 */
- unsigned char *pdid_buf, /* IN
- contains DER encoded kdcPKId */
- unsigned int pkid_len, /* IN
- contains length of pdid_buf */
- int *valid_kdcPkId); /* OUT
- 1 if kdcPKId matches, otherwise 0 */
-
-krb5_error_code pkinit_get_kdc_identity_crypto
- (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx);
-
-krb5_error_code pkinit_identity_set_prompter
- (pkinit_identity_crypto_context id_cryptoctx,
- krb5_prompter_fct prompter, void *prompter_data);
+ pkinit_identity_opts *idopts, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx); /* IN/OUT */
/*
* initialization and free functions
@@ -685,15 +271,15 @@ krb5_error_code pkinit_kdcdefault_boolean
int default_value, int *ret_value);
krb5_error_code pkinit_kdcdefault_integer
(krb5_context context, const char *realmname, const char *option,
- int default_value, int *ret_value);
+ int default_value, int *ret_value);
krb5_error_code pkinit_libdefault_strings
(krb5_context context, const krb5_data *realm,
- const char *option, char ***ret_value);
+ const char *option, char ***ret_value);
krb5_error_code pkinit_libdefault_string
(krb5_context context, const krb5_data *realm,
- const char *option, char **ret_value);
+ const char *option, char **ret_value);
krb5_error_code pkinit_libdefault_boolean
(krb5_context context, const krb5_data *realm, const char *option,
int default_value, int *ret_value);
@@ -701,15 +287,15 @@ krb5_error_code pkinit_libdefault_integer
(krb5_context context, const krb5_data *realm, const char *option,
int default_value, int *ret_value);
-krb5_error_code pkinit_get_kdc_hostnames
- (krb5_context context, krb5_data *realm, char ***hostnames);
-
/*
- * main api end
+ * debugging functions
*/
-
-/* debugging functions */
void print_buffer(unsigned char *, unsigned int);
void print_buffer_bin(unsigned char *, unsigned int, char *);
+/*
+ * Now get crypto function declarations
+ */
+#include "pkinit_crypto.h"
+
#endif /* _PKINIT_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 2921fa1..5ce5aa9 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -43,6 +43,8 @@
/* #define DEBUG */
/* #define DEBUG_DH */
+int longhorn = 0; /* XXX Talking to a Longhorn server? */
+
krb5_error_code pkinit_client_process
(krb5_context context, void *plugin_context, void *request_context,
krb5_get_init_creds_opt *opt,
@@ -134,7 +136,8 @@ pa_pkinit_gen_req(krb5_context context,
cksum.contents = NULL;
reqctx->pa_type = in_padata->pa_type;
- pkiDebug("option included = %d till=%d\n", request->kdc_options, request->till);
+ pkiDebug("kdc_options = 0x%x till = %d\n",
+ request->kdc_options, request->till);
/* If we don't have a client, we're done */
if (request->client == NULL) {
pkiDebug("No request->client; aborting PKINIT\n");
@@ -215,8 +218,8 @@ pa_pkinit_gen_req(krb5_context context,
return_pa_data[0]->length = out_data->length;
return_pa_data[0]->contents = (krb5_octet *) out_data->data;
- if (return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
- && reqctx->opts->win2k_require_cksum) {
+ if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
+ && reqctx->opts->win2k_require_cksum) || (longhorn == 1)) {
return_pa_data[1]->pa_type = 132;
return_pa_data[1]->length = 0;
return_pa_data[1]->contents = NULL;
@@ -378,12 +381,10 @@ pkinit_as_req_create(krb5_context context,
reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1,
(unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
&req->signedAuthPack.data, &req->signedAuthPack.length);
-#if 0 /* VISTA HACK */
- if (retval == 0) {
- pkiDebug("Hacking SignedAuthPack for VISTA!\n");
- req->signedAuthPack.data +=4;
- req->signedAuthPack.length -= 4;
- }
+#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:
@@ -397,6 +398,11 @@ pkinit_as_req_create(krb5_context context,
(unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
&req9->signedAuthPack.data, &req9->signedAuthPack.length);
break;
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)req9->signedAuthPack.data,
+ req9->signedAuthPack.length,
+ "/tmp/client_signed_data_draft9");
+#endif
}
krb5_free_data(context, coded_auth_pack);
if (retval) {
@@ -411,7 +417,6 @@ pkinit_as_req_create(krb5_context context,
reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers);
if (retval)
goto cleanup;
-
retval = create_issuerAndSerial(context, plgctx->cryptoctx,
reqctx->cryptoctx, reqctx->idctx, &req->kdcPkId.data,
&req->kdcPkId.length);
@@ -420,13 +425,6 @@ pkinit_as_req_create(krb5_context context,
/* Encode the as-req */
retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
-#if 0 /* VISTA HACK */
- if (retval == 0) {
- pkiDebug("Hacking SignedAuthPack for VISTA!\n");
- req->signedAuthPack.data -=4;
- req->signedAuthPack.length += 4;
- }
-#endif
break;
case KRB5_PADATA_PK_AS_REQ_OLD:
#if 0
@@ -512,6 +510,152 @@ cleanup:
return retval;
}
+static krb5_error_code
+verify_kdc_san(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_principal kdcprinc,
+ int *valid_san,
+ int *need_eku_checking)
+{
+ krb5_error_code retval;
+ char **certhosts = NULL, **cfghosts = NULL;
+ krb5_principal *princs = NULL;
+ unsigned char ***get_dns;
+ int i, j;
+
+ *valid_san = 0;
+ *need_eku_checking = 1;
+
+ retval = pkinit_libdefault_strings(context,
+ krb5_princ_realm(context, kdcprinc),
+ "pkinit_kdc_hostname",
+ &cfghosts);
+ if (retval || cfghosts == NULL) {
+ pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n",
+ __FUNCTION__);
+ get_dns = NULL;
+ } else {
+ pkiDebug("%s: pkinit_kdc_hostname values found in config file\n",
+ __FUNCTION__);
+ get_dns = (unsigned char ***)&certhosts;
+ }
+
+ retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ &princs, NULL, get_dns);
+ if (retval) {
+ pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto out;
+ }
+#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++) {
+ if (krb5_principal_compare(context, princs[i], kdcprinc)) {
+ pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
+ *valid_san = 1;
+ *need_eku_checking = 0;
+ retval = 0;
+ goto out;
+ }
+ }
+ pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
+
+ if (certhosts == NULL) {
+ pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto out;
+ }
+
+ for (i = 0; certhosts[i] != NULL; i++) {
+ for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) {
+ pkiDebug("%s: comparing cert name '%s' with config name '%s'\n",
+ __FUNCTION__, certhosts[i], cfghosts[j]);
+ if (strcmp(certhosts[i], cfghosts[j]) == 0) {
+ pkiDebug("%s: we have a dnsName match\n", __FUNCTION__);
+ *valid_san = 1;
+ retval = 0;
+ goto out;
+ }
+ }
+ }
+ pkiDebug("%s: no dnsName san match found\n", __FUNCTION__);
+
+ /* We found no match */
+ retval = 0;
+
+out:
+ if (princs != NULL) {
+ for (i = 0; princs[i] != NULL; i++)
+ krb5_free_principal(context, princs[i]);
+ free(princs);
+ }
+ if (certhosts != NULL) {
+ for (i = 0; certhosts[i] != NULL; i++)
+ free(certhosts[i]);
+ free(certhosts);
+ }
+ if (cfghosts != NULL)
+ profile_free_list(cfghosts);
+
+ pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n",
+ __FUNCTION__, retval, *valid_san, *need_eku_checking);
+ return retval;
+}
+
+static krb5_error_code
+verify_kdc_eku(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ int *eku_accepted)
+{
+ krb5_error_code retval;
+
+ *eku_accepted = 0;
+
+ if (reqctx->opts->require_eku == 0) {
+ pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
+ *eku_accepted = 1;
+ retval = 0;
+ goto out;
+ }
+ retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ 1, /* kdc cert */
+ reqctx->opts->accept_secondary_eku,
+ eku_accepted);
+ if (retval) {
+ pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto out;
+ }
+
+out:
+ pkiDebug("%s: returning retval %d, eku_accepted %d\n",
+ __FUNCTION__, retval, *eku_accepted);
+ return retval;
+}
+
/*
* Parse PA-PK-AS-REP message. Optionally evaluates the message's certificate chain.
* Optionally returns various components.
@@ -536,9 +680,10 @@ pkinit_as_rep_parse(krb5_context context,
unsigned char *client_key = NULL, *kdc_hostname = NULL;
unsigned int client_key_len = 0;
krb5_checksum cksum = {0, 0, 0, NULL};
- krb5_principal tmp_server;
- int valid_eku = 0, valid_san = 0, i = 0;
krb5_data k5data;
+ int valid_san = 0;
+ int valid_eku = 0;
+ int need_eku_checking = 1;
assert((as_rep != NULL) && (key_block != NULL));
@@ -588,79 +733,30 @@ pkinit_as_rep_parse(krb5_context context,
goto cleanup;
}
- if (reqctx->opts->require_san) {
- retval = verify_id_pkinit_san(context, plgctx->cryptoctx,
- reqctx->cryptoctx, reqctx->idctx, pa_type,
- plgctx->opts->allow_upn, &tmp_server, &kdc_hostname, &valid_san);
+ retval = verify_kdc_san(context, plgctx, reqctx, request->server,
+ &valid_san, &need_eku_checking);
+ if (retval)
+ goto cleanup;
+ if (!valid_san) {
+ pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto cleanup;
+ }
+
+ if (need_eku_checking) {
+ retval = verify_kdc_eku(context, plgctx, reqctx,
+ &valid_eku);
if (retval)
goto cleanup;
- if (!valid_san) {
- pkiDebug("failed to verify id-pkinit-san\n");
- retval = KRB5KDC_ERR_KDC_NOT_TRUSTED;
+ if (!valid_eku) {
+ pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
goto cleanup;
- } else {
- if (pa_type == KRB5_PADATA_PK_AS_REP &&
- tmp_server != NULL) {
- retval = krb5_principal_compare(context, request->server,
- tmp_server);
- krb5_free_principal(context, tmp_server);
- if (!retval) {
- pkiDebug("identity in the certificate does not match "
- "the requested principal\n");
- retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
- goto cleanup;
- }
- } else if (pa_type == KRB5_PADATA_PK_AS_REP_OLD) {
- char **hostnames = NULL;
- int name_matched = 0;
-
- if (reqctx->opts->require_hostname_match) {
- retval = pkinit_libdefault_strings(context,
- &request->server->realm,
- "pkinit_win2k_hostname",
- &hostnames);
- if (retval) {
- pkiDebug("No pkinit_win2k_hostname values found in "
- "the config file.\n");
- retval = KRB5KDC_ERR_KDC_NOT_TRUSTED;
- goto cleanup;
- }
- for (i = 0; hostnames != NULL && hostnames[i] != NULL; i++) {
- pkiDebug("Comparing cert's dnsName '%s' with "
- "trusted win2k hostname '%s'\n",
- kdc_hostname, hostnames[i]);
- if (strcmp((char *)kdc_hostname, hostnames[i]) == 0) {
- pkiDebug("Cert's dnsName, '%s', matched\n",
- kdc_hostname);
- name_matched = 1;
- break;
- }
- }
- profile_free_list(hostnames);
- if (!name_matched) {
- pkiDebug("Cert's dnsName, '%s', NOT matched\n",
- kdc_hostname);
- retval = KRB5KDC_ERR_KDC_NOT_TRUSTED;
- goto cleanup;
- }
- } else {
- pkiDebug("config options says skip hostname check\n");
- }
- }
}
- } else {
- pkiDebug("config option says not to check for SAN\n");
- }
-
- retval = verify_id_pkinit_eku(context, plgctx->cryptoctx, reqctx->cryptoctx,
- reqctx->idctx, pa_type, reqctx->opts->require_eku, &valid_eku);
- if (retval)
- goto cleanup;
- if (!valid_eku) {
- pkiDebug("failed to verify id-pkinit-KPKdc\n");
- retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
- goto cleanup;
- }
+ } else
+ pkiDebug("%s: skipping EKU check\n", __FUNCTION__);
OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);
@@ -703,7 +799,7 @@ pkinit_as_rep_parse(krb5_context context,
if ((retval = k5int_decode_krb5_reply_key_pack(&k5data,
&key_pack)) != 0) {
pkiDebug("failed to decode reply_key_pack\n");
- if (pa_type == KRB5_PADATA_PK_AS_REP)
+ if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0)
goto cleanup;
else {
if ((retval =
@@ -729,9 +825,10 @@ pkinit_as_rep_parse(krb5_context context,
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, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
- encoded_request, &cksum);
+ key_pack->asChecksum.checksum_type,
+ &key_pack->replyKey,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ encoded_request, &cksum);
if (retval) {
pkiDebug("failed to make a checksum\n");
goto cleanup;
@@ -802,6 +899,8 @@ pkinit_client_profile(krb5_context context,
pkinit_req_context reqctx,
krb5_kdc_req *request)
{
+ char *eku_string = NULL;
+
pkiDebug("pkinit_client_profile %p %p %p %p\n",
context, plgctx, reqctx, request);
@@ -813,18 +912,39 @@ pkinit_client_profile(krb5_context context,
"pkinit_win2k_require_binding",
reqctx->opts->win2k_require_cksum,
&reqctx->opts->win2k_require_cksum);
+ /* Temporarily just set global flag from config file */
pkinit_libdefault_boolean(context, &request->server->realm,
- "pkinit_require_eku",
- reqctx->opts->require_eku,
- &reqctx->opts->require_eku);
+ "pkinit_longhorn",
+ 0,
+ &longhorn);
pkinit_libdefault_boolean(context, &request->server->realm,
"pkinit_require_krbtgt_otherName",
reqctx->opts->require_san,
&reqctx->opts->require_san);
pkinit_libdefault_boolean(context, &request->server->realm,
- "pkinit_require_hostname_match",
- reqctx->opts->require_hostname_match,
- &reqctx->opts->require_hostname_match);
+ "pkinit_allow_upn",
+ reqctx->opts->allow_upn,
+ &reqctx->opts->allow_upn);
+ pkinit_libdefault_string(context, &request->server->realm,
+ "pkinit_eku_checking",
+ &eku_string);
+ if (eku_string != NULL) {
+ if (strcasecmp(eku_string, "kpKDC") == 0) {
+ reqctx->opts->require_eku = 1;
+ reqctx->opts->accept_secondary_eku = 0;
+ } else if (strcasecmp(eku_string, "kpServerAuth") == 0) {
+ reqctx->opts->require_eku = 1;
+ reqctx->opts->accept_secondary_eku = 1;
+ } else if (strcasecmp(eku_string, "none") == 0) {
+ reqctx->opts->require_eku = 0;
+ reqctx->opts->accept_secondary_eku = 0;
+ } else {
+ pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
+ __FUNCTION__, eku_string);
+ }
+ free(eku_string);
+ }
+
/* Only process anchors here if they were not specified on command line */
if (reqctx->idopts->anchors == NULL)
pkinit_libdefault_strings(context, &request->server->realm,
@@ -905,8 +1025,11 @@ pkinit_client_process(krb5_context context,
pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data);
retval = pkinit_initialize_identity(context, reqctx->idopts,
reqctx->idctx);
- if (retval)
+ if (retval) {
+ pkiDebug("pkinit_initialize_identity returned %d (%s)\n",
+ retval, error_message(retval));
return retval;
+ }
retval = pa_pkinit_gen_req(context, plgctx, reqctx, request,
in_padata, out_padata, prompter,
prompter_data, opt);
@@ -916,8 +1039,11 @@ pkinit_client_process(krb5_context context,
*/
retval = (*get_data_proc)(context, rock,
krb5plugin_preauth_client_get_etype, &cdata);
- if (retval)
+ if (retval) {
+ pkiDebug("get_data_proc returned %d (%s)\n",
+ retval, error_message(retval));
return retval;
+ }
enctype = *((krb5_enctype *)cdata->data);
(*get_data_proc)(context, rock,
krb5plugin_preauth_client_free_etype, &cdata);
@@ -1077,11 +1203,11 @@ pkinit_client_req_init(krb5_context context,
goto cleanup;
reqctx->opts->require_eku = plgctx->opts->require_eku;
+ reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku;
reqctx->opts->require_san = plgctx->opts->require_san;
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->require_hostname_match = 0;
retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
if (retval)
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
new file mode 100644
index 0000000..a753dd0
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -0,0 +1,453 @@
+/*
+ * COPYRIGHT (C) 2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+/*
+ * This header defines the cryptographic interface
+ */
+
+#ifndef _PKINIT_CRYPTO_H
+#define _PKINIT_CRYPTO_H
+
+#include <krb5/krb5.h>
+#include <krb5/preauth_plugin.h>
+#include <k5-int-pkinit.h>
+#include <profile.h>
+#include "pkinit_accessor.h"
+
+/*
+ * these describe the CMS message types
+ */
+enum cms_msg_types {
+ CMS_SIGN_CLIENT,
+ CMS_SIGN_DRAFT9,
+ CMS_SIGN_SERVER,
+ CMS_ENVEL_SERVER
+};
+
+/*
+ * Functions to initialize and cleanup crypto contexts
+ */
+krb5_error_code pkinit_init_plg_crypto(pkinit_plg_crypto_context *);
+void pkinit_fini_plg_crypto(pkinit_plg_crypto_context);
+
+krb5_error_code pkinit_init_req_crypto(pkinit_req_crypto_context *);
+void pkinit_fini_req_crypto(pkinit_req_crypto_context);
+
+krb5_error_code pkinit_init_identity_crypto(pkinit_identity_crypto_context *);
+void pkinit_fini_identity_crypto(pkinit_identity_crypto_context);
+
+/*
+ * this function creates a CMS message where eContentType is SignedData
+ */
+krb5_error_code cms_signeddata_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 */
+ 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) */
+ unsigned int auth_pack_len, /* IN
+ contains length of auth_pack */
+ unsigned char **signed_data, /* OUT
+ for CMS_SIGN_CLIENT receives DER encoded
+ SignedAuthPack (CMS_SIGN_CLIENT) or DER
+ encoded DHInfo (CMS_SIGN_SERVER) */
+ unsigned int *signed_data_len); /* OUT
+ receives length of signed_data */
+
+/*
+ * this function verifies a CMS message where eContentType is SignedData
+ */
+krb5_error_code cms_signeddata_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 */
+ int cms_msg_type, /* IN
+ specifies CMS_SIGN_CLIENT for client-side
+ CMS message and CMS_SIGN_SERVER for kdc-side */
+ int require_crl_checking, /* IN
+ specifies whether CRL checking should be
+ strictly enforced, i.e. if no CRLs available
+ for the CA then fail verification.
+ note, if the value is 0, crls are still
+ checked if present */
+ unsigned char *signed_data, /* IN
+ contains DER encoded SignedAuthPack (CMS_SIGN_CLIENT)
+ or DER encoded DHInfo (CMS_SIGN_SERVER) */
+ unsigned int signed_data_len, /* IN
+ contains length of signed_data*/
+ unsigned char **auth_pack, /* OUT
+ receives DER encoded AuthPack (CMS_SIGN_CLIENT)
+ or DER encoded DHRepInfo (CMS_SIGN_SERVER)*/
+ unsigned int *auth_pack_len, /* OUT
+ receives length of auth_pack */
+ unsigned char **authz_data, /* OUT
+ receives required authorization data that
+ contains the verified certificate chain
+ (only used by the KDC) */
+ unsigned int *authz_data_len); /* OUT
+ receives length of authz_data */
+
+/*
+ * 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 */
+ 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
+ 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 returns SAN information found in the
+ * received certificate. at least one of pkinit_sans,
+ * upn_sans, or kdc_hostnames must be non-NULL.
+ */
+krb5_error_code crypto_retrieve_cert_sans
+ (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_principal **pkinit_sans, /* OUT
+ if non-NULL, a null-terminated array of
+ id-pkinit-san values found in the certificate
+ are returned */
+ krb5_principal **upn_sans, /* OUT
+ if non-NULL, a null-terminated array of
+ id-ms-upn-san values found in the certificate
+ are returned */
+ unsigned char ***kdc_hostname); /* OUT
+ if non-NULL, a null-terminated array of
+ dNSName (hostname) SAN values found in the
+ certificate are returned */
+
+/*
+ * this function checks for acceptable key usage values
+ * in the received certificate.
+ *
+ * when checking a received kdc certificate, it looks for
+ * the kpKdc key usage. if allow_secondary_usage is
+ * non-zero, it will also accept kpServerAuth.
+ *
+ * when checking a received user certificate, it looks for
+ * kpClientAuth key usage. if allow_secondary_usage is
+ * non-zero, it will also accept id-ms-sc-logon EKU.
+ *
+ * this function must also assert that the digitalSignature
+ * key usage is consistent.
+ */
+krb5_error_code crypto_check_cert_eku
+ (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 */
+ int checking_kdc_cert, /* IN
+ specifies if the received certificate is
+ a KDC certificate (non-zero),
+ or a user certificate (zero) */
+ int allow_secondary_usage, /* IN
+ specifies if the secondary key usage
+ should be accepted or not (see above) */
+ int *eku_valid); /* OUT
+ receives non-zero if an acceptable EKU was found */
+
+/*
+ * this functions takes in generated DH secret key and converts
+ * it in to a kerberos session key. it takes into the account the
+ * enc type and then follows the procedure specified in the RFC p 22.
+ */
+krb5_error_code pkinit_octetstring2key
+ (krb5_context context, /* IN */
+ krb5_enctype etype, /* IN
+ specifies the enc type */
+ unsigned char *key, /* IN
+ contains the DH secret key */
+ unsigned int key_len, /* IN
+ contains length of key */
+ krb5_keyblock * krb5key); /* OUT
+ receives kerberos session key */
+
+/*
+ * this function implements clients first part of the DH protocol.
+ * client selects its DH parameters and pub key
+ */
+krb5_error_code client_create_dh
+ (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 */
+ 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 */
+
+/*
+ * this function completes client's the DH protocol. client
+ * processes received DH pub key from the KDC and computes
+ * the DH secret key
+ */
+krb5_error_code client_process_dh
+ (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 */
+ unsigned char *dh_pubkey, /* IN
+ 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
+ receives DH secret key */
+ unsigned int *dh_session_key_len); /* OUT
+ receives length of dh_session_key */
+
+/*
+ * this function implements the KDC first part of the DH protocol.
+ * it decodes the client's DH parameters and pub key and checks
+ * if they are acceptable.
+ */
+krb5_error_code server_check_dh
+ (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_octet_data *dh_params, /* IN
+ ???? */
+ int minbits); /* IN
+ the mininum number of key bits acceptable */
+
+/*
+ * this function completes the KDC's DH protocol. The KDC generates
+ * its DH pub key and computes the DH secret key
+ */
+krb5_error_code server_process_dh
+ (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 */
+ 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
+ receives KDC's DER encoded DH pub key */
+ unsigned int *dh_pubkey_len, /* OUT
+ receives length of dh_pubkey */
+ unsigned char **server_key, /* OUT
+ receives DH secret key */
+ unsigned int *server_key_len); /* OUT
+ receives length of server_key */
+
+/*
+ * this functions takes in crypto specific representation of
+ * trustedCertifiers and creates a list of
+ * krb5_external_principal_identifier
+ */
+krb5_error_code create_krb5_trustedCertifiers
+ (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_external_principal_identifier ***trustedCertifiers); /* OUT */
+
+/*
+ * this functions takes in crypto specific representation of
+ * trustedCas (draft9) and creates a list of krb5_trusted_ca (draft9).
+ * draft9 trustedCAs is a CHOICE. we only support choices for
+ * [1] caName and [2] issuerAndSerial. there is no config
+ * option available to select the choice yet. default = 1.
+ */
+krb5_error_code create_krb5_trustedCas
+ (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 */
+ int flag, /* IN
+ specifies the tag of the CHOICE */
+ krb5_trusted_ca ***trustedCas); /* OUT */
+
+/*
+ * this functions takes in crypto specific representation of the
+ * KDC's certificate and creates a DER encoded kdcPKId
+ */
+krb5_error_code create_issuerAndSerial
+ (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 */
+ unsigned char **kdcId_buf, /* OUT
+ receives DER encoded kdcPKId */
+ unsigned int *kdcId_len); /* OUT
+ receives length of encoded kdcPKId */
+
+/*
+ * process identity options specified via the command-line
+ * or config file and populate the crypto-specific identity
+ * information.
+ */
+krb5_error_code pkinit_process_identity_option
+ (krb5_context context, /* IN */
+ int attr, /* IN */
+ const char *value, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx); /* IN/OUT */
+
+krb5_error_code pkinit_get_kdc_cert
+ (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *principal, krb5_get_init_creds_opt *opt);
+
+/*
+ * this function creates edata that contains TD-DH-PARAMETERS
+ */
+krb5_error_code pkinit_create_td_dh_parameters
+ (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 */
+ pkinit_plg_opts *opts, /* IN */
+ krb5_data **edata); /* OUT */
+
+/*
+ * this function processes edata that contains TD-DH-PARAMETERS.
+ * the client processes the received acceptable by KDC DH
+ * parameters and picks the first acceptable to it. it matches
+ * them against the known DH parameters.
+ */
+krb5_error_code pkinit_process_td_dh_params
+ (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_algorithm_identifier **algId, /* IN */
+ int *new_dh_size); /* OUT
+ receives the new DH modulus to use in the new AS-REQ */
+
+/*
+ * this function creates edata that contains TD-INVALID-CERTIFICATES
+ */
+krb5_error_code pkinit_create_td_invalid_certificate
+ (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_data **edata); /* OUT */
+
+/*
+ * this function creates edata that contains TD-TRUSTED-CERTIFIERS
+ */
+krb5_error_code pkinit_create_td_trusted_certifiers
+ (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_data **edata); /* OUT */
+
+/*
+ * this function processes edata that contains either
+ * TD-TRUSTED-CERTIFICATES or TD-INVALID-CERTIFICATES.
+ * current implementation only decodes the received message
+ * but does not act on it
+ */
+krb5_error_code pkinit_process_td_trusted_certifiers
+ (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_external_principal_identifier **trustedCertifiers, /* IN */
+ int td_type); /* IN */
+
+/*
+ * this function checks if the received kdcPKId matches
+ * the KDC's certificate
+ */
+krb5_error_code pkinit_check_kdc_pkid
+ (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 */
+ unsigned char *pdid_buf, /* IN
+ contains DER encoded kdcPKId */
+ unsigned int pkid_len, /* IN
+ contains length of pdid_buf */
+ int *valid_kdcPkId); /* OUT
+ 1 if kdcPKId matches, otherwise 0 */
+
+krb5_error_code pkinit_identity_set_prompter
+ (pkinit_identity_crypto_context id_cryptoctx, /* IN */
+ krb5_prompter_fct prompter, /* IN */
+ void *prompter_data); /* IN */
+
+#endif /* _PKINIT_CRYPTO_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index af26661..b51040b 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -164,12 +164,7 @@ unsigned char pkinit_4096_dhprime[4096/8] = {
static int pkinit_oids_refs = 0;
-const krb5_octet_data dh_oid = { 0, 7,
- (unsigned char *)"\x2A\x86\x48\xce\x3e\x02\x01" };
-
-static krb5_error_code
-create_identifiers_from_stack(STACK_OF(X509) *sk,
- krb5_external_principal_identifier *** ids);
+#define MAX_CREDS_ALLOWED 20
krb5_error_code
pkinit_init_plg_crypto(pkinit_plg_crypto_context *cryptoctx) {
@@ -304,61 +299,57 @@ static krb5_error_code
pkinit_init_pkinit_oids(pkinit_plg_crypto_context ctx)
{
krb5_error_code retval = ENOMEM;
- int tmp = 0;
+ int nid = 0;
- tmp = OBJ_create("1.3.6.1.5.2.2", "id-pkinit-san", "KRB5PrincipalName");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_san = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
-
- tmp = OBJ_create("1.3.6.1.5.2.3.1", "id-pkinit-authdata",
- "PKINIT signedAuthPack");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_authData = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
-
- tmp = OBJ_create("1.3.6.1.5.2.3.2", "id-pkinit-DHKeyData",
- "PKINIT dhSignedData");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_DHKeyData = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
+ /*
+ * If OpenSSL already knows about the OID, use the
+ * existing definition. Otherwise, create an OID object.
+ */
+ #define CREATE_OBJ_IF_NEEDED(oid, vn, sn, ln) \
+ nid = OBJ_txt2nid(oid); \
+ if (nid == NID_undef) { \
+ nid = OBJ_create(oid, sn, ln); \
+ if (nid == NID_undef) { \
+ pkiDebug("Error creating oid object for '%s'\n", oid); \
+ goto out; \
+ } \
+ } \
+ ctx->vn = OBJ_nid2obj(nid);
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.2", id_pkinit_san,
+ "id-pkinit-san", "KRB5PrincipalName");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.1", id_pkinit_authData,
+ "id-pkinit-authdata", "PKINIT signedAuthPack");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.2", id_pkinit_DHKeyData,
+ "id-pkinit-DHKeyData", "PKINIT dhSignedData");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.3", id_pkinit_rkeyData,
+ "id-pkinit-rkeyData", "PKINIT encKeyPack");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.4", id_pkinit_KPClientAuth,
+ "id-pkinit-KPClientAuth", "PKINIT Client EKU");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.5", id_pkinit_KPKdc,
+ "id-pkinit-KPKdc", "KDC EKU");
+
+#if 0
+ CREATE_OBJ_IF_NEEDED("1.2.840.113549.1.7.1", id_pkinit_authData9,
+ "id-pkcs7-data", "PKCS7 data");
+#else
+ /* See note in pkinit_pkcs7type2oid() */
+ ctx->id_pkinit_authData9 = NULL;
+#endif
- tmp = OBJ_create("1.3.6.1.5.2.3.3", "id-pkinit-rkeyData",
- "PKINIT encKeyPack");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_rkeyData = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.4.1.311.20.2.2", id_ms_kp_sc_logon,
+ "id-ms-kp-sc-logon EKU", "Microsoft SmartCard Login EKU");
- tmp = OBJ_create("1.3.6.1.5.2.3.4", "id-pkinit-KPClientAuth",
- "PKINIT Client EKU");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_KPClientAuth = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.4.1.311.20.2.3", id_ms_san_upn,
+ "id-ms-san-upn", "Microsoft Universal Principal Name");
- tmp = OBJ_create("1.3.6.1.5.2.3.5", "id-pkinit-KPKdc", "KDC EKU");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_KPKdc = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
- tmp = OBJ_create("1.2.840.113549.1.7.1", "id-data",
- "CMS id-data");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_authData9 = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
- tmp = OBJ_create("1.3.6.1.4.1.311.20.2.3", "id-pkinit-san draft9",
- "KRB5PrincipalName draft9");
- if (tmp == NID_undef)
- goto out;
- ctx->id_pkinit_san9 = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
- tmp = OBJ_create("1.3.6.1.4.1.311.20.2.2", "id-ms-kp-sc-logon EKU",
- "KDC/Client EKU draft9");
- if (tmp == NID_undef)
- goto out;
- ctx->id_ms_kp_sc_logon = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
- tmp = OBJ_create("1.3.6.1.5.5.7.3.1", "id-kp-serverAuth EKU",
- "KDC EKU draft9");
- if (tmp == NID_undef)
- goto out;
- ctx->id_kp_serverAuth = (ASN1_OBJECT *)OBJ_nid2obj(tmp);
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.5.7.3.1", id_kp_serverAuth,
+ "id-kp-serverAuth EKU", "Server Authentication EKU");
/* Success */
retval = 0;
@@ -589,7 +580,7 @@ cms_signeddata_create(krb5_context context,
if ((p7s = PKCS7_SIGNED_new()) == NULL)
goto cleanup;
p7->d.sign = p7s;
- if (!ASN1_INTEGER_set(p7s->version, 1))
+ if (!ASN1_INTEGER_set(p7s->version, 3))
goto cleanup;
/* create a cert chain that has at least the signer's certificate */
@@ -605,7 +596,7 @@ cms_signeddata_create(krb5_context context,
X509_STORE *certstore = NULL;
X509_STORE_CTX certctx;
STACK_OF(X509) *certstack = NULL;
- char buf[256];
+ char buf[DN_BUF_LEN];
int i = 0, size = 0;
if ((certstore = X509_STORE_new()) == NULL)
@@ -627,7 +618,7 @@ cms_signeddata_create(krb5_context context,
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, 256);
+ 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));
}
@@ -709,7 +700,7 @@ cms_signeddata_create(krb5_context context,
* digestAlgorithm AlgorithmIdentifier,
* digest OCTET STRING }
*/
- if (id_cryptoctx->pkcs11_method && id_cryptoctx->mech == CKM_RSA_PKCS) {
+ if (id_cryptoctx->pkcs11_method == 1 && id_cryptoctx->mech == CKM_RSA_PKCS) {
pkiDebug("mech = CKM_RSA_PKCS\n");
EVP_MD_CTX_init(&ctx2);
EVP_DigestInit_ex(&ctx2, md_tmp, NULL);
@@ -755,7 +746,7 @@ cms_signeddata_create(krb5_context context,
#endif
{
pkiDebug("mech = %s\n",
- id_cryptoctx->pkcs11_method ? "CKM_SHA1_RSA_PKCS" : "FS");
+ id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS");
retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen,
&sig, &sig_len);
}
@@ -800,6 +791,7 @@ cms_signeddata_create(krb5_context context,
if (!PKCS7_set0_type_other(inner_p7, OBJ_obj2nid(oid), pkinit_data))
goto cleanup2;
+
if (p7s->contents != NULL)
PKCS7_free(p7s->contents);
p7s->contents = inner_p7;
@@ -830,13 +822,13 @@ cms_signeddata_create(krb5_context context,
retval = 0;
#ifdef DEBUG_ASN1
- //print_buffer_bin(*signed_data, *signed_data_len, "/tmp/pkcs7_signeddata");
+ print_buffer_bin(*signed_data, *signed_data_len, "/tmp/pkcs7_signeddata");
#endif
cleanup2:
EVP_MD_CTX_cleanup(&ctx);
#ifndef WITHOUT_PKCS11
- if (id_cryptoctx->pkcs11_method && id_cryptoctx->mech == CKM_RSA_PKCS)
+ if (id_cryptoctx->pkcs11_method == 1 && id_cryptoctx->mech == CKM_RSA_PKCS)
EVP_MD_CTX_cleanup(&ctx2);
#endif
if (alg != NULL)
@@ -850,7 +842,7 @@ cms_signeddata_create(krb5_context context,
if (digestInfo_buf != NULL)
free(digestInfo_buf);
cleanup:
- if (p7 != NULL)
+ if (p7 != NULL)
PKCS7_free(p7);
if (sig != NULL)
free(sig);
@@ -886,14 +878,20 @@ cms_signeddata_verify(krb5_context context,
STACK_OF(X509) *intermediateCAs = NULL;
STACK_OF(X509_CRL) *revoked = NULL;
STACK_OF(X509) *verified_chain = NULL;
+ ASN1_OBJECT *oid = NULL;
krb5_external_principal_identifier **krb5_verified_chain = NULL;
krb5_data *authz = NULL;
- char buf[256];
+ char buf[DN_BUF_LEN];
#ifdef DEBUG_ASN1
print_buffer_bin(signed_data, signed_data_len, "/tmp/pkcs7_signeddata");
#endif
+ /* Do this early enough to create the shadow OID for pkcs7-data if needed */
+ oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type);
+ if (oid == NULL)
+ goto cleanup;
+
/* decode received PKCS7 messag */
if ((p7 = d2i_PKCS7(NULL, &p, (int)signed_data_len)) == NULL) {
unsigned long err = ERR_peek_error();
@@ -988,7 +986,7 @@ cms_signeddata_verify(krb5_context context,
pkiDebug("untrusted cert chain of size %d\n", size);
for (i = 0; i < size; i++) {
X509_NAME_oneline(X509_get_subject_name(
- sk_X509_value(intermediateCAs, i)), buf, 256);
+ sk_X509_value(intermediateCAs, i)), buf, sizeof(buf));
pkiDebug("cert #%d: %s\n", i, buf);
}
}
@@ -997,7 +995,7 @@ cms_signeddata_verify(krb5_context context,
pkiDebug("trusted cert chain of size %d\n", size);
for (i = 0; i < size; i++) {
X509_NAME_oneline(X509_get_subject_name(
- sk_X509_value(idctx->trustedCAs, i)), buf, 256);
+ sk_X509_value(idctx->trustedCAs, i)), buf, sizeof(buf));
pkiDebug("cert #%d: %s\n", i, buf);
}
}
@@ -1006,7 +1004,7 @@ cms_signeddata_verify(krb5_context context,
pkiDebug("CRL chain of size %d\n", size);
for (i = 0; i < size; i++) {
X509_CRL *crl = sk_X509_CRL_value(revoked, i);
- X509_NAME_oneline(X509_CRL_get_issuer(crl), buf, 256);
+ X509_NAME_oneline(X509_CRL_get_issuer(crl), buf, sizeof(buf));
pkiDebug("crls by CA #%d: %s\n", i , buf);
}
}
@@ -1032,7 +1030,7 @@ cms_signeddata_verify(krb5_context context,
retval = KRB5KDC_ERR_INVALID_CERTIFICATE;
}
X509_NAME_oneline(X509_get_subject_name(
- reqctx->received_cert), buf, 256);
+ reqctx->received_cert), buf, sizeof(buf));
pkiDebug("problem with cert DN = %s (error=%d) %s\n", buf, j,
X509_verify_cert_error_string(j));
krb5_set_error_message(context, retval, "%s\n",
@@ -1042,7 +1040,7 @@ cms_signeddata_verify(krb5_context context,
pkiDebug("received cert chain of size %d\n", size);
for (j = 0; j < size; j++) {
X509 *tmp_cert = sk_X509_value(p7->d.sign->cert, j);
- X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, 256);
+ X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, sizeof(buf));
pkiDebug("cert #%d: %s\n", j, buf);
}
#endif
@@ -1057,10 +1055,6 @@ cms_signeddata_verify(krb5_context context,
out = BIO_new(BIO_s_mem());
if (PKCS7_verify(p7, NULL, store, NULL, out, flags)) {
- ASN1_OBJECT *oid = NULL;
- oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type);
- if (oid == NULL)
- goto cleanup;
if (!OBJ_cmp(p7->d.sign->contents->type, oid))
pkiDebug("PKCS7 Verification successful\n");
else {
@@ -1290,8 +1284,9 @@ cms_envelopeddata_verify(krb5_context context,
int i = 0;
unsigned int size = 0;
const unsigned char *p = enveloped_data;
- unsigned int tmp_buf_len = 0, tmp_buf2_len = 0;
- unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL;
+ 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;
/* decode received PKCS7 message */
if ((p7 = d2i_PKCS7(NULL, &p, (int)enveloped_data_len)) == NULL) {
@@ -1334,35 +1329,55 @@ cms_envelopeddata_verify(krb5_context context,
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:
- retval = encode_signeddata(tmp_buf, tmp_buf_len, &tmp_buf2,
- &tmp_buf2_len);
- if (retval) {
- pkiDebug("failed to encode signeddata\n");
- goto cleanup;
- }
-#ifdef DEBUG_ASN1
- print_buffer_bin(tmp_buf2, tmp_buf2_len, "/tmp/client_enc_keypack2");
-#endif
- retval = cms_signeddata_verify(context, plg_cryptoctx,
- req_cryptoctx, id_cryptoctx, CMS_ENVEL_SERVER,
- require_crl_checking, tmp_buf2, tmp_buf2_len, data,
- data_len, NULL, NULL);
+ msg_type = CMS_ENVEL_SERVER;
+
break;
case KRB5_PADATA_PK_AS_REP_OLD:
- retval = cms_signeddata_verify(context, plg_cryptoctx,
- req_cryptoctx, id_cryptoctx, CMS_SIGN_DRAFT9,
- require_crl_checking, tmp_buf, tmp_buf_len, data,
- data_len, NULL, NULL);
+ msg_type = CMS_SIGN_DRAFT9;
break;
default:
- pkiDebug("unrecognized pa_type = %d\n", pa_type);
+ pkiDebug("%s: unrecognized pa_type = %d\n", __FUNCTION__, pa_type);
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
}
+ /* If this is the RFC style, wrap the signed data to make
+ * decoding easier in the verify routine.
+ * For win2k-compatible, we don't do anything because it
+ * is already wrapped. (Except for longhorn which does
+ * things differently...)
+ */
+ if (msg_type == CMS_ENVEL_SERVER || longhorn) {
+ retval = wrap_signeddata(tmp_buf, tmp_buf_len,
+ &tmp_buf2, &tmp_buf2_len, longhorn);
+ 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;
+ }
+
+#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,
+ require_crl_checking,
+ vfy_buf, vfy_buf_len,
+ data, data_len, NULL, NULL);
+
if (!retval)
pkiDebug("PKCS7 Verification Success\n");
else {
@@ -1386,44 +1401,78 @@ cms_envelopeddata_verify(krb5_context context,
return retval;
}
-krb5_error_code
-verify_id_pkinit_san(krb5_context context,
- pkinit_plg_crypto_context plgctx,
- pkinit_req_crypto_context reqctx,
- pkinit_identity_crypto_context idctx,
- krb5_preauthtype pa_type,
- int allow_upn,
- krb5_principal *out,
- unsigned char **kdc_hostname,
- int *valid_san)
+static krb5_error_code
+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,
+ unsigned char ***dns_ret)
{
- krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- int i = 0, ok = 0;
- char buf[256];
+ krb5_error_code retval = EINVAL;
+ char buf[DN_BUF_LEN];
+ int p = 0, u = 0, d = 0;
+ krb5_principal *princs = NULL;
+ krb5_principal *upns = NULL;
+ unsigned char **dnss = NULL;
+ int i, num_found = 0;
+
+ if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) {
+ pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__);
+ return retval;
+ }
- *valid_san = 0;
- if (reqctx->received_cert == NULL) return retval;
+ if (cert == NULL) {
+ pkiDebug("%s: no certificate!\n", __FUNCTION__);
+ return retval;
+ }
- /* now let's verify that KDC's certificate has id-pkinit-san */
- X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert), buf, 256);
- pkiDebug("looking for SANs in cert = %s\n", buf);
+ X509_NAME_oneline(X509_get_subject_name(cert),
+ buf, sizeof(buf));
+ pkiDebug("%s: looking for SANs in cert = %s\n", __FUNCTION__, buf);
- if ((i = X509_get_ext_by_NID(reqctx->received_cert,
- NID_subject_alt_name, -1)) >= 0) {
+ if ((i = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1)) >= 0) {
X509_EXTENSION *ext = NULL;
GENERAL_NAMES *ialt = NULL;
GENERAL_NAME *gen = NULL;
int ret = 0;
+ unsigned int num_sans = 0;
- if (!(ext = X509_get_ext(reqctx->received_cert, i))
- || !(ialt = X509V3_EXT_d2i(ext))) {
- pkiDebug("unable to retrieve subject alt name ext\n");
+ if (!(ext = X509_get_ext(cert, i)) || !(ialt = X509V3_EXT_d2i(ext))) {
+ pkiDebug("%s: found no subject alt name extensions\n",
+ __FUNCTION__);
goto cleanup;
}
- pkiDebug("found %d subject alt name extension(s)\n",
- sk_GENERAL_NAME_num(ialt));
+ 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));
+ if (princs == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ }
+ if (upn_ret != NULL) {
+ upns = calloc(num_sans + 1, sizeof(krb5_principal));
+ if (upns == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ }
+ if (dns_ret != NULL) {
+ dnss = calloc(num_sans + 1, sizeof(*dnss));
+ if (dnss == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ }
- for (i = 0; i < sk_GENERAL_NAME_num(ialt); i++) {
+ for (i = 0; i < num_sans; i++) {
krb5_data name = { 0, 0, NULL };
gen = sk_GENERAL_NAME_value(ialt, i);
@@ -1431,143 +1480,188 @@ verify_id_pkinit_san(krb5_context context,
case GEN_OTHERNAME:
name.length = gen->d.otherName->value->value.sequence->length;
name.data = (char *)gen->d.otherName->value->value.sequence->data;
- if (!OBJ_cmp(plgctx->id_pkinit_san, gen->d.otherName->type_id)) {
+ if (princs != NULL
+ && OBJ_cmp(plgctx->id_pkinit_san,
+ gen->d.otherName->type_id) == 0) {
#ifdef DEBUG_ASN1
print_buffer_bin((unsigned char *)name.data, name.length,
"/tmp/pkinit_san");
#endif
- ret = k5int_decode_krb5_principal_name(&name, out);
- } else if (allow_upn && !OBJ_cmp(plgctx->id_pkinit_san9,
- gen->d.otherName->type_id)) {
- ret = krb5_parse_name(context, name.data, out);
+ ret = k5int_decode_krb5_principal_name(&name, &princs[p]);
+ if (ret) {
+ pkiDebug("%s: failed decoding pkinit san value\n",
+ __FUNCTION__);
+ } else {
+ p++;
+ num_found++;
+ }
+ } else if (upns != NULL
+ && OBJ_cmp(plgctx->id_ms_san_upn,
+ gen->d.otherName->type_id) == 0) {
+ ret = krb5_parse_name(context, name.data, &upns[u]);
+ if (ret) {
+ pkiDebug("%s: failed parsing ms-upn san value\n",
+ __FUNCTION__);
+ } else {
+ u++;
+ num_found++;
+ }
} else {
- pkiDebug("unrecognized or pa_type incorrect oid in SAN\n");
+ pkiDebug("%s: unrecognized othername oid in SAN\n",
+ __FUNCTION__);
continue;
}
- if (ret) {
- pkiDebug("failed to decode Krb5PrincipalName in SAN\n");
- break;
- }
- if (out != NULL)
- ok = 1;
break;
case GEN_DNS:
- if (pa_type == KRB5_PADATA_PK_AS_REP_OLD &&
- kdc_hostname != NULL) {
- pkiDebug("Win2K KDC on host = %s\n", gen->d.dNSName->data);
- *kdc_hostname = (unsigned char *)
+ if (dnss != NULL) {
+ pkiDebug("%s: found dns name = %s\n",
+ __FUNCTION__, gen->d.dNSName->data);
+ dnss[d] = (unsigned char *)
strdup((char *)gen->d.dNSName->data);
- ok = 1;
+ if (dnss[d] == NULL) {
+ pkiDebug("%s: failed to duplicate dns name\n",
+ __FUNCTION__);
+ } else {
+ d++;
+ num_found++;
+ }
}
break;
default:
- pkiDebug("SAN type = %d expecting %d\n", gen->type,
- GEN_OTHERNAME);
+ pkiDebug("%s: SAN type = %d expecting %d\n",
+ __FUNCTION__, gen->type, GEN_OTHERNAME);
}
- if (ok)
- break;
}
sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
}
- if (!ok) {
- pkiDebug("didn't find id_pkinit_san\n");
- }
retval = 0;
+ if (princs)
+ *princs_ret = princs;
+ if (upns)
+ *upn_ret = upns;
+ if (dnss)
+ *dns_ret = dnss;
cleanup:
- *valid_san = ok;
+ if (retval) {
+ if (princs != NULL) {
+ for (i = 0; princs[i] != NULL; i++)
+ krb5_free_principal(context, princs[i]);
+ free(princs);
+ }
+ if (upns != NULL) {
+ for (i = 0; upns[i] != NULL; i++)
+ krb5_free_principal(context, upns[i]);
+ free(upns);
+ }
+ if (dnss != NULL) {
+ for (i = 0; dnss[i] != NULL; i++)
+ free(dnss[i]);
+ free(dnss);
+ }
+ }
return retval;
}
krb5_error_code
-verify_id_pkinit_eku(krb5_context context,
- pkinit_plg_crypto_context plgctx,
- pkinit_req_crypto_context reqctx,
- pkinit_identity_crypto_context idctx,
- krb5_preauthtype pa_type,
- int require_eku,
- int *valid_eku)
+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,
+ unsigned char ***dns_ret)
{
- krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- int i = 0, ok = 0;
- char buf[256];
- ASN1_OBJECT * oid_client = NULL, *oid_server = NULL;
- ASN1_OBJECT *oid_logon = NULL, *oid_kp = NULL;
+ krb5_error_code retval = EINVAL;
- *valid_eku = 0;
- if (reqctx->received_cert == NULL) return retval;
+ if (reqctx->received_cert == NULL) {
+ pkiDebug("%s: No certificate!\n", __FUNCTION__);
+ return retval;
+ }
- /* now let's verify that KDC's certificate has pkinit EKU */
- X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert), buf, 256);
- pkiDebug("looking for EKUs in cert = %s\n", buf);
+ return crypto_retrieve_X509_sans(context, plgctx, reqctx,
+ reqctx->received_cert, princs_ret,
+ upn_ret, dns_ret);
+}
- oid_client = plgctx->id_pkinit_KPClientAuth;
- oid_server = plgctx->id_pkinit_KPKdc;
- oid_logon = plgctx->id_ms_kp_sc_logon;
- oid_kp = plgctx->id_kp_serverAuth;
+krb5_error_code
+crypto_check_cert_eku(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_identity_crypto_context idctx,
+ int checking_kdc_cert,
+ int allow_secondary_usage,
+ int *valid_eku)
+{
+ char buf[DN_BUF_LEN];
+ int found_eku = 0;
+ krb5_error_code retval = EINVAL;
+ int i;
+
+ *valid_eku = 0;
+ if (reqctx->received_cert == NULL)
+ goto cleanup;
+
+ X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert),
+ buf, sizeof(buf));
+ pkiDebug("%s: looking for EKUs in cert = %s\n", __FUNCTION__, buf);
if ((i = X509_get_ext_by_NID(reqctx->received_cert,
- NID_ext_key_usage, -1)) >= 0) {
+ NID_ext_key_usage, -1)) >= 0) {
EXTENDED_KEY_USAGE *extusage;
- if ((extusage = X509_get_ext_d2i(reqctx->received_cert,
- NID_ext_key_usage, NULL, NULL))) {
- for (i = 0; i < sk_ASN1_OBJECT_num(extusage); i++) {
- ASN1_OBJECT *tmp_oid = NULL;
- int flag = 0;
+ extusage = X509_get_ext_d2i(reqctx->received_cert, NID_ext_key_usage,
+ NULL, NULL);
+ if (extusage) {
+ pkiDebug("%s: found eku info in the cert\n", __FUNCTION__);
+ for (i = 0; found_eku == 0 && i < sk_ASN1_OBJECT_num(extusage); i++) {
+ ASN1_OBJECT *tmp_oid;
tmp_oid = sk_ASN1_OBJECT_value(extusage, i);
- switch ((int)pa_type) {
- case KRB5_PADATA_PK_AS_REQ_OLD:
- case KRB5_PADATA_PK_AS_REQ:
- if (!OBJ_cmp(oid_client, tmp_oid) ||
- !OBJ_cmp(oid_logon, tmp_oid))
- flag = 1;
- break;
- case KRB5_PADATA_PK_AS_REP_OLD:
- case KRB5_PADATA_PK_AS_REP:
- if (!OBJ_cmp(oid_server, tmp_oid) ||
- !OBJ_cmp(oid_kp, tmp_oid))
- flag = 1;
- break;
- default:
- goto cleanup;
- }
- if (flag) {
- ASN1_BIT_STRING *usage = NULL;
- pkiDebug("found pa_type-specific EKU\n");
-
- /* check that digitalSignature KeyUsage is present */
- if ((usage = X509_get_ext_d2i(reqctx->received_cert,
- NID_key_usage, NULL, NULL))) {
-
- if (!ku_reject(reqctx->received_cert,
- X509v3_KU_DIGITAL_SIGNATURE)) {
- pkiDebug("found digitalSignature KU\n");
- ok = 1;
- } else
- pkiDebug("didn't find digitalSignature KU\n");
- }
-
- ASN1_BIT_STRING_free(usage);
- break;
+ pkiDebug("%s: checking eku %d of %d, allow_secondary = %d\n",
+ __FUNCTION__, i+1, sk_ASN1_OBJECT_num(extusage),
+ allow_secondary_usage);
+ if (checking_kdc_cert) {
+ if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPKdc) == 0)
+ || (allow_secondary_usage
+ && OBJ_cmp(tmp_oid, plgctx->id_kp_serverAuth) == 0))
+ found_eku = 1;
+ } else {
+ if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPClientAuth) == 0)
+ || (allow_secondary_usage
+ && OBJ_cmp(tmp_oid, plgctx->id_ms_kp_sc_logon) == 0))
+ found_eku = 1;
}
}
}
EXTENDED_KEY_USAGE_free(extusage);
+
+ if (found_eku) {
+ ASN1_BIT_STRING *usage = NULL;
+ pkiDebug("%s: found acceptable EKU, checking for digitalSignature\n", __FUNCTION__);
+
+ /* check that digitalSignature KeyUsage is present */
+ if ((usage = X509_get_ext_d2i(reqctx->received_cert,
+ NID_key_usage, NULL, NULL))) {
+
+ if (!ku_reject(reqctx->received_cert,
+ X509v3_KU_DIGITAL_SIGNATURE)) {
+ pkiDebug("%s: found digitalSignature KU\n",
+ __FUNCTION__);
+ *valid_eku = 1;
+ } else
+ pkiDebug("%s: didn't find digitalSignature KU\n",
+ __FUNCTION__);
+ }
+ ASN1_BIT_STRING_free(usage);
+ }
}
retval = 0;
cleanup:
- if (!ok) {
- pkiDebug("didn't find extended key usage (EKU) for pkinit\n");
- if (0 == require_eku) {
- pkiDebug("configuration says ignore missing EKU\n");
- ok = 1;
- }
- }
- *valid_eku = ok;
+ pkiDebug("%s: returning retval %d, valid_eku %d\n",
+ __FUNCTION__, retval, *valid_eku);
return retval;
}
@@ -2106,10 +2200,10 @@ pkinit_create_sequence_of_principal_identifiers(
krb5_typed_data **typed_data = NULL;
switch(type) {
- case TD_TRUSTED_CERTIFIERS:
- retval = create_krb5_trustedCertifiers(context, plg_cryptoctx,
- req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);
- if (retval) {
+ case TD_TRUSTED_CERTIFIERS:
+ retval = create_krb5_trustedCertifiers(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);
+ if (retval) {
pkiDebug("create_krb5_trustedCertifiers failed\n");
goto cleanup;
}
@@ -2117,7 +2211,7 @@ pkinit_create_sequence_of_principal_identifiers(
case TD_INVALID_CERTIFICATES:
retval = create_krb5_invalidCertificates(context, plg_cryptoctx,
req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);
- if (retval) {
+ if (retval) {
pkiDebug("create_krb5_invalidCertificates failed\n");
goto cleanup;
}
@@ -2421,7 +2515,7 @@ pkinit_check_kdc_pkid(krb5_context context,
PKCS7_ISSUER_AND_SERIAL *is = NULL;
const unsigned char *p = pdid_buf;
int status = 1;
- X509 *kdc_cert = sk_X509_value(id_cryptoctx->my_certs, 0);
+ X509 *kdc_cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index);
*valid_kdcPkId = 0;
pkiDebug("found kdcPkId in AS REQ\n");
@@ -2562,9 +2656,9 @@ openssl_callback(int ok, X509_STORE_CTX * ctx)
{
#ifdef DEBUG
if (!ok) {
- char buf[256];
+ char buf[DN_BUF_LEN];
- X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, 256);
+ X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, sizeof(buf));
pkiDebug("cert = %s\n", buf);
pkiDebug("callback function: %d (%s)\n", ctx->error,
X509_verify_cert_error_string(ctx->error));
@@ -2590,10 +2684,30 @@ openssl_callback_ignore_crls(int ok, X509_STORE_CTX * ctx)
static ASN1_OBJECT *
pkinit_pkcs7type2oid(pkinit_plg_crypto_context cryptoctx, int pkcs7_type)
{
+ int nid;
+
switch (pkcs7_type) {
case CMS_SIGN_CLIENT:
return cryptoctx->id_pkinit_authData;
case CMS_SIGN_DRAFT9:
+ /*
+ * Delay creating this OID until we know we need it.
+ * It shadows an existing OpenSSL oid. If it
+ * is created too early, it breaks things like
+ * the use of pkcs12 (which uses pkcs7 structures).
+ * We need this shadow version because our code
+ * depends on the "other" type to be unknown to the
+ * OpenSSL code.
+ */
+ if (cryptoctx->id_pkinit_authData9 == NULL) {
+ pkiDebug("%s: Creating shadow instance of pkcs7-data oid\n",
+ __FUNCTION__);
+ nid = OBJ_create("1.2.840.113549.1.7.1", "id-pkcs7-data",
+ "PKCS7 data");
+ if (nid == NID_undef)
+ return NULL;
+ cryptoctx->id_pkinit_authData9 = OBJ_nid2obj(nid);
+ }
return cryptoctx->id_pkinit_authData9;
case CMS_SIGN_SERVER:
return cryptoctx->id_pkinit_DHKeyData;
@@ -2606,29 +2720,42 @@ pkinit_pkcs7type2oid(pkinit_plg_crypto_context cryptoctx, int pkcs7_type)
}
static int
-encode_signeddata(unsigned char *data, unsigned int data_len,
- unsigned char **out, unsigned int *out_len)
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server)
{
- unsigned int size = 0, r = 0;
- ASN1_OBJECT *oid;
+ unsigned int orig_len = 0, oid_len = 0, tot_len = 0;
+ ASN1_OBJECT *oid = NULL;
unsigned char *p = NULL;
- r = ASN1_object_size(1, (int)data_len, V_ASN1_SEQUENCE);
- oid = OBJ_nid2obj(NID_pkcs7_signed);
- size = i2d_ASN1_OBJECT(oid, NULL);
- size += r;
+ /* Get length to wrap the original data with SEQUENCE tag */
+ tot_len = orig_len = ASN1_object_size(1, (int)data_len, V_ASN1_SEQUENCE);
+
+ if (is_longhorn_server == 0) {
+ /* Add the signedData OID and adjust lengths */
+ oid = OBJ_nid2obj(NID_pkcs7_signed);
+ oid_len = i2d_ASN1_OBJECT(oid, NULL);
+
+ tot_len = ASN1_object_size(1, (int)(orig_len+oid_len), V_ASN1_SEQUENCE);
+ }
- r = ASN1_object_size(1, (int)size, V_ASN1_SEQUENCE);
- p = *out = (unsigned char *)malloc(r);
+ p = *out = (unsigned char *)malloc(tot_len);
if (p == NULL) return -1;
- ASN1_put_object(&p, 1, (int)size, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
- i2d_ASN1_OBJECT(oid, &p);
- ASN1_put_object(&p, 1, (int)data_len, 0, V_ASN1_CONTEXT_SPECIFIC);
+ if (is_longhorn_server == 0) {
+ ASN1_put_object(&p, 1, (int)(orig_len+oid_len),
+ V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_OBJECT(oid, &p);
+
+ ASN1_put_object(&p, 1, (int)data_len, 0, V_ASN1_CONTEXT_SPECIFIC);
+ } else {
+ ASN1_put_object(&p, 1, (int)data_len, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+ }
memcpy(p, data, data_len);
- *out_len = r;
+ *out_len = tot_len;
return 0;
}
@@ -2835,8 +2962,8 @@ pkinit_open_session(krb5_context context,
krb5_error_code
pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx,
- CK_ATTRIBUTE_TYPE usage,
- CK_OBJECT_HANDLE *objp)
+ CK_ATTRIBUTE_TYPE usage,
+ CK_OBJECT_HANDLE *objp)
{
CK_OBJECT_CLASS cls;
CK_ATTRIBUTE attrs[4];
@@ -2879,16 +3006,16 @@ pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx,
nattrs++;
if (id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session,
- attrs, nattrs) != CKR_OK) {
- pkiDebug("krb5_pkinit_sign_data: fail C_FindObjectsInit\n");
- return KRB5KDC_ERR_PREAUTH_FAILED;
+ attrs, nattrs) != CKR_OK) {
+ pkiDebug("krb5_pkinit_sign_data: fail C_FindObjectsInit\n");
+ 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 %x\n", (int) count, (int) r);
if (r != CKR_OK || count < 1)
- return KRB5KDC_ERR_PREAUTH_FAILED;
+ return KRB5KDC_ERR_PREAUTH_FAILED;
return 0;
}
#endif
@@ -2998,7 +3125,7 @@ pkinit_decode_data(krb5_context context,
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- if (!id_cryptoctx->pkcs11_method)
+ if (id_cryptoctx->pkcs11_method != 1)
retval = pkinit_decode_data_fs(context, id_cryptoctx, data, data_len,
decoded_data, decoded_data_len);
#ifndef WITHOUT_PKCS11
@@ -3098,7 +3225,7 @@ pkinit_sign_data(krb5_context context,
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- if (id_cryptoctx == NULL || !id_cryptoctx->pkcs11_method)
+ if (id_cryptoctx == NULL || id_cryptoctx->pkcs11_method != 1)
retval = pkinit_sign_data_fs(context, id_cryptoctx, data, data_len,
sig, sig_len);
#ifndef WITHOUT_PKCS11
@@ -3215,15 +3342,137 @@ pkinit_get_kdc_cert(krb5_context context,
}
static krb5_error_code
+pkinit_get_client_cert_pkcs12(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *principal,
+ krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ X509 *x = NULL;
+ PKCS12 *p12 = NULL;
+ int ret;
+ FILE *fp;
+ EVP_PKEY *y = NULL;
+
+ if (id_cryptoctx->cert_filename == NULL) {
+ pkiDebug("failed to get user's cert location\n");
+ goto cleanup;
+ }
+
+ if (id_cryptoctx->key_filename == NULL) {
+ pkiDebug("failed to get user's private key location\n");
+ goto cleanup;
+ }
+
+ fp = fopen(id_cryptoctx->cert_filename, "rb");
+ if (fp == NULL) {
+ pkiDebug("Failed to open PKCS12 file '%s', error %d\n",
+ id_cryptoctx->cert_filename, errno);
+ goto cleanup;
+ }
+
+ p12 = d2i_PKCS12_fp(fp, NULL);
+ fclose(fp);
+ if (p12 == NULL) {
+ pkiDebug("Failed to decode PKCS12 file '%s' contents\n",
+ id_cryptoctx->cert_filename);
+ goto cleanup;
+ }
+ /*
+ * Try parsing with no pass phrase first. If that fails,
+ * prompt for the pass phrase and try again.
+ */
+ ret = PKCS12_parse(p12, NULL, &y, &x, NULL);
+ if (ret == 0) {
+ krb5_data rdat;
+ krb5_prompt kprompt;
+ krb5_prompt_type prompt_type;
+ int r = 0;
+ char prompt_string[128];
+ char prompt_reply[128];
+ char prompt_prefix[] = "Pass phrase for";
+
+ pkiDebug("Initial PKCS12_parse with no password failed\n");
+
+ memset(prompt_reply, '\0', sizeof(prompt_reply));
+ rdat.data = prompt_reply;
+ rdat.length = sizeof(prompt_reply);
+
+ r = snprintf(prompt_string, sizeof(prompt_string), "%s %s",
+ prompt_prefix, id_cryptoctx->cert_filename);
+ if (r >= sizeof(prompt_string)) {
+ pkiDebug("Prompt string, '%s %s', is too long!\n",
+ prompt_prefix, id_cryptoctx->cert_filename);
+ goto cleanup;
+ }
+ kprompt.prompt = prompt_string;
+ kprompt.hidden = 1;
+ kprompt.reply = &rdat;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+
+ /* PROMPTER_INVOCATION */
+ krb5int_set_prompt_types(context, &prompt_type);
+ r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+ NULL, NULL, 1, &kprompt);
+ krb5int_set_prompt_types(context, 0);
+
+ ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL);
+ if (ret == 0) {
+ pkiDebug("Seconde PKCS12_parse with password failed\n");
+ goto cleanup;
+ }
+ }
+#if 0
+ id_cryptoctx->my_certs = sk_X509_new_null();
+ sk_X509_push(id_cryptoctx->my_certs, x);
+ id_cryptoctx->cert_index = 0;
+#else
+ creds[0] = malloc(sizeof(struct _pkinit_cred_info));
+ if (creds[0] == NULL)
+ goto cleanup;
+ creds[0]->cert = x;
+ creds[0]->cert_label = NULL;
+ creds[0]->label_len = 0;
+ creds[0]->key = y;
+ creds[1] = NULL;
+#endif
+
+ retval = 0;
+
+cleanup:
+ if (p12)
+ PKCS12_free(p12);
+ if (retval) {
+#if 0
+ if (id_cryptoctx->my_certs != NULL) {
+ sk_X509_pop_free(id_cryptoctx->my_certs, X509_free);
+ id_cryptoctx->my_certs = NULL;
+ }
+#else
+ if (x != NULL)
+ X509_free(x);
+ if (y != NULL)
+ EVP_PKEY_free(y);
+#endif
+ }
+ return retval;
+}
+
+static krb5_error_code
pkinit_get_client_cert_fs(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
const char *principal,
- krb5_get_init_creds_opt *opt)
+ krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
X509 *x = NULL;
+ EVP_PKEY *y = NULL;
if (id_cryptoctx->cert_filename == NULL) {
pkiDebug("failed to get user's cert location\n");
@@ -3242,26 +3491,53 @@ pkinit_get_client_cert_fs(krb5_context context,
goto cleanup;
}
else {
+#if 0
/* add the certificate */
id_cryptoctx->my_certs = sk_X509_new_null();
sk_X509_push(id_cryptoctx->my_certs, x);
id_cryptoctx->cert_index = 0;
+#endif
+#if 0
/* add the private key */
if ((id_cryptoctx->my_key = get_key(id_cryptoctx->key_filename)) == NULL) {
pkiDebug("failed to load user's private key from '%s'\n",
id_cryptoctx->key_filename);
goto cleanup;
}
+#else
+ /* add the private key */
+ if ((y = get_key(id_cryptoctx->key_filename)) == NULL) {
+ pkiDebug("failed to load user's private key from '%s'\n",
+ id_cryptoctx->key_filename);
+ goto cleanup;
+ }
+#endif
}
- retval = 0;
+
+ creds[0] = malloc(sizeof(struct _pkinit_cred_info));
+ if (creds[0] == NULL)
+ goto cleanup;
+ creds[0]->cert = x;
+ creds[0]->cert_label = NULL;
+ creds[0]->label_len = 0;
+ creds[0]->key = y;
+ creds[1] = NULL;
+
+retval = 0;
cleanup:
if (retval) {
+#if 0
if (id_cryptoctx->my_certs != NULL) {
sk_X509_pop_free(id_cryptoctx->my_certs, X509_free);
id_cryptoctx->my_certs = NULL;
}
+#endif
+ if (x != NULL)
+ X509_free(x);
+ if (y != NULL)
+ EVP_PKEY_free(y);
}
return retval;
}
@@ -3273,7 +3549,8 @@ pkinit_get_client_cert_pkcs11(krb5_context context,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
const char *principal,
- krb5_get_init_creds_opt *opt)
+ krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds)
{
#ifdef PKINIT_USE_MECH_LIST
CK_MECHANISM_TYPE_PTR mechp;
@@ -3374,9 +3651,13 @@ pkinit_get_client_cert_pkcs11(krb5_context context,
}
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) {
+ creds[i] = NULL;
break;
}
@@ -3416,6 +3697,7 @@ pkinit_get_client_cert_pkcs11(krb5_context context,
pkiDebug("cert %d size %d id %d idlen %d\n", i,
(int) attrs[0].ulValueLen, (int) cert_id[0],
(int) attrs[1].ulValueLen);
+#if 0
/* Just take the first one */
if (i == 0) {
id_cryptoctx->cert_id = cert_id;
@@ -3429,6 +3711,19 @@ pkinit_get_client_cert_pkcs11(krb5_context context,
id_cryptoctx->cert_index = 0;
} else
free(cert_id);
+#else
+ cp = (unsigned char *) cert;
+ x = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen);
+ if (x == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ creds[i] = malloc(sizeof(struct _pkinit_cred_info));
+ if (creds[i] == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ creds[i]->cert = x;
+ creds[i]->key = NULL;
+ creds[i]->cert_label = (char *)cert_id;
+ creds[i]->label_len = attrs[1].ulValueLen;
+#endif
free(cert);
}
id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session);
@@ -3438,7 +3733,7 @@ pkinit_get_client_cert_pkcs11(krb5_context context,
}
#endif
-krb5_error_code
+static krb5_error_code
pkinit_get_client_cert(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context req_cryptoctx,
@@ -3448,149 +3743,93 @@ pkinit_get_client_cert(krb5_context context,
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ pkinit_cred_info creds[MAX_CREDS_ALLOWED+1];
+
+ memset(creds, 0, sizeof(creds));
- if (!id_cryptoctx->pkcs11_method)
- retval = pkinit_get_client_cert_fs(context, plg_cryptoctx,
- req_cryptoctx, id_cryptoctx,
- principal, opt);
+ switch(id_cryptoctx->pkcs11_method) {
+ case 0:
+ retval = pkinit_get_client_cert_fs(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx,
+ principal, opt, creds);
+ break;
#ifndef WITHOUT_PKCS11
- else
- retval = pkinit_get_client_cert_pkcs11(context, plg_cryptoctx,
- req_cryptoctx, id_cryptoctx,
- principal, opt);
+ case 1:
+ retval = pkinit_get_client_cert_pkcs11(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx,
+ principal, opt, creds);
+ break;
#endif
- return retval;
-}
-
-krb5_error_code
-pkinit_get_trusted_cacerts(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_get_init_creds_opt *opt)
-{
- krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- char *filename = NULL, *dirname = NULL;
-
- id_cryptoctx->trustedCAs = NULL;
-
- if (get_filename(&filename, "X509_CA_TRUSTED_BUNDLE", 1) != 0) {
- pkiDebug("failed to get the name of the ca-bundle file of trusted CAs\n");
- }
-
- if (get_filename(&dirname, "X509_CA_TRUSTED_DIR", 1) != 0) {
- pkiDebug("failed to get the dir of trusted CAs\n");
- }
-
- if (filename) {
- retval = load_trusted_certifiers(&id_cryptoctx->trustedCAs, NULL, 0,
- filename);
- if (retval)
- goto cleanup;
+ case 2:
+ retval = pkinit_get_client_cert_pkcs12(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx,
+ principal, opt, creds);
+ break;
}
+ if (retval)
+ goto cleanup;
- if (dirname) {
- retval = load_trusted_certifiers_dir(&id_cryptoctx->trustedCAs,
- NULL, 0, dirname);
- if (retval)
- goto cleanup;
- }
+ /* pick the right certificate/key from the retrieved certs */
+ retval = pkinit_match_cert(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, principal, opt, creds);
+ if (retval)
+ goto cleanup;
- retval = 0;
cleanup:
- if (filename != NULL)
- free(filename);
- if (dirname != NULL)
- free(dirname);
return retval;
}
-krb5_error_code
-pkinit_get_intermediate_cacerts(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_get_init_creds_opt *opt)
+static krb5_error_code
+pkinit_match_cert(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *principal,
+ krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds)
{
- krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- char *filename = NULL, *dirname = NULL;
-
- id_cryptoctx->intermediateCAs = NULL;
-
- if (get_filename(&filename, "X509_CA_INTERM_BUNDLE", 1) != 0) {
- pkiDebug("failed to get the name of the ca-bundle file of intermediate CAs\n");
- }
- if (get_filename(&dirname, "X509_CA_INTERM_DIR", 1) != 0) {
- pkiDebug("failed to get the dir of intermediate CAs\n");
- }
-
- if (filename) {
- retval = load_trusted_certifiers(&id_cryptoctx->intermediateCAs, NULL,
- 0, filename);
- if (retval)
- goto cleanup;
- }
-
- if (dirname) {
- retval = load_trusted_certifiers_dir(&id_cryptoctx->intermediateCAs,
- NULL, 0, dirname);
- if (retval)
- goto cleanup;
- }
-
- retval = 0;
-cleanup:
- if (filename != NULL)
- free(filename);
- if (dirname != NULL)
- free(dirname);
-
- return retval;
-}
-
-krb5_error_code
-pkinit_get_crls(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_get_init_creds_opt *opt)
-{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- char *filename = NULL, *dirname = NULL;
+ int i, creds_index = 0;
- id_cryptoctx->revoked = NULL;
+ /* add code to find the right certificate */
- if (get_filename(&filename, "X509_CRL_BUNDLE", 1) != 0) {
- pkiDebug("failed to get the name of the ca-bundle file of CRLs\n");
- }
-
- if (get_filename(&dirname, "X509_CRL_DIR", 1) != 0) {
- pkiDebug("failed to get the dir of CRLs\n");
- }
+ /* copy the selected cert into our id_cryptoctx */
+ id_cryptoctx->my_certs = sk_X509_new_null();
+ sk_X509_push(id_cryptoctx->my_certs, creds[creds_index]->cert);
+ id_cryptoctx->cert_index = 0;
- if (filename) {
- retval = load_trusted_certifiers(NULL, &id_cryptoctx->revoked,
- 1, filename);
- if (retval)
- goto cleanup;
+ if (id_cryptoctx->pkcs11_method != 1) {
+ id_cryptoctx->my_key = creds[creds_index]->key;
+ }
+#ifndef WITHOUT_PKCS11
+ else {
+ id_cryptoctx->cert_id = (CK_BYTE_PTR)creds[creds_index]->cert_label;
+ id_cryptoctx->cert_id_len = creds[creds_index]->label_len;
}
+#endif
- if (dirname) {
- retval = load_trusted_certifiers_dir(NULL, &id_cryptoctx->revoked,
- 1, dirname);
- if (retval)
- goto cleanup;
+ /* free unused cred_infos */
+ for (i = 0; ; i++) {
+ if (creds[i] == NULL) break;
+ else if (i == creds_index) {
+ free (creds[i]);
+ } else {
+ if (creds[i]->cert != NULL)
+ X509_free(creds[i]->cert);
+ if (creds[i]->key != NULL)
+ EVP_PKEY_free(creds[i]->key);
+#ifndef WITHOUT_PKCS11
+ if (creds[i]->cert_label != NULL)
+ free(creds[i]->cert_label);
+#endif
+ free(creds[i]);
+ }
}
retval = 0;
cleanup:
- if (filename != NULL)
- free(filename);
- if (dirname != NULL)
- free(dirname);
-
return retval;
}
@@ -3791,7 +4030,7 @@ create_identifiers_from_stack(STACK_OF(X509) *sk,
unsigned char *p = NULL;
int len = 0;
PKCS7_ISSUER_AND_SERIAL *is = NULL;
- char buf[256];
+ char buf[DN_BUF_LEN];
*ids = NULL;
@@ -3806,7 +4045,7 @@ create_identifiers_from_stack(STACK_OF(X509) *sk,
x = sk_X509_value(sk, i);
- X509_NAME_oneline(X509_get_subject_name(x), buf, 256);
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
pkiDebug("#%d cert= %s\n", i, buf);
/* fill-in subjectName */
@@ -3826,6 +4065,7 @@ create_identifiers_from_stack(STACK_OF(X509) *sk,
krb5_cas[i]->issuerAndSerialNumber.magic = 0;
krb5_cas[i]->issuerAndSerialNumber.data = NULL;
+if (longhorn == 0) { /* XXX Longhorn doesn't like this */
is = PKCS7_ISSUER_AND_SERIAL_new();
X509_NAME_set(&is->issuer, X509_get_issuer_name(x));
M_ASN1_INTEGER_free(is->serial);
@@ -3836,12 +4076,14 @@ create_identifiers_from_stack(STACK_OF(X509) *sk,
goto cleanup;
i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
krb5_cas[i]->issuerAndSerialNumber.length = len;
+}
/* fill-in subjectKeyIdentifier */
krb5_cas[i]->subjectKeyIdentifier.length = 0;
krb5_cas[i]->subjectKeyIdentifier.magic = 0;
krb5_cas[i]->subjectKeyIdentifier.data = NULL;
+if (longhorn == 0) { /* XXX Longhorn doesn't like this */
if (X509_get_ext_by_NID(x, NID_subject_key_identifier, -1) >= 0) {
ASN1_OCTET_STRING *ikeyid = NULL;
@@ -3857,6 +4099,7 @@ create_identifiers_from_stack(STACK_OF(X509) *sk,
if (ikeyid != NULL)
ASN1_OCTET_STRING_free(ikeyid);
}
+}
if (is != NULL) {
if (is->issuer != NULL)
X509_NAME_free(is->issuer);
@@ -3878,10 +4121,10 @@ create_identifiers_from_stack(STACK_OF(X509) *sk,
static krb5_error_code
create_krb5_invalidCertificates(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_external_principal_identifier *** ids)
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_external_principal_identifier *** ids)
{
krb5_error_code retval = ENOMEM;
@@ -3937,7 +4180,7 @@ create_krb5_trustedCas(krb5_context context,
int i = 0, len = 0, sk_size = sk_X509_num(sk);
krb5_trusted_ca **krb5_cas = NULL;
X509 *x = NULL;
- char buf[256];
+ char buf[DN_BUF_LEN];
X509_NAME *xn = NULL;
unsigned char *p = NULL;
PKCS7_ISSUER_AND_SERIAL *is = NULL;
@@ -3957,7 +4200,7 @@ create_krb5_trustedCas(krb5_context context,
goto cleanup;
x = sk_X509_value(sk, i);
- X509_NAME_oneline(X509_get_subject_name(x), buf, 256);
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
pkiDebug("#%d cert= %s\n", i, buf);
switch (flag) {
@@ -4014,6 +4257,7 @@ cleanup:
#define IDTYPE_DIR 2
#define IDTYPE_PKCS11 3
#define IDTYPE_ENVVAR 4
+#define IDTYPE_PKCS12 5
#define CATYPE_ANCHORS 1
#define CATYPE_INTERMEDIATES 2
@@ -4037,25 +4281,25 @@ parse_pkcs11_options(krb5_context context,
return retval;
for ((cp = strtok(s, ":")); cp; (cp = strtok(NULL, ":"))) {
- vp = strchr(cp, '=');
+ vp = strchr(cp, '=');
- /* If there is no "=", this is a pkcs11 module name */
- if (vp == NULL) {
+ /* If there is no "=", this is a pkcs11 module name */
+ if (vp == NULL) {
if (id_cryptoctx->p11_module_name != NULL)
free(id_cryptoctx->p11_module_name);
- id_cryptoctx->p11_module_name = strdup(cp);
+ id_cryptoctx->p11_module_name = strdup(cp);
if (id_cryptoctx->p11_module_name == NULL)
goto cleanup;
- continue;
- }
- *vp++ = '\0';
- if (!strcmp(cp, "module_name")) {
+ continue;
+ }
+ *vp++ = '\0';
+ if (!strcmp(cp, "module_name")) {
if (id_cryptoctx->p11_module_name != NULL)
free(id_cryptoctx->p11_module_name);
- id_cryptoctx->p11_module_name = strdup(vp);
+ id_cryptoctx->p11_module_name = strdup(vp);
if (id_cryptoctx->p11_module_name == NULL)
goto cleanup;
- } else if (!strcmp(cp, "slotid")) {
+ } else if (!strcmp(cp, "slotid")) {
long slotid = strtol(vp, NULL, 10);
if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) {
retval = EINVAL;
@@ -4065,32 +4309,32 @@ parse_pkcs11_options(krb5_context context,
retval = EINVAL;
goto cleanup;
}
- id_cryptoctx->slotid = slotid;
- } else if (!strcmp(cp, "token")) {
- if (id_cryptoctx->token_label != NULL)
- free(id_cryptoctx->token_label);
- id_cryptoctx->token_label = strdup(vp);
+ id_cryptoctx->slotid = slotid;
+ } else if (!strcmp(cp, "token")) {
+ if (id_cryptoctx->token_label != NULL)
+ free(id_cryptoctx->token_label);
+ id_cryptoctx->token_label = strdup(vp);
if (id_cryptoctx->token_label == NULL)
goto cleanup;
- } else if (!strcmp(cp, "certid")) {
- if (id_cryptoctx->cert_id != NULL)
- free(id_cryptoctx->cert_id);
- bn = NULL;
+ } else if (!strcmp(cp, "certid")) {
+ if (id_cryptoctx->cert_id != NULL)
+ free(id_cryptoctx->cert_id);
+ bn = NULL;
/* XXX do we need BN stuff at this point? */
- BN_hex2bn(&bn, vp);
- id_cryptoctx->cert_id_len = BN_num_bytes(bn);
- id_cryptoctx->cert_id = (unsigned char *)malloc((size_t) id_cryptoctx->cert_id_len);
- if (id_cryptoctx->cert_id == NULL)
+ BN_hex2bn(&bn, vp);
+ id_cryptoctx->cert_id_len = BN_num_bytes(bn);
+ id_cryptoctx->cert_id = (unsigned char *)malloc((size_t) id_cryptoctx->cert_id_len);
+ if (id_cryptoctx->cert_id == NULL)
goto cleanup;
- BN_bn2bin(bn, id_cryptoctx->cert_id);
- BN_free(bn);
- } else if (!strcmp(cp, "certlabel")) {
- if (id_cryptoctx->cert_label != NULL)
- free(id_cryptoctx->cert_label);
- id_cryptoctx->cert_label = strdup(vp);
+ BN_bn2bin(bn, id_cryptoctx->cert_id);
+ BN_free(bn);
+ } else if (!strcmp(cp, "certlabel")) {
+ if (id_cryptoctx->cert_label != NULL)
+ free(id_cryptoctx->cert_label);
+ id_cryptoctx->cert_label = strdup(vp);
if (id_cryptoctx->cert_label == NULL)
goto cleanup;
- }
+ }
}
retval = 0;
cleanup:
@@ -4132,6 +4376,32 @@ cleanup:
return retval;
}
+static krb5_error_code
+parse_pkcs12_options(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *residual)
+{
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ id_cryptoctx->cert_filename = strdup(residual);
+ if (id_cryptoctx->cert_filename == NULL)
+ goto cleanup;
+
+ id_cryptoctx->key_filename = strdup(residual);
+ if (id_cryptoctx->key_filename == NULL)
+ goto cleanup;
+
+ pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
+ __FUNCTION__, id_cryptoctx->cert_filename,
+ id_cryptoctx->key_filename);
+ retval = 0;
+cleanup:
+ return retval;
+}
+
/*
* XXX
* This should be moved to the common code (pkinit_lib.c?)
@@ -4145,6 +4415,8 @@ process_option_identity(krb5_context context, const char *value,
int idtype;
krb5_error_code retval = 0;
+ pkiDebug("%s: processing value '%s'\n",
+ __FUNCTION__, value ? value : "NULL");
if (value == NULL)
return EINVAL;
@@ -4157,11 +4429,15 @@ process_option_identity(krb5_context context, const char *value,
idtype = IDTYPE_FILE;
} else if (strncmp(value, "PKCS11:", typelen) == 0) {
idtype = IDTYPE_PKCS11;
+ } else if (strncmp(value, "PKCS12:", typelen) == 0) {
+ idtype = IDTYPE_PKCS12;
} else if (strncmp(value, "DIR:", typelen) == 0) {
idtype = IDTYPE_DIR;
} else if (strncmp(value, "ENV:", typelen) == 0) {
idtype = IDTYPE_ENVVAR;
} else {
+ pkiDebug("%s: Unsupported type while processing '%s'\n",
+ __FUNCTION__, value);
krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
"Unsupported type while processing '%s'\n",
value);
@@ -4174,12 +4450,16 @@ process_option_identity(krb5_context context, const char *value,
switch (idtype) {
case IDTYPE_ENVVAR:
- retval = process_option_identity(context, getenv(residual),
- id_cryptoctx);
+ return process_option_identity(context, getenv(residual),
+ id_cryptoctx);
break;
case IDTYPE_FILE:
retval = parse_fs_options(context, id_cryptoctx, residual);
break;
+ case IDTYPE_PKCS12:
+ id_cryptoctx->pkcs11_method = 2;
+ retval = parse_pkcs12_options(context, id_cryptoctx, residual);
+ break;
#ifndef WITHOUT_PKCS11
case IDTYPE_PKCS11:
id_cryptoctx->pkcs11_method = 1;
@@ -4378,7 +4658,7 @@ pkinit_process_td_trusted_certifiers(
PKCS7_ISSUER_AND_SERIAL *is = NULL;
ASN1_OCTET_STRING *id = NULL;
const unsigned char *p = NULL;
- char buf[256];
+ char buf[DN_BUF_LEN];
int i = 0;
if (td_type == TD_TRUSTED_CERTIFIERS)
@@ -4394,7 +4674,7 @@ pkinit_process_td_trusted_certifiers(
(int)krb5_trusted_certifiers[i]->subjectName.length);
if (xn == NULL)
goto cleanup;
- X509_NAME_oneline(xn, buf, 256);
+ X509_NAME_oneline(xn, buf, sizeof(buf));
if (td_type == TD_TRUSTED_CERTIFIERS)
pkiDebug("#%d cert = %s is trusted by kdc\n", i, buf);
else
@@ -4408,7 +4688,7 @@ pkinit_process_td_trusted_certifiers(
(int)krb5_trusted_certifiers[i]->issuerAndSerialNumber.length);
if (is == NULL)
goto cleanup;
- X509_NAME_oneline(is->issuer, buf, 256);
+ X509_NAME_oneline(is->issuer, buf, sizeof(buf));
if (td_type == TD_TRUSTED_CERTIFIERS)
pkiDebug("#%d issuer = %s serial = %ld is trusted bu kdc\n", i,
buf, ASN1_INTEGER_get(is->serial));
@@ -4477,12 +4757,12 @@ pkcs7_dataDecode(krb5_context context,
goto cleanup;
}
-/* It was encrypted, we need to decrypt the secret key
- * with the private key */
+ /* It was encrypted, we need to decrypt the secret key
+ * with the private key */
-/* Find the recipientInfo which matches the passed certificate
- * (if any)
- */
+ /* Find the recipientInfo which matches the passed certificate
+ * (if any)
+ */
if (cert) {
for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++) {
@@ -4506,7 +4786,7 @@ pkcs7_dataDecode(krb5_context context,
}
-/* If we haven't got a certificate try each ri in turn */
+ /* If we haven't got a certificate try each ri in turn */
if (cert == NULL) {
for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++) {
@@ -4554,10 +4834,10 @@ pkcs7_dataDecode(krb5_context context,
goto cleanup;
if (jj != 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.
- */
+ /* 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)jj)) {
PKCS7err(PKCS7_F_PKCS7_DATADECODE,
PKCS7_R_DECRYPTED_KEY_IS_WRONG_LENGTH);
@@ -4624,31 +4904,6 @@ der_decode_data(unsigned char *data, long data_len,
return retval;
}
-krb5_error_code
-get_filename(char **name, char *env_name, int type)
-{
- char *ev;
-
- if ((*name = (char *) malloc(1024)) == NULL)
- return ENOMEM;
-
- if ((ev = getenv(env_name)) == NULL) {
- if (!type) {
- snprintf(*name, 1024, "/tmp/x509up_u%d", geteuid());
- pkiDebug("using %s=%s\n", env_name, *name);
- } else {
- free(*name);
- *name = NULL;
- return ENOENT;
- }
- } else {
- pkiDebug("found %s=%s\n", env_name, ev);
- snprintf(*name, 1024, "%s", ev);
- }
-
- return 0;
-}
-
static X509 *
get_cert(char *filename)
{
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
index 8c47b55..1d22d88 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
@@ -35,6 +35,7 @@
#include <openssl/dh.h>
#include <openssl/x509.h>
#include <openssl/pkcs7.h>
+#include <openssl/pkcs12.h>
#include <openssl/obj_mac.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
@@ -50,6 +51,8 @@
#include <opensc/pkcs11.h>
#endif
+#define DN_BUF_LEN 256
+
#define PKCS11_MODNAME "opensc-pkcs11.so"
#define PK_SIGLEN_GUESS 1000
#define PK_NOSLOT 999999
@@ -89,7 +92,7 @@ struct _pkinit_plg_crypto_context {
ASN1_OBJECT *id_pkinit_DHKeyData;
ASN1_OBJECT *id_pkinit_rkeyData;
ASN1_OBJECT *id_pkinit_san;
- ASN1_OBJECT *id_pkinit_san9;
+ ASN1_OBJECT *id_ms_san_upn;
ASN1_OBJECT *id_pkinit_KPClientAuth;
ASN1_OBJECT *id_pkinit_KPKdc;
ASN1_OBJECT *id_ms_kp_sc_logon;
@@ -101,6 +104,14 @@ struct _pkinit_req_crypto_context {
DH *dh;
};
+struct _pkinit_cred_info {
+ X509 *cert;
+ EVP_PKEY *key;
+ char *cert_label;
+ int label_len;
+};
+typedef struct _pkinit_cred_info * pkinit_cred_info;
+
static void openssl_init(void);
static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );
@@ -145,7 +156,6 @@ static void print_dh(DH *, char *);
static void print_pubkey(BIGNUM *, char *);
#endif
-static krb5_error_code get_filename(char **, char *, int);
static X509 *get_cert(char *filename);
static EVP_PKEY *get_key(char *filename);
@@ -190,12 +200,22 @@ CK_RV pkinit_C_Decrypt
CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen);
#endif
+
+static krb5_error_code pkinit_get_client_cert
+ (krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *principal,
+ krb5_get_init_creds_opt *opt);
+
static krb5_error_code pkinit_get_client_cert_pkcs11
(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
const char *principal,
- krb5_get_init_creds_opt *opt);
+ krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds);
static krb5_error_code pkinit_sign_data_pkcs11
(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
unsigned char *data, unsigned int data_len,
@@ -206,12 +226,29 @@ static krb5_error_code pkinit_decode_data_pkcs11
unsigned char **decoded_data, unsigned int *decoded_data_len);
#endif /* WITHOUT_PKCS11 */
+static krb5_error_code pkinit_get_client_cert_pkcs12
+ (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *principal,
+ krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds);
+
static krb5_error_code pkinit_get_client_cert_fs
(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
const char *principal,
- krb5_get_init_creds_opt *opt);
+ krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds);
+
+static krb5_error_code pkinit_match_cert
+ (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *principal, krb5_get_init_creds_opt *opt,
+ pkinit_cred_info *creds);
+
static krb5_error_code pkinit_sign_data_fs
(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
unsigned char *data, unsigned int data_len,
@@ -224,10 +261,6 @@ static krb5_error_code pkinit_decode_data_fs
static krb5_error_code der_decode_data
(unsigned char *, long, unsigned char **, long *);
-static int encode_signeddata
- (unsigned char *data, unsigned int data_len,
- unsigned char **out, unsigned int *out_len);
-
static krb5_error_code load_trusted_certifiers
(STACK_OF(X509) **trusted_CAs, STACK_OF(X509_CRL) **crls,
int return_crls, char *filename);
@@ -243,6 +276,14 @@ create_krb5_invalidCertificates(krb5_context context,
pkinit_identity_crypto_context id_cryptoctx,
krb5_external_principal_identifier *** ids);
+static krb5_error_code
+create_identifiers_from_stack(STACK_OF(X509) *sk,
+ krb5_external_principal_identifier *** ids);
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server);
+
/* This handy macro borrowed from crypto/x509v3/v3_purp.c */
#define ku_reject(x, usage) \
(((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c
index d5bffaf..76b703e 100644
--- a/src/plugins/preauth/pkinit/pkinit_lib.c
+++ b/src/plugins/preauth/pkinit/pkinit_lib.c
@@ -40,6 +40,10 @@
#define FAKECERT
+const krb5_octet_data
+ dh_oid = { 0, 7, (unsigned char *)"\x2A\x86\x48\xce\x3e\x02\x01" };
+
+
krb5_error_code pkinit_init_req_opts(pkinit_req_opts **reqopts)
{
krb5_error_code retval = ENOMEM;
@@ -51,11 +55,11 @@ krb5_error_code pkinit_init_req_opts(pkinit_req_opts **reqopts)
return retval;
opts->require_eku = 1;
+ opts->accept_secondary_eku = 0;
opts->require_san = 1;
opts->allow_upn = 0;
opts->dh_or_rsa = DH_PROTOCOL;
opts->require_crl_checking = 0;
- opts->require_hostname_match = 0;
opts->dh_size = 1024;
opts->win2k_target = 0;
opts->win2k_require_cksum = 0;
@@ -83,14 +87,13 @@ krb5_error_code pkinit_init_plg_opts(pkinit_plg_opts **plgopts)
return retval;
opts->require_eku = 1;
+ opts->accept_secondary_eku = 0;
opts->require_san = 1;
opts->dh_or_rsa = DH_PROTOCOL;
opts->allow_upn = 0;
opts->require_crl_checking = 0;
- opts->princ_in_cert = 0;
opts->dh_min_bits = 0;
- opts->allow_proxy_certs = 0;
*plgopts = opts;
@@ -226,6 +229,7 @@ pkinit_fini_identity_opts(pkinit_identity_opts *idopts)
free_list(idopts->anchors);
free_list(idopts->intermediates);
free_list(idopts->crls);
+ free_list(idopts->identity_alt);
free(idopts);
}
@@ -238,8 +242,7 @@ pkinit_initialize_identity(krb5_context context,
krb5_error_code retval = EINVAL;
int i;
- pkiDebug("pkinit_initialize_identity %p %p %p\n",
- context, idopts, id_cryptoctx);
+ pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
if (idopts == NULL || id_cryptoctx == NULL)
goto errout;
@@ -251,20 +254,19 @@ pkinit_initialize_identity(krb5_context context,
* then we will try alternatives which may have been specified
* in the config file.
*/
- if (idopts->identity != NULL)
+ if (idopts->identity != NULL) {
retval = pkinit_process_identity_option(context,
PKINIT_ID_OPT_USER_IDENTITY,
idopts->identity, id_cryptoctx);
- else {
- for (i = 0;
- retval != 0
- && idopts->identity_alt != NULL
- && idopts->identity_alt[i] != NULL; i++) {
+ } else if (idopts->identity_alt != NULL) {
+ for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++)
retval = pkinit_process_identity_option(context,
PKINIT_ID_OPT_USER_IDENTITY,
idopts->identity_alt[i],
id_cryptoctx);
- }
+ } else {
+ pkiDebug("%s: no user identity options specified\n", __FUNCTION__);
+ goto errout;
}
if (retval)
goto errout;
diff --git a/src/plugins/preauth/pkinit/pkinit_profile.c b/src/plugins/preauth/pkinit/pkinit_profile.c
index 9815d63..abdd9cc 100644
--- a/src/plugins/preauth/pkinit/pkinit_profile.c
+++ b/src/plugins/preauth/pkinit/pkinit_profile.c
@@ -200,13 +200,13 @@ pkinit_kdcdefault_integer(krb5_context context, const char *realmname,
retval = pkinit_kdcdefault_string(context, realmname, option, &string);
if (retval == 0) {
- char *endptr;
- long l;
- l = strtol(string, &endptr, 0);
- if (endptr == string)
+ char *endptr;
+ long l;
+ l = strtol(string, &endptr, 0);
+ if (endptr == string)
*ret_value = default_value;
- else
- *ret_value = l;
+ else
+ *ret_value = l;
free(string);
} else
*ret_value = default_value;
@@ -235,7 +235,7 @@ pkinit_libdefault_strings(krb5_context context, const krb5_data *realm,
char realmstr[1024];
if (realm != NULL && realm->length > sizeof(realmstr)-1)
- return EINVAL;
+ return EINVAL;
if (realm != NULL) {
strncpy(realmstr, realm->data, realm->length);
@@ -243,7 +243,7 @@ pkinit_libdefault_strings(krb5_context context, const krb5_data *realm,
}
if (!context || (context->magic != KV5M_CONTEXT))
- return KV5M_CONTEXT;
+ return KV5M_CONTEXT;
profile = context->profile;
@@ -254,9 +254,9 @@ pkinit_libdefault_strings(krb5_context context, const krb5_data *realm,
* Try number one:
*
* [libdefaults]
- * REALM = {
- * option = <value>
- * }
+ * REALM = {
+ * option = <value>
+ * }
*/
names[1] = realmstr;
@@ -270,18 +270,18 @@ pkinit_libdefault_strings(krb5_context context, const krb5_data *realm,
* Try number two:
*
* [libdefaults]
- * option = <value>
+ * option = <value>
*/
names[1] = option;
names[2] = 0;
retval = profile_get_values(profile, names, &values);
if (retval == 0 && values != NULL && values[0] != NULL)
- goto goodbye;
+ goto goodbye;
goodbye:
if (values == NULL)
- return ENOENT;
+ return ENOENT;
*ret_value = values;
@@ -324,10 +324,10 @@ pkinit_libdefault_boolean(krb5_context context, const krb5_data *realm,
retval = pkinit_libdefault_string(context, realm, option, &string);
if (retval == 0) {
- *ret_value = _krb5_conf_boolean(string);
- free(string);
+ *ret_value = _krb5_conf_boolean(string);
+ free(string);
} else
- *ret_value = default_value;
+ *ret_value = default_value;
return 0;
}
@@ -343,13 +343,13 @@ pkinit_libdefault_integer(krb5_context context, const krb5_data *realm,
retval = pkinit_libdefault_string(context, realm, option, &string);
if (retval == 0) {
- char *endptr;
- long l;
- l = strtol(string, &endptr, 0);
- if (endptr == string)
+ char *endptr;
+ long l;
+ l = strtol(string, &endptr, 0);
+ if (endptr == string)
*ret_value = default_value;
- else
- *ret_value = l;
+ else
+ *ret_value = l;
free(string);
}
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index c4a67aa..f260096 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -147,7 +147,7 @@ pkinit_server_get_edata(krb5_context context,
krb5_error_code retval = 0;
pkinit_kdc_context plgctx = NULL;
- pkiDebug("pkinit_get_edata: entered!\n");
+ pkiDebug("pkinit_server_get_edata: entered!\n");
/*
* If we don't have a realm context for the given realm,
@@ -162,6 +162,138 @@ pkinit_server_get_edata(krb5_context context,
}
static krb5_error_code
+verify_client_san(krb5_context context,
+ pkinit_kdc_context plgctx,
+ pkinit_kdc_req_context reqctx,
+ krb5_principal client,
+ int *valid_san)
+{
+ krb5_error_code retval;
+ krb5_principal *princs = NULL;
+ krb5_principal *upns = NULL;
+ int i;
+
+ retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ &princs,
+ plgctx->opts->allow_upn ? &upns : NULL,
+ NULL);
+ if (retval) {
+ pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+ 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
+
+ pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
+ for (i = 0; princs != NULL && princs[i] != NULL; i++) {
+ if (krb5_principal_compare(context, princs[i], client)) {
+ pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
+ *valid_san = 1;
+ retval = 0;
+ goto out;
+ }
+ }
+ pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
+ /*
+ * XXX if cert has names but none match, should we
+ * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
+ */
+
+ if (upns == NULL) {
+ pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+ goto out;
+ }
+
+ pkiDebug("%s: Checking upn sans\n", __FUNCTION__);
+ for (i = 0; upns[i] != NULL; i++) {
+ if (krb5_principal_compare(context, upns[i], client)) {
+ pkiDebug("%s: upn san match found\n", __FUNCTION__);
+ *valid_san = 1;
+ retval = 0;
+ goto out;
+ }
+ }
+ 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++)
+ krb5_free_principal(context, princs[i]);
+ free(princs);
+ }
+ if (upns != NULL) {
+ for (i = 0; upns[i] != NULL; i++)
+ krb5_free_principal(context, upns[i]);
+ free(upns);
+ }
+ pkiDebug("%s: returning retval %d, valid_san %d\n",
+ __FUNCTION__, retval, *valid_san);
+ return retval;
+}
+
+static krb5_error_code
+verify_client_eku(krb5_context context,
+ pkinit_kdc_context plgctx,
+ pkinit_kdc_req_context reqctx,
+ int *eku_accepted)
+{
+ krb5_error_code retval;
+
+ *eku_accepted = 0;
+
+ if (plgctx->opts->require_eku == 0) {
+ pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
+ *eku_accepted = 1;
+ retval = 0;
+ goto out;
+ }
+
+ retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ 0, /* kdc cert */
+ plgctx->opts->accept_secondary_eku,
+ eku_accepted);
+ if (retval) {
+ pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto out;
+ }
+
+out:
+ pkiDebug("%s: returning retval %d, eku_accepted %d\n",
+ __FUNCTION__, retval, *eku_accepted);
+ return retval;
+}
+
+static krb5_error_code
pkinit_server_verify_padata(krb5_context context,
struct _krb5_db_entry_new * client,
krb5_data *req_pkt,
@@ -183,7 +315,6 @@ pkinit_server_verify_padata(krb5_context context,
pkinit_kdc_context plgctx = NULL;
pkinit_kdc_req_context reqctx;
krb5_preauthtype pa_type;
- krb5_principal tmp_client;
krb5_checksum cksum = {0, 0, 0, NULL};
krb5_data *der_req = NULL;
int valid_eku = 0, valid_san = 0;
@@ -259,39 +390,25 @@ pkinit_server_verify_padata(krb5_context context,
goto cleanup;
}
- retval = verify_id_pkinit_san(context, plgctx->cryptoctx, reqctx->cryptoctx,
- plgctx->idctx, data->pa_type, plgctx->opts->allow_upn, &tmp_client,
- NULL, &valid_san);
+ retval = verify_client_san(context, plgctx, reqctx, request->client,
+ &valid_san);
if (retval)
goto cleanup;
+
if (!valid_san) {
- pkiDebug("failed to verify id-pkinit-san\n");
- retval = KRB5KDC_ERR_CLIENT_NOT_TRUSTED;
+ pkiDebug("%s: did not find an acceptable SAN in user certificate\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
goto cleanup;
- } else {
- if (tmp_client != NULL) {
- retval = krb5_principal_compare(context, request->client,
- tmp_client);
- krb5_free_principal(context, tmp_client);
- if (!retval) {
- pkiDebug("identity in the certificate does not match "
- "the requested principal\n");
- retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
- goto cleanup;
- }
- } else {
- pkiDebug("didn't find Kerberos identity in certificate\n");
- retval = KRB5KDC_ERR_CLIENT_NOT_TRUSTED;
- goto cleanup;
- }
}
- retval = verify_id_pkinit_eku(context, plgctx->cryptoctx, reqctx->cryptoctx,
- plgctx->idctx, data->pa_type, plgctx->opts->require_eku, &valid_eku);
+ retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
if (retval)
goto cleanup;
+
if (!valid_eku) {
- pkiDebug("failed to verify id-pkinit-KPClientAuth\n");
+ pkiDebug("%s: did not find an acceptable EKU in user certificate\n",
+ __FUNCTION__);
retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
goto cleanup;
}
@@ -718,11 +835,6 @@ pkinit_server_return_padata(krb5_context context,
goto cleanup;
}
- init_krb5_pa_pk_as_rep(&rep);
- if (rep == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
rep->choice = choice_pa_pk_as_rep_encKeyPack;
retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
@@ -747,11 +859,7 @@ pkinit_server_return_padata(krb5_context context,
pkiDebug("failed to encode reply_key_pack\n");
goto cleanup;
}
- init_krb5_pa_pk_as_rep_draft9(&rep9);
- if (rep9 == NULL) {
- retval = ENOMEM;
- 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,
@@ -888,6 +996,7 @@ static krb5_error_code
pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
{
krb5_error_code retval;
+ char *eku_string = NULL;
pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
retval = pkinit_kdcdefault_string(context, plgctx->realmname,
@@ -928,18 +1037,31 @@ pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
"pkinit_mappings_file",
&plgctx->idopts->dn_mapping_file);
- pkinit_kdcdefault_boolean(context, plgctx->realmname,
- "pkinit_principal_in_certificate",
- 1, &plgctx->opts->princ_in_cert);
-
- pkinit_kdcdefault_boolean(context, plgctx->realmname,
- "pkinit_allow_proxy_certificate",
- 0, &plgctx->opts->allow_proxy_certs);
-
pkinit_kdcdefault_integer(context, plgctx->realmname,
"pkinit_dh_min_bits",
0, &plgctx->opts->dh_min_bits);
+ pkinit_kdcdefault_string(context, plgctx->realmname,
+ "pkinit_eku_checking",
+ &eku_string);
+ if (eku_string != NULL) {
+ if (strcasecmp(eku_string, "kpClientAuth") == 0) {
+ plgctx->opts->require_eku = 1;
+ plgctx->opts->accept_secondary_eku = 0;
+ } else if (strcasecmp(eku_string, "scLogin") == 0) {
+ plgctx->opts->require_eku = 1;
+ plgctx->opts->accept_secondary_eku = 1;
+ } else if (strcasecmp(eku_string, "none") == 0) {
+ plgctx->opts->require_eku = 0;
+ plgctx->opts->accept_secondary_eku = 0;
+ } else {
+ pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
+ __FUNCTION__, eku_string);
+ }
+ free(eku_string);
+ }
+
+
return 0;
errout:
pkinit_fini_kdc_profile(context, plgctx);
@@ -1041,7 +1163,7 @@ pkinit_server_plugin_init(krb5_context context, void **blob,
retval = pkinit_accessor_init();
if (retval)
- return retval;
+ return retval;
/* Determine how many realms we may need to support */
for (i = 0; realmnames[i] != NULL; i++) {};
@@ -1141,8 +1263,8 @@ static void pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
- pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
- return;
+ pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
+ return;
}
pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);