diff options
author | Kevin Coffman <kwc@citi.umich.edu> | 2007-06-14 23:20:13 +0000 |
---|---|---|
committer | Kevin Coffman <kwc@citi.umich.edu> | 2007-06-14 23:20:13 +0000 |
commit | 0623629d90d218ba2d378e3f55fd1adebb33e105 (patch) | |
tree | 6b81dfd63adf407c6ea4e53f69c0c579089530c4 | |
parent | 0e661a5c3a09f10dd85537d51c9a767ac9f1137e (diff) | |
download | krb5-0623629d90d218ba2d378e3f55fd1adebb33e105.zip krb5-0623629d90d218ba2d378e3f55fd1adebb33e105.tar.gz krb5-0623629d90d218ba2d378e3f55fd1adebb33e105.tar.bz2 |
Update documentation:
kinit man-page
admin-guide: krb5.conf options
admin-guide: kdc.conf options
---
Add more functions to accessor structure.
---
Patch from Ken Raeburn, minus the README changes.
README needs major updating or should be removed.
Hopefully, it is now all captured in the doc changes.
Log:
r807@dh169: raeburn | 2007-05-30 19:23:15 -0400
dev
r810@dh169: raeburn | 2007-05-31 15:44:25 -0400
Add crypto lib check at top level, to make maintainer mode happier.
No comma at end of enum lists.
Remove extraneous ';'.
Fix uninitialized variable.
No variadic macros.
---
returning authorization data only for pa-type 16
---
return signed attributes only for pa-type 16
---
Don't segfault if we fail to decode the PKCS7 message in
cms_signeddata_verify().
---
append PIN warnings to PIN prompt
---
translate pkcs11 error codes to text
---
Change get_cert() and get_key() to return an error code so we can
give a better reason why they failed.
---
Fix more compiler warnings.
---
In crypto_cert_select_default(), enforce that there is exactly one
cert to choose from.
---
add (currently-hardcoded des3) supportedCMSTypes to pa-type 16 request
---
accept various oids in the envelopeddata for pa-type 15 request
---
fix ad_type for authorization data
---
git-svn-id: svn://anonsvn.mit.edu/krb5/users/coffman/pkinit@19577 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r-- | doc/admin.texinfo | 391 | ||||
-rw-r--r-- | doc/copyright.texinfo | 43 | ||||
-rw-r--r-- | src/clients/kinit/kinit.M | 26 | ||||
-rw-r--r-- | src/clients/kinit/kinit.c | 6 | ||||
-rw-r--r-- | src/configure.in | 5 | ||||
-rw-r--r-- | src/include/k5-int-pkinit.h | 6 | ||||
-rw-r--r-- | src/include/k5-int.h | 9 | ||||
-rw-r--r-- | src/include/krb5/krb5.hin | 1 | ||||
-rw-r--r-- | src/lib/krb5/os/accessor.c | 4 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit.h | 20 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_accessor.c | 12 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_accessor.h | 9 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_clnt.c | 48 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto.h | 12 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto_openssl.c | 510 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto_openssl.h | 6 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_identity.c | 33 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_lib.c | 14 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_matching.c | 16 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_profile.c | 23 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_srv.c | 89 |
21 files changed, 1071 insertions, 212 deletions
diff --git a/doc/admin.texinfo b/doc/admin.texinfo index fdc0b2b..6a87c64 100644 --- a/doc/admin.texinfo +++ b/doc/admin.texinfo @@ -21,7 +21,7 @@ @include definitions.texinfo @set EDITION 1.0 -@set UPDATED June 16, 2000 +@set UPDATED June 14, 2007 @finalout @c don't print black warning boxes @@ -592,6 +592,8 @@ The default value for this flag is @value{DefaultProxiable}. @end table + + @node appdefaults, login, libdefaults, krb5.conf @subsection [appdefaults] @@ -1072,7 +1074,7 @@ This LDAP specific tag indicates the list of LDAP servers that the Kerberos serv This LDAP specific tag indicates the number of connections to be maintained per LDAP server. This value is used if the number of connections per LDAP server are not mentioned in the configuration section under [dbmodules]. The default value is 5. @end table -@node dbmodules, Sample krb5.conf File, dbdefaults, krb5.conf +@node dbmodules, pkinit options, dbdefaults, krb5.conf @subsection [dbmodules] Contains database specific parameters used by the database library. Each tag in the [dbmodules] section of the file names a configuration section for database specific parameters that can be referred to by a realm. The value of the tag is a subsection where the relations in that subsection define the database specific parameters. @@ -1103,8 +1105,276 @@ This LDAP specific tags indicates the number of connections to be maintained per @end table +@node pkinit options, Sample krb5.conf File, dbmodules, krb5.conf +@subsection pkinit options + +The following are @b{pkinit-specific} options. +Note that these values may be specified in @code{[libdefaults]} +as global defaults, +or within a realm-specific subsection of @code{[libdefaults]}, +or may be specified as realm-specific values in the +@code{[realms]} section. +Also note that a realm-specific value over-rides, does not add to, +a generic @code{[libdefaults]} specification. +The search order is: +@enumerate +@item realm-specific subsection of @code{[libdefaults]} +@smallexample +@group +[libdefaults] + EXAMPLE.COM = @{ + pkinit_anchors = FILE:/usr/local/example.com.crt + @} +@end group +@end smallexample + +@item realm-specific value in the @code{[realms]} section, +@smallexample +@group +[realms] + OTHERREALM.ORG = @{ + pkinit_anchors = FILE:/usr/local/otherrealm.org.crt + @} +@end group +@end smallexample + +@item generic value in the @code{[libdefaults]} section. +@smallexample +@group +[libdefaults] + pkinit_anchors = DIR:/usr/local/generic_trusted_cas/ + +@end group +@end smallexample +@end enumerate + +@node pkinit identity syntax, pkinit krb5.conf options, pkinit options, pkinit options +@subsubsection Specifying pkinit identity information + +The syntax for specifying Public Key identity, trust, and revocation +information for pkinit is as follows: +@table @b + +@item FILE:@i{file-name}[,@i{key-file-name}] +This option has context-specific behavior. +@table @b +@item pkinit_identity +@itemx pkinit_identities +@i{file-name} specifies the name of a PEM-format file +containing the user's certificate. If @i{key-file-name} is +not specified, the uesr's private key is expected to be +in @i{file-name} as well. Otherwise, @i{key-file-name} +is the name of the file containing the private key. +@item pkinit_anchors +@itemx pkinit_pool +@i{file-name} is assumed to be the name of an OpenSSL-style +ca-bundle file. +@end table + +@item DIR:@i{directory-name} +This option has context-specific behavior. +@table @b +@item pkinit_identity +@itemx pkinit_identities +@i{directory-name} specifies a directory with files named +@code{*.crt} and @code{*.key}, where the first part of the +file name is the same for matching pairs of certificate and +private key files. When a file with a name ending with @code{.crt} +is found, a matching file ending with @code{.key} is assumed +to contain the private key. If no such file is found, then +the certificate in the @code{.crt} is not used. +@item pkinit_anchors +@itemx pkinit_pool +@i{directory-name} is assumed to be an OpenSSL-style hashed CA directory +where each CA cert is stored in a file named @i{hash-of-ca-cert}.@i{#}. +This infrastructure is encouraged, but all files in the directory +will be examined and if they contain certificates (in PEM format), +they will be used. +@item pkinit_revoke +@i{directory-name} is assumed to be an OpenSSL-style hashed CA directory +where each revocation list is stored in a file named @i{hash-of-ca-cert}.@b{r}@i{#}. +This infrastructure is encouraged, but all files in the directory +will be examined and if they contain a revocation list (in PEM format), +they will be used. +@end table + +@item PKCS12:@i{pkcs12-file-name} +@i{pkcs12-file-name} is the name of a @code{PKCS #12} format file, containing +the user's certificate and private key. + +@item PKCS11:[@b{module_name=}]@i{module-name}[@b{:slotid=}@i{slot-id}]@ +[@b{:token=}@i{token-label}][@b{:certid=}@i{cert-id}][@b{:certlabel=}@i{cert-label}] +All keyword/values are optional. +@i{module-name} specifies the location of a library implementing +@code{PKCS #11}. If a value is encountered with not keyword, it +is assumed to be the @i{module-name}. If no @i{module-name} is +specified, the default is @code{opensc-pkcs11.so}. +@b{slotid=} and/or @b{token=} may be specified to force the use of a +particular smard card reader or token if there is more than one +available. +@b{certid=} and/or @b{certlabel=} may be specified to force the selection +of a particular certificate on the device. See the @code{pkinit_cert_match} +configuration option for more ways to select a particular certificate to +use for pkinit. + +@item ENV:@i{environment-variable-name} +@i{environment-variable-name} specifies the name of an environment +variable which has been set to a value conforming to one of the +previous values. For example, @code{ENV:X509_PROXY}, where environment +variable @code{X509_PROXY} has been set to @code{FILE:/tmp/my_proxy.pem}. +@end table + +@node pkinit krb5.conf options, , pkinit identity syntax, pkinit options +@subsubsection pkinit krb5.conf options +@table @b + +@item pkinit_identities +Specifies the location(s) to be used to find the user's X.509 identity +information. This option may be specified multiple times. +Each value is attempted in order until identity information is found +and authentication is attempted. Note that these values are @b{not} +used if the user specifies @b{X509_user_identity} on the command line. + +@item pkinit_anchors +Specifies the location of trusted anchor (root) certificates which +the client trusts to sign KDC certificates. This option may be +specified multiple times. These values from the config file are +@b{not} used if the user specifies @b{X509_anchors} on the command line. + +@item pkinit_pool +Specifies the location of intermediate certificates which may be +used by the client to complete the trust chain between a KDC +certificate and a trusted anchor. This option may be specified +multiple times. + +@item pkinit_revoke +Specifies the location of Certificate Revocation List (CRL) information +to be used by the client when verifying the validity of the KDC +certificate presented. This option may be specified multiple times. + +@item pkinit_require_crl_checking +The default certificate verification process will always check +the available revocation information to see if a certificate has +been revoked. If a match is found for the certificate in a CRL, +verification fails. If the certificate being verified is not listed +in a CRL, or there is no CRL present for its issuing CA, +and @code{pkinit_require_crl_checking} is @code{false}, +then verification succeeds. + +However, if @code{pkinit_require_crl_checking} is @code{true} and +there is no CRL information available for the issuing CA, +then verification fails. + +@code{pkinit_require_crl_checking} should be set to @code{true} +if the policy is such that up-to-date CRLs @b{must} be present for +every CA. + +@item pkinit_dh_min_bits +Specifies the size of the Diffie-Hellman key the client will +attempt to use. The acceptable values are currently 1024, 2048, +and 4096. The default is 2048. + +@item pkinit_win2k +This flag specifies whether the target realm is assumed +to support only the @i{old}, pre-RFC version of the protocol. +The default is false. + +@item pkinit_win2k_require_binding +If this flag is set to true, it expects that the target +KDC is patched to return a reply with a checksum rather than a +nonce. The default is false. + +@item pkinit_eku_checking +This option specifies what Extended Key Usage value the KDC certificate +presented to the client must contain. +(@b{Note} that if the KDC certificate has the pkinit +SubjectAlternativeName encoded as the Kerberos TGS name, EKU checking +is not necessary since the issuing CA has certified this as a KDC +certificate.) +The values recognized in the @code{krb5.conf} file are: +@table @b +@item kpKDC +This is the default value and specifies that the KDC must have the +id-pkinit-KPKdc EKU as defined in RFC4556. +@item kpServerAuth +If @code{kpServerAuth} is specified, a KDC certificate with the +id-kp-serverAuth EKU as used by Microsoft will be accepted. +@item none +If @code{none} is specified, then the KDC certificate will not be +checked to verify it has an acceptable EKU. The use of this option +is @b{not recommended}. +@end table -@node Sample krb5.conf File, , dbmodules, krb5.conf +@item pkinit_kdc_hostname +The presense of this option indicates that the client is willing to +accept a KDC certificate with a dNSName SAN (Subject Alternative Name) +rather than requiring the id-pkinit-san as defined in RFC4556. This +option may be specified multiple times. Its value should contain +the acceptable hostname for the KDC (as contained in its certificate). + +@item pkinit_cert_match +Specifies matching rules that the client certificate must match before +it is used to attempt pkinit authentication. If a user has multiple +certificates available (on a smart card, or via other media), there +must be exactly one certificate chosen before attempting pkinit +authentication. This option may be specified multiple times. All the +available certificates are checked against each rule in order until +there is a match of exactly one certificate. + +The Subject and Issuer comparison strings are the RFC2253 string +representations from the certificate Subject DN and Issuer DN values. + +The syntax of the matching rules is: +@example +[@i{relation-operator}]@i{component-rule} @code{...} +@end example +where +@table @i +@item relation-operator +can be either @code{&&}, meaning all component rules must match, +or @code{||}, meaning only one component rule must match. +The default is @code{&&} if not specified. + +@item component-rule +can be one of the following. Note that there is no punctuation +or whitespace between component rules. +@table @b +@item @code{<SUBJECT>}@i{regular-expression} +@item @code{<ISSUER>}@i{regular-expression} +@item @code{<SAN>}@i{regular-expression} +@item @code{<EKU>}@i{extended-key-usage-list} +where @i{extended-key-usage-list} is a comma-separated list of +required Extended Key Usage values. All values in the list must +be present in the certificate. +@smallexample +@group +@code{pkinit} +@code{msScLogin} +@code{clientAuth} +@code{emailProtection} +@end group +@end smallexample +@item @code{<KU>}@i{key-usage-list} +where @i{key-usage-list} is a comma-separated list of required +Key Usage values. All values in the list must be present in +the certificate. +@smallexample +@group +@code{digitalSignature} +@code{keyEncipherment} +@end group +@end smallexample +@end table +@end table +Examples: +@example +pkinit_cert_match = ||<SUBJECT>.*DoE.*<SAN>.*@@EXAMPLE.COM +pkinit_cert_match = &&<EKU>msScLogin,clientAuth<ISSUER>.*DoE.* +pkinit_cert_match = <EKU>msScLogin,clientAuth<KU>digitalSignature +@end example +@end table + +@node Sample krb5.conf File, , pkinit options, krb5.conf @subsection Sample krb5.conf File Here is an example of a generic @code{krb5.conf} file: @@ -1225,7 +1495,7 @@ The default value is @value{DefaultV4Mode}. @comment these values found in krb5/src/kdc/kerberos_v4.c in v4mode_table @end table -@node realms (kdc.conf), Sample kdc.conf File, kdcdefaults, kdc.conf +@node realms (kdc.conf), pkinit options, kdcdefaults, kdc.conf @subsection [realms] Each tag in the [realms] section of the file names a Kerberos realm. @@ -1409,7 +1679,118 @@ This option defaults to @code{true}. @end table -@node Sample kdc.conf File, , realms (kdc.conf), kdc.conf +@node pkinit options, Sample kdc.conf File, realms (kdc.conf), kdc.conf +@subsection pkinit options +The following are @b{pkinit-specific} options. +Note that these values may be specified in @code{[kdcdefaults]} +as global defaults, +or within a realm-specific subsection of @code{[realms]}. +Also note that a realm-specific value over-rides, does not add to, +a generic @code{[kdcdefaults]} specification. +The search order is: +@enumerate +@item realm-specific subsection of @code{[realms]} +@smallexample +@group +[realms] + EXAMPLE.COM = @{ + pkinit_anchors = FILE:/usr/local/example.com.crt + @} +@end group +@end smallexample + +@item generic value in the @code{[kdcdefaults]} section. +@smallexample +@group +[kdcdefaults] + pkinit_anchors = DIR:/usr/local/generic_trusted_cas/ +@end group +@end smallexample +@end enumerate + +@node pkinit kdc.conf options, , pkinit options, pkinit options +@subsubsection pkinit kdc.conf options + +For information about the syntax of some of these options, +see @xref{pkinit identity syntax}. + +@table @b +@item pkinit_identity +Specifies the location of the KDC's X.509 identity information. +This option is @b{required} if pkinit is to be supported by the +KDC. + +@item pkinit_anchors +Specifies the location of trusted anchor (root) certificates +which the KDC trusts to sign client certificates. +This option is @b{required} if pkinit is to be supported by the +KDC. +This option may be specified multiple times. + +@item pkinit_pool +Specifies the location of intermediate certificates which may be +used by the KDC to complete the trust chain between a client's +certificate and a trusted anchor. +This option may be specified multiple times. + +@item pkinit_revoke +Specifies the location of Certificate Revocation List (CRL) +information to be used by the KDC when verifying the validity +of client certificates. +This option may be specified multiple times. + +@item pkinit_require_crl_checking +The default certificate verification process will always check +the available revocation information to see if a certificate has +been revoked. If a match is found for the certificate in a CRL, +verification fails. If the certificate being verified is not listed +in a CRL, or there is no CRL present for its issuing CA, +and @code{pkinit_require_crl_checking} is @code{false}, +then verification succeeds. + +However, if @code{pkinit_require_crl_checking} is @code{true} and +there is no CRL information available for the issuing CA, +then verification fails. + +@code{pkinit_require_crl_checking} should be set to @code{true} +if the policy is such that up-to-date CRLs @b{must} be present for +every CA. + +@item pkinit_dh_min_bits +Specifies the minimum number of bits the KDC is willing to accept +for a client's Diffie-Hellman key. The default is 2048. + +@item pkinit_allow_upn +Specifies that the KDC is willing to accept client certificates with +the Microsoft UserPrincipalName (UPN) Subject Alternative Name +(SAN). This means the KDC accepts the binding of the UPN in the +certificate to the Kerberos principal name. + +The default is false. + +Without this option, the KDC will only +accept certificates with the id-pkinit-san as defined in RFC4556. +There is currently no option to disable SAN checking in the KDC. + +@item pkinit_eku_checking +This option specifies what Extended Key Usage (EKU) values the +KDC is willing to accept in client certificates. +The values recognized in the @code{kdc.conf} file are: +@table @b +@item kpClientAuth +This is the default value and specifies that client certificates must +have the id-pkinit-KPClientAuth EKU as defined in RFC4556. +@item scLogin +If @code{scLogin} is specified, client certificates with the +Microsoft Smart Card Login EKU (id-ms-kp-sc-logon) will be accepted. +@item none +If @code{none} is specified, then client certificates will not be +checked to verify they have an acceptable EKU. +The use of this option is @b{not recommended}. +@end table +@end table + +@node Sample kdc.conf File, , pkinit options, kdc.conf @subsection Sample kdc.conf File Here's an example of a @code{kdc.conf} file: diff --git a/doc/copyright.texinfo b/doc/copyright.texinfo index 5d513e5..be6e12c 100644 --- a/doc/copyright.texinfo +++ b/doc/copyright.texinfo @@ -435,6 +435,49 @@ POSSIBILITY OF SUCH DAMAGE. @vskip 12pt @end iftex +Portions funded by Sandia National Laborartories and developed by +the University of Michigan, Center for Information Technology Integration, including the PKINIT implementation, are subject to the following license: + +@quotation +@iftex +@smallfonts @rm +@end iftex + +COPYRIGHT @copyright{} 2006-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. +@end quotation + +@iftex +@vskip 12pt +@hrule +@vskip 12pt +@end iftex + Permission is granted to make and distribute verbatim copies of this manual provided the copyright notices and this permission notice are preserved on all copies. diff --git a/src/clients/kinit/kinit.M b/src/clients/kinit/kinit.M index e7aa47c..eca8be3 100644 --- a/src/clients/kinit/kinit.M +++ b/src/clients/kinit/kinit.M @@ -39,7 +39,9 @@ kinit \- obtain and cache Kerberos ticket-granting ticket [\fB\-A\fP] [\fB\-v\fP] [\fB\-R\fP] [\fB\-k\fP [\fB\-t\fP \fIkeytab_file\fP]] [\fB\-c\fP \fIcache_name\fP] -[\fB\-S\fP \fIservice_name\fP] [\fIprincipal\fP] +[\fB\-S\fP \fIservice_name\fP] +[\fB\-X\fP \fIattribute\fP[=\fIvalue\fP]] +[\fIprincipal\fP] .ad b .br .SH DESCRIPTION @@ -174,6 +176,28 @@ specify an alternate service name to use when getting initial tickets. (Applicable to Kerberos 5 or if using both Kerberos 5 and Kerberos 4 with a kdc that supports Kerberos 5 to Kerberos 4 ticket conversion.) +.TP +\fB\-X\fP \fIattribute\fP[=\fIvalue\fP] +specify a pre\-authentication attribute and value to be passed to +pre\-authentication plugins. The acceptable \fIattribute\fP and +\fIvalue\fP values vary from pre\-authentication plugin to plugin. +This option may be specified multiple times to specify multiple +attributes. If no \fIvalue\fP is specified, it is assumed to be +"yes". +.sp +.nf +The following attributes are recognized by the OpenSSL pkinit +pre-authentication mechanism: +.in +.3i +\fBX509_user_identity\fP=\fIvalue\fP + specify where to find user's X509 identity information +\fBX509_anchors\fP=\fIvalue\fP + specify where to find trusted X509 anchor information +\fBflag_RSA_PROTOCOL\fP[=yes] + specify use of RSA, rather than the default Diffie-Hellman protocol +.in -.3i +.fi +.sp .SH ENVIRONMENT .B Kinit uses the following environment variables: diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c index 8037d85..1add7d9 100644 --- a/src/clients/kinit/kinit.c +++ b/src/clients/kinit/kinit.c @@ -219,9 +219,10 @@ usage(progname) USAGE_BREAK "[-v] [-R] " "[-k [-t keytab_file]] " - USAGE_BREAK "[-c cachename] " - "[-S service_name] [principal]" + USAGE_BREAK + "[-S service_name]" + "[-X <attribute>[=<value>]] [principal]" "\n\n", progname); @@ -272,6 +273,7 @@ fprintf(stderr, USAGE_OPT_FMT, indent, col1) /* This options is not yet available: */ /* ULINE("\t", "-C Kerberos 4 cache name", OPTTYPE_KRB4); */ ULINE("\t", "-S service", OPTTYPE_BOTH); + ULINE("\t", "-X <attribute>[=<value>]", OPTTYPE_KRB5); exit(2); } diff --git a/src/configure.in b/src/configure.in index 515d4a0..05b9ab9 100644 --- a/src/configure.in +++ b/src/configure.in @@ -850,6 +850,11 @@ esac changequote([, ]) AC_SUBST(PASS) +dnl for pkinit +old_LIBS="$LIBS" +AC_CHECK_LIB(crypto, PKCS7_get_signer_info) +LIBS="$old_LIBS" + dnl for lib/apputils AC_REPLACE_FUNCS(daemon) diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h index bf6073e..e75c803 100644 --- a/src/include/k5-int-pkinit.h +++ b/src/include/k5-int-pkinit.h @@ -91,7 +91,7 @@ typedef struct _krb5_trusted_ca { choice_trusted_cas_UNKNOWN = -1, choice_trusted_cas_principalName = 0, choice_trusted_cas_caName = 1, - choice_trusted_cas_issuerAndSerial = 2, + choice_trusted_cas_issuerAndSerial = 2 } choice; union { krb5_principal principalName; @@ -159,7 +159,7 @@ typedef struct _krb5_pa_pk_as_rep_draft9 { enum { choice_pa_pk_as_rep_draft9_UNKNOWN = -1, choice_pa_pk_as_rep_draft9_dhSignedData = 0, - choice_pa_pk_as_rep_draft9_encKeyPack = 1, + choice_pa_pk_as_rep_draft9_encKeyPack = 1 } choice; union { krb5_octet_data dhSignedData; @@ -172,7 +172,7 @@ typedef struct _krb5_pa_pk_as_rep { enum { choice_pa_pk_as_rep_UNKNOWN = -1, choice_pa_pk_as_rep_dhInfo = 0, - choice_pa_pk_as_rep_encKeyPack = 1, + choice_pa_pk_as_rep_encKeyPack = 1 } choice; union { krb5_dh_rep_info dh_Info; diff --git a/src/include/k5-int.h b/src/include/k5-int.h index bdeb540..4f8e0dd 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -1967,6 +1967,15 @@ typedef struct _krb5int_access { krb5_error_code (*decode_krb5_typed_data) (const krb5_data *, krb5_typed_data ***); + krb5_error_code (*decode_krb5_as_req) + (const krb5_data *output, krb5_kdc_req **rep); + krb5_error_code (*encode_krb5_kdc_req_body) + (const krb5_kdc_req *rep, krb5_data **code); + void KRB5_CALLCONV (*krb5_free_kdc_req) + (krb5_context, krb5_kdc_req * ); + void (*krb5int_set_prompt_types) + (krb5_context, krb5_prompt_type *); + } krb5int_access; #define KRB5INT_ACCESS_VERSION \ diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index 5c1fed8..0621eda 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -900,6 +900,7 @@ krb5_error_code KRB5_CALLCONV krb5_verify_checksum #define KRB5_AUTHDATA_KDC_ISSUED 4 #define KRB5_AUTHDATA_AND_OR 5 #define KRB5_AUTHDATA_MANDATORY_FOR_KDC 8 +#define KRB5_AUTHDATA_INITIAL_VERIFIED_CAS 9 #define KRB5_AUTHDATA_OSF_DCE 64 #define KRB5_AUTHDATA_SESAME 65 diff --git a/src/lib/krb5/os/accessor.c b/src/lib/krb5/os/accessor.c index 5612353..fb70cff 100644 --- a/src/lib/krb5/os/accessor.c +++ b/src/lib/krb5/os/accessor.c @@ -104,6 +104,10 @@ krb5int_accessor(krb5int_access *internals, krb5_int32 version) S (decode_krb5_typed_data, decode_krb5_typed_data), S (decode_krb5_td_trusted_certifiers, decode_krb5_td_trusted_certifiers), S (decode_krb5_td_dh_parameters, decode_krb5_td_dh_parameters), + S (decode_krb5_as_req, decode_krb5_as_req), + S (encode_krb5_kdc_req_body, encode_krb5_kdc_req_body), + S (krb5_free_kdc_req, krb5_free_kdc_req), + S (krb5int_set_prompt_types, krb5int_set_prompt_types) #if DESIGNATED_INITIALIZERS }; #else diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h index 5546de5..11bfda3 100644 --- a/src/plugins/preauth/pkinit/pkinit.h +++ b/src/plugins/preauth/pkinit/pkinit.h @@ -55,10 +55,18 @@ #define PKINIT_CTX_MAGIC 0x05551212 #define PKINIT_REQ_CTX_MAGIC 0xdeadbeef +#define PKINIT_DEFAULT_DH_MIN_BITS 2048 + +/* Make pkiDebug(fmt,...) print, or not. */ #ifdef DEBUG -#define pkiDebug(args...) printf(args) +#define pkiDebug printf #else -#define pkiDebug(args...) +/* Still evaluates for side effects. */ +static inline void pkiDebug (const char *fmt, ...) { } +/* This is better if the compiler doesn't inline variadic functions + well, but gcc will warn about "left-hand operand of comma + expression has no effect". Still evaluates for side effects. */ +/* #define pkiDebug (void) */ #endif /* Solaris compiler doesn't grok __FUNCTION__ @@ -117,7 +125,6 @@ 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) */ @@ -130,7 +137,6 @@ 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; int require_crl_checking; @@ -237,6 +243,12 @@ 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); +/* + * Functions in pkinit_identity.c + */ +char * idtype2string(int idtype); +char * catype2string(int catype); + krb5_error_code pkinit_identity_initialize (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.c b/src/plugins/preauth/pkinit/pkinit_accessor.c index 74f25e7..2c23eb9 100644 --- a/src/plugins/preauth/pkinit/pkinit_accessor.c +++ b/src/plugins/preauth/pkinit/pkinit_accessor.c @@ -64,6 +64,14 @@ krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers) krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers) (const krb5_data *, krb5_external_principal_identifier ***); +krb5_error_code (*k5int_decode_krb5_as_req) + (const krb5_data *output, krb5_kdc_req **rep); +krb5_error_code (*k5int_encode_krb5_kdc_req_body) + (const krb5_kdc_req *rep, krb5_data **code); +void KRB5_CALLCONV (*k5int_krb5_free_kdc_req) + (krb5_context, krb5_kdc_req * ); +void (*k5int_set_prompt_types) + (krb5_context, krb5_prompt_type *); /* @@ -97,5 +105,9 @@ k5int_decode_##type = k5int.decode_##type; /* special cases... */ k5int_decode_krb5_principal_name = k5int.decode_krb5_principal_name; + k5int_decode_krb5_as_req = k5int.decode_krb5_as_req; + k5int_encode_krb5_kdc_req_body = k5int.encode_krb5_kdc_req_body; + k5int_krb5_free_kdc_req = k5int.krb5_free_kdc_req; + k5int_set_prompt_types = k5int.krb5int_set_prompt_types; return 0; } diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.h b/src/plugins/preauth/pkinit/pkinit_accessor.h index 16aff0e..a72fcb0 100644 --- a/src/plugins/preauth/pkinit/pkinit_accessor.h +++ b/src/plugins/preauth/pkinit/pkinit_accessor.h @@ -69,4 +69,13 @@ extern krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers) extern krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers) (const krb5_data *, krb5_external_principal_identifier ***); +extern krb5_error_code (*k5int_decode_krb5_as_req) + (const krb5_data *output, krb5_kdc_req **rep); +extern krb5_error_code (*k5int_encode_krb5_kdc_req_body) + (const krb5_kdc_req *rep, krb5_data **code); +extern void KRB5_CALLCONV (*k5int_krb5_free_kdc_req) + (krb5_context, krb5_kdc_req * ); +extern void (*k5int_set_prompt_types) + (krb5_context, krb5_prompt_type *); + #endif /* _PKINIT_ACCESSOR_H */ diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c index 818de0c..388dc15 100644 --- a/src/plugins/preauth/pkinit/pkinit_clnt.c +++ b/src/plugins/preauth/pkinit/pkinit_clnt.c @@ -151,7 +151,7 @@ pa_pkinit_gen_req(krb5_context context, } /* checksum of the encoded KDC-REQ-BODY */ - retval = encode_krb5_kdc_req_body(request, &der_req); + retval = k5int_encode_krb5_kdc_req_body(request, &der_req); if (retval) { pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); goto cleanup; @@ -298,9 +298,19 @@ pkinit_as_req_create(krb5_context context, auth_pack->pkAuthenticator.cusec = cusec; auth_pack->pkAuthenticator.nonce = nonce; auth_pack->pkAuthenticator.paChecksum = *cksum; - auth_pack->supportedCMSTypes = NULL; auth_pack->clientDHNonce.length = 0; auth_pack->clientPublicValue = info; + + /* add List of CMS algorithms */ +#if 1 + retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, + &auth_pack->supportedCMSTypes); + if (retval) + goto cleanup; +#else + auth_pack->supportedCMSTypes = NULL; +#endif break; default: pkiDebug("as_req: unrecognized pa_type = %d\n", @@ -907,19 +917,21 @@ 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_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_allow_upn", - reqctx->opts->allow_upn, - &reqctx->opts->allow_upn); + "pkinit_require_crl_checking", + reqctx->opts->require_crl_checking, + &reqctx->opts->require_crl_checking); + pkinit_libdefault_integer(context, &request->server->realm, + "pkinit_dh_min_bits", + reqctx->opts->dh_size, + &reqctx->opts->dh_size); + if (reqctx->opts->dh_size != 1024 && reqctx->opts->dh_size != 2048 + && reqctx->opts->dh_size != 4096) { + pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, " + "using default value (%d) instead\n", __FUNCTION__, + reqctx->opts->dh_size, PKINIT_DEFAULT_DH_MIN_BITS); + reqctx->opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS; + } pkinit_libdefault_string(context, &request->server->realm, "pkinit_eku_checking", &eku_string); @@ -939,6 +951,11 @@ pkinit_client_profile(krb5_context context, } free(eku_string); } + /* Temporarily just set global flag from config file */ + pkinit_libdefault_boolean(context, &request->server->realm, + "pkinit_longhorn", + 0, + &longhorn); /* Only process anchors here if they were not specified on command line */ if (reqctx->idopts->anchors == NULL) @@ -952,7 +969,7 @@ pkinit_client_profile(krb5_context context, "pkinit_revoke", &reqctx->idopts->crls); pkinit_libdefault_strings(context, &request->server->realm, - "pkinit_identity_alt", + "pkinit_identities", &reqctx->idopts->identity_alt); } @@ -1200,7 +1217,6 @@ pkinit_client_req_init(krb5_context context, reqctx->opts->require_eku = plgctx->opts->require_eku; reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku; - reqctx->opts->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; diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h index dfb31c1..779c08c 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto.h +++ b/src/plugins/preauth/pkinit/pkinit_crypto.h @@ -363,6 +363,18 @@ krb5_error_code server_process_dh /* * this functions takes in crypto specific representation of + * supportedCMSTypes and creates a list of + * krb5_algorithm_identifier + */ +krb5_error_code create_krb5_supportedCMSTypes + (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 ***supportedCMSTypes); /* OUT */ + +/* + * this functions takes in crypto specific representation of * trustedCertifiers and creates a list of * krb5_external_principal_identifier */ diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c index b45104b..d3e066e 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c @@ -40,6 +40,98 @@ #include "pkinit_crypto_openssl.h" +static struct pkcs11_errstrings { + short code; + char *text; +} pkcs11_errstrings[] = { + { 0x0, "ok" }, + { 0x1, "cancel" }, + { 0x2, "host memory" }, + { 0x3, "slot id invalid" }, + { 0x5, "general error" }, + { 0x6, "function failed" }, + { 0x7, "arguments bad" }, + { 0x8, "no event" }, + { 0x9, "need to create threads" }, + { 0xa, "cant lock" }, + { 0x10, "attribute read only" }, + { 0x11, "attribute sensitive" }, + { 0x12, "attribute type invalid" }, + { 0x13, "attribute value invalid" }, + { 0x20, "data invalid" }, + { 0x21, "data len range" }, + { 0x30, "device error" }, + { 0x31, "device memory" }, + { 0x32, "device removed" }, + { 0x40, "encrypted data invalid" }, + { 0x41, "encrypted data len range" }, + { 0x50, "function canceled" }, + { 0x51, "function not parallel" }, + { 0x54, "function not supported" }, + { 0x60, "key handle invalid" }, + { 0x62, "key size range" }, + { 0x63, "key type inconsistent" }, + { 0x64, "key not needed" }, + { 0x65, "key changed" }, + { 0x66, "key needed" }, + { 0x67, "key indigestible" }, + { 0x68, "key function not permitted" }, + { 0x69, "key not wrappable" }, + { 0x6a, "key unextractable" }, + { 0x70, "mechanism invalid" }, + { 0x71, "mechanism param invalid" }, + { 0x82, "object handle invalid" }, + { 0x90, "operation active" }, + { 0x91, "operation not initialized" }, + { 0xa0, "pin incorrect" }, + { 0xa1, "pin invalid" }, + { 0xa2, "pin len range" }, + { 0xa3, "pin expired" }, + { 0xa4, "pin locked" }, + { 0xb0, "session closed" }, + { 0xb1, "session count" }, + { 0xb3, "session handle invalid" }, + { 0xb4, "session parallel not supported" }, + { 0xb5, "session read only" }, + { 0xb6, "session exists" }, + { 0xb7, "session read only exists" }, + { 0xb8, "session read write so exists" }, + { 0xc0, "signature invalid" }, + { 0xc1, "signature len range" }, + { 0xd0, "template incomplete" }, + { 0xd1, "template inconsistent" }, + { 0xe0, "token not present" }, + { 0xe1, "token not recognized" }, + { 0xe2, "token write protected" }, + { 0xf0, "unwrapping key handle invalid" }, + { 0xf1, "unwrapping key size range" }, + { 0xf2, "unwrapping key type inconsistent" }, + { 0x100, "user already logged in" }, + { 0x101, "user not logged in" }, + { 0x102, "user pin not initialized" }, + { 0x103, "user type invalid" }, + { 0x104, "user another already logged in" }, + { 0x105, "user too many types" }, + { 0x110, "wrapped key invalid" }, + { 0x112, "wrapped key len range" }, + { 0x113, "wrapping key handle invalid" }, + { 0x114, "wrapping key size range" }, + { 0x115, "wrapping key type inconsistent" }, + { 0x120, "random seed not supported" }, + { 0x121, "random no rng" }, + { 0x130, "domain params invalid" }, + { 0x150, "buffer too small" }, + { 0x160, "saved state invalid" }, + { 0x170, "information sensitive" }, + { 0x180, "state unsaveable" }, + { 0x190, "cryptoki not initialized" }, + { 0x191, "cryptoki already initialized" }, + { 0x1a0, "mutex bad" }, + { 0x1a1, "mutex not locked" }, + { 0x200, "function rejected" }, + { -1, NULL } +}; + /* DH parameters */ unsigned char pkinit_1024_dhprime[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -357,6 +449,77 @@ out: return retval; } +static krb5_error_code +get_cert(char *filename, X509 **retcert) +{ + X509 *cert = NULL; + BIO *tmp = NULL; + int code; + krb5_error_code retval; + + if (filename == NULL || retcert == NULL) + return EINVAL; + + *retcert = NULL; + + tmp = BIO_new(BIO_s_file()); + if (tmp == NULL) + return ENOMEM; + + code = BIO_read_filename(tmp, filename); + if (code == 0) { + retval = errno; + goto cleanup; + } + + cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL); + if (cert == NULL) { + retval = EIO; + pkiDebug("failed to read certificate from %s\n", filename); + goto cleanup; + } + *retcert = cert; + retval = 0; +cleanup: + if (tmp != NULL) + BIO_free(tmp); + return retval; +} + +static krb5_error_code +get_key(char *filename, EVP_PKEY **retkey) +{ + EVP_PKEY *pkey = NULL; + BIO *tmp = NULL; + int code; + krb5_error_code retval; + + if (filename == NULL || retkey == NULL) + return EINVAL; + + tmp = BIO_new(BIO_s_file()); + if (tmp == NULL) + return ENOMEM; + + code = BIO_read_filename(tmp, filename); + if (code == 0) { + retval = errno; + goto cleanup; + } + pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL); + if (pkey == NULL) { + retval = EIO; + pkiDebug("failed to read private key from %s\n", filename); + goto cleanup; + } + *retkey = pkey; + retval = 0; +cleanup: + if (tmp != NULL) + BIO_free(tmp); + return retval; +} + static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ctx) { @@ -553,7 +716,7 @@ cms_signeddata_create(krb5_context context, STACK_OF(X509) * cert_stack = NULL; ASN1_OCTET_STRING *digest_attr = NULL; EVP_MD_CTX ctx, ctx2; - const EVP_MD *md_tmp; + const EVP_MD *md_tmp = NULL; unsigned char md_data[EVP_MAX_MD_SIZE], md_data2[EVP_MAX_MD_SIZE]; unsigned char *digestInfo_buf = NULL, *abuf = NULL; unsigned int md_len, md_len2, alen, digestInfo_len; @@ -657,35 +820,42 @@ cms_signeddata_create(krb5_context context, goto cleanup; p7si->digest_enc_alg->parameter->type = V_ASN1_NULL; - /* add signed attributes */ - /* compute sha1 digest over the EncapsulatedContentInfo */ - EVP_MD_CTX_init(&ctx); - EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL); - EVP_DigestUpdate(&ctx, data, data_len); - md_tmp = EVP_MD_CTX_md(&ctx); - EVP_DigestFinal_ex(&ctx, md_data, &md_len); - - /* create a message digest attr */ - digest_attr = ASN1_OCTET_STRING_new(); - ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len); - PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest, - V_ASN1_OCTET_STRING, (char *) digest_attr); - /* pick the correct oid for the eContentInfo */ oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type); if (oid == NULL) goto cleanup; - /* create a content-type attr */ - PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, V_ASN1_OBJECT, oid); - - /* create the signature over signed attributes. get DER encoded value */ - /* This is the place where smartcard signature needs to be calculated */ - sk = p7si->auth_attr; - alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, - ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); - if (abuf == NULL) - goto cleanup2; + if (cms_msg_type == CMS_SIGN_DRAFT9) { + /* don't include signed attributes for pa-type 15 request */ + abuf = data; + alen = data_len; + } else { + /* add signed attributes */ + /* compute sha1 digest over the EncapsulatedContentInfo */ + EVP_MD_CTX_init(&ctx); + EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL); + EVP_DigestUpdate(&ctx, data, data_len); + md_tmp = EVP_MD_CTX_md(&ctx); + EVP_DigestFinal_ex(&ctx, md_data, &md_len); + + /* create a message digest attr */ + digest_attr = ASN1_OCTET_STRING_new(); + ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len); + PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest, + V_ASN1_OCTET_STRING, (char *) digest_attr); + + /* create a content-type attr */ + PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, + V_ASN1_OBJECT, oid); + + /* create the signature over signed attributes. get DER encoded value */ + /* This is the place where smartcard signature needs to be calculated */ + sk = p7si->auth_attr; + alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, + ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); + if (abuf == NULL) + goto cleanup2; + } #ifndef WITHOUT_PKCS11 /* Some tokens can only do RSAEncryption without sha1 hash */ @@ -695,10 +865,15 @@ cms_signeddata_create(krb5_context context, * digestAlgorithm AlgorithmIdentifier, * digest OCTET STRING } */ - if (id_cryptoctx->pkcs11_method == 1 && 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); + /* if this is not draft9 request, include digest signed attribute */ + if (cms_msg_type != CMS_SIGN_DRAFT9) + EVP_DigestInit_ex(&ctx2, md_tmp, NULL); + else + EVP_DigestInit_ex(&ctx2, EVP_sha1(), NULL); EVP_DigestUpdate(&ctx2, abuf, alen); EVP_DigestFinal_ex(&ctx2, md_data2, &md_len2); @@ -748,7 +923,8 @@ cms_signeddata_create(krb5_context context, #ifdef DEBUG_SIG print_buffer(sig, sig_len); #endif - free(abuf); + if (cms_msg_type != CMS_SIGN_DRAFT9) + free(abuf); if (retval) goto cleanup2; @@ -817,25 +993,36 @@ cms_signeddata_create(krb5_context context, retval = 0; #ifdef DEBUG_ASN1 - print_buffer_bin(*signed_data, *signed_data_len, "/tmp/pkcs7_signeddata"); + if (cms_msg_type == CMS_SIGN_CLIENT) { + print_buffer_bin(*signed_data, *signed_data_len, "/tmp/client_pkcs7_signeddata"); + } else { + if (cms_msg_type == CMS_SIGN_SERVER) { + print_buffer_bin(*signed_data, *signed_data_len, "/tmp/kdc_pkcs7_signeddata"); + } else { + print_buffer_bin(*signed_data, *signed_data_len, "/tmp/draft9_pkcs7_signeddata"); + } + } #endif cleanup2: - EVP_MD_CTX_cleanup(&ctx); + if (cms_msg_type != CMS_SIGN_DRAFT9) + EVP_MD_CTX_cleanup(&ctx); #ifndef WITHOUT_PKCS11 - if (id_cryptoctx->pkcs11_method == 1 && id_cryptoctx->mech == CKM_RSA_PKCS) + if (id_cryptoctx->pkcs11_method == 1 && + id_cryptoctx->mech == CKM_RSA_PKCS) { EVP_MD_CTX_cleanup(&ctx2); + if (digest_buf != NULL) + free(digest_buf); + if (digestInfo_buf != NULL) + free(digestInfo_buf); + if (alg_buf != NULL) + free(alg_buf); + if (digest != NULL) + ASN1_OCTET_STRING_free(digest); + } #endif if (alg != NULL) X509_ALGOR_free(alg); - if (digest != NULL) - ASN1_OCTET_STRING_free(digest); - if (alg_buf != NULL) - free(alg_buf); - if (digest_buf != NULL) - free(digest_buf); - if (digestInfo_buf != NULL) - free(digestInfo_buf); cleanup: if (p7 != NULL) PKCS7_free(p7); @@ -879,7 +1066,7 @@ cms_signeddata_verify(krb5_context context, char buf[DN_BUF_LEN]; #ifdef DEBUG_ASN1 - print_buffer_bin(signed_data, signed_data_len, "/tmp/pkcs7_signeddata"); + print_buffer_bin(signed_data, signed_data_len, "/tmp/client_received_pkcs7_signeddata"); #endif /* Do this early enough to create the shadow OID for pkcs7-data if needed */ @@ -887,17 +1074,19 @@ cms_signeddata_verify(krb5_context context, if (oid == NULL) goto cleanup; - /* decode received PKCS7 messag */ + /* decode received PKCS7 message */ if ((p7 = d2i_PKCS7(NULL, &p, (int)signed_data_len)) == NULL) { unsigned long err = ERR_peek_error(); krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); + pkiDebug("%s: failed to decode message: %s\n", + __FUNCTION__, ERR_error_string(err, NULL)); goto cleanup; } /* verify that the received message is PKCS7 SignedData message */ if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) { - pkiDebug("Excepted id-signedData PKCS7 mgs (received type = %d)\n", + pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n", OBJ_obj2nid(p7->type)); krb5_set_error_message(context, retval, "wrong oid\n"); goto cleanup; @@ -1049,8 +1238,24 @@ cms_signeddata_verify(krb5_context context, goto cleanup; out = BIO_new(BIO_s_mem()); + if (cms_msg_type == CMS_SIGN_DRAFT9) + flags |= PKCS7_NOATTR; if (PKCS7_verify(p7, NULL, store, NULL, out, flags)) { - if (!OBJ_cmp(p7->d.sign->contents->type, oid)) + int valid_oid = 0; + + if (!OBJ_cmp(p7->d.sign->contents->type, oid)) + valid_oid = 1; + else if (cms_msg_type == CMS_SIGN_DRAFT9) { + /* check to see if longhorn is sending RFC oids for pa-type 15 */ + ASN1_OBJECT *client_oid = NULL, *server_oid = NULL; + client_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_CLIENT); + server_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_SERVER); + if (!OBJ_cmp(p7->d.sign->contents->type, client_oid) || + !OBJ_cmp(p7->d.sign->contents->type, server_oid)) + valid_oid = 1; + } + + if (valid_oid) pkiDebug("PKCS7 Verification successful\n"); else { pkiDebug("wrong oid in eContentType\n"); @@ -1130,12 +1335,13 @@ cms_signeddata_verify(krb5_context context, BIO_free(out); if (store != NULL) X509_STORE_free(store); - if (idctx->intermediateCAs != NULL && p7->d.sign->cert) - sk_X509_free(intermediateCAs); - if (idctx->revoked != NULL && p7->d.sign->crl) - sk_X509_CRL_free(revoked); - if (p7 != NULL) + if (p7 != NULL) { + if (idctx->intermediateCAs != NULL && p7->d.sign->cert) + sk_X509_free(intermediateCAs); + if (idctx->revoked != NULL && p7->d.sign->crl) + sk_X509_CRL_free(revoked); PKCS7_free(p7); + } if (verified_chain != NULL) sk_X509_pop_free(verified_chain, X509_free); if (krb5_verified_chain != NULL) @@ -1228,7 +1434,20 @@ cms_envelopeddata_create(krb5_context context, retval = -1; goto cleanup; } - p7->d.enveloped->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_signed); + switch (pa_type) { + case KRB5_PADATA_PK_AS_REQ: + p7->d.enveloped->enc_data->content_type = + OBJ_nid2obj(NID_pkcs7_signed); + break; + case KRB5_PADATA_PK_AS_REP_OLD: + case KRB5_PADATA_PK_AS_REQ_OLD: + p7->d.enveloped->enc_data->content_type = + OBJ_nid2obj(NID_pkcs7_data); + break; + break; + break; +break; + } *out_len = i2d_PKCS7(p7, NULL); if (!*out_len || (p = *out = (unsigned char *)malloc(*out_len)) == NULL) { @@ -1283,6 +1502,9 @@ cms_envelopeddata_verify(krb5_context context, unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL, *vfy_buf = NULL; int msg_type = 0; +#ifdef DEBUG_ASN1 + print_buffer_bin(enveloped_data, enveloped_data_len, "/tmp/client_envelopeddata"); +#endif /* decode received PKCS7 message */ if ((p7 = d2i_PKCS7(NULL, &p, (int)enveloped_data_len)) == NULL) { unsigned long err = ERR_peek_error(); @@ -1294,7 +1516,7 @@ cms_envelopeddata_verify(krb5_context context, /* verify that the received message is PKCS7 EnvelopedData message */ if (OBJ_obj2nid(p7->type) != NID_pkcs7_enveloped) { - pkiDebug("Excepted id-enveloped PKCS7 msg (received type = %d)\n", + pkiDebug("Expected id-enveloped PKCS7 msg (received type = %d)\n", OBJ_obj2nid(p7->type)); krb5_set_error_message(context, retval, "wrong oid\n"); goto cleanup; @@ -2840,9 +3062,15 @@ pkinit_login(krb5_context context, rdat.data = NULL; rdat.length = 0; } else { - if ((prompt = (char *) malloc(sizeof (tip->label) + 8)) == NULL) + if ((prompt = (char *) malloc(sizeof (tip->label) + 32)) == NULL) return ENOMEM; sprintf(prompt, "%.*s PIN", sizeof (tip->label), tip->label); + if (tip->flags & CKF_USER_PIN_LOCKED) + strcat(prompt, " (Warning: PIN locked)"); + else if (tip->flags & CKF_USER_PIN_FINAL_TRY) + strcat(prompt, " (Warning: PIN final try)"); + else if (tip->flags & CKF_USER_PIN_COUNT_LOW) + strcat(prompt, " (Warning: PIN count low)"); rdat.data = (char *)malloc(tip->ulMaxPinLen + 2); rdat.length = tip->ulMaxPinLen + 1; @@ -2852,10 +3080,10 @@ pkinit_login(krb5_context context, prompt_type = KRB5_PROMPT_TYPE_PREAUTH; /* PROMPTER_INVOCATION */ - krb5int_set_prompt_types(context, &prompt_type); + k5int_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); + k5int_set_prompt_types(context, 0); free(prompt); } @@ -2864,7 +3092,7 @@ pkinit_login(krb5_context context, (u_char *) rdat.data, rdat.length); if (r != CKR_OK) { - pkiDebug("fail C_Login %x\n", r); + pkiDebug("C_Login: %s\n", pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; } } @@ -2895,7 +3123,7 @@ pkinit_open_session(krb5_context context, /* Init */ if ((r = cctx->p11->C_Initialize(NULL)) != CKR_OK) { - pkiDebug("fail C_Initialize %x\n", r); + pkiDebug("C_Initialize: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } @@ -2920,13 +3148,13 @@ pkinit_open_session(krb5_context context, /* Open session */ if ((r = cctx->p11->C_OpenSession(slotlist[i], CKF_SERIAL_SESSION, NULL, NULL, &cctx->session)) != CKR_OK) { - pkiDebug("fail C_OpenSession %x\n", r); + pkiDebug("C_OpenSession: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } /* Get token info */ if ((r = cctx->p11->C_GetTokenInfo(slotlist[i], &tinfo)) != CKR_OK) { - pkiDebug("fail C_GetTokenInfo %x\n", r); + pkiDebug("C_GetTokenInfo: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } for (cp = tinfo.label + sizeof (tinfo.label) - 1; @@ -2980,9 +3208,10 @@ pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx, { CK_OBJECT_CLASS cls; CK_ATTRIBUTE attrs[4]; - CK_ULONG r, count; + CK_ULONG count; CK_KEY_TYPE keytype; unsigned int nattrs = 0; + int r; #ifdef PKINIT_USE_KEY_USAGE CK_BBOOL true_false; #endif @@ -3018,15 +3247,16 @@ pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx, attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len; nattrs++; - if (id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, - attrs, nattrs) != CKR_OK) { - pkiDebug("krb5_pkinit_sign_data: fail C_FindObjectsInit\n"); + r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs); + if (r != CKR_OK) { + pkiDebug("krb5_pkinit_sign_data: C_FindObjectsInit: %s\n", + pkinit_pkcs11_code_to_text(r)); 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); + pkiDebug("found %d private keys (%s)\n", (int) count, pkinit_pkcs11_code_to_text(r)); if (r != CKR_OK || count < 1) return KRB5KDC_ERR_PREAUTH_FAILED; return 0; @@ -3097,7 +3327,7 @@ pkinit_decode_data_pkcs11(krb5_context context, if ((r = id_cryptoctx->p11->C_DecryptInit(id_cryptoctx->session, &mech, obj)) != CKR_OK) { - pkiDebug("fail C_DecryptInit %x\n", (int) r); + pkiDebug("C_DecryptInit: 0x%x\n", (int) r); return KRB5KDC_ERR_PREAUTH_FAILED; } pkiDebug("data_len = %d\n", data_len); @@ -3115,7 +3345,7 @@ pkinit_decode_data_pkcs11(krb5_context context, if ((r = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, data, (CK_ULONG) data_len, cp, &len)) != CKR_OK) { #endif - pkiDebug("fail C_Decrypt %x\n", (int) r); + pkiDebug("C_Decrypt: %s\n", pkinit_pkcs11_code_to_text(r)); if (r == CKR_BUFFER_TOO_SMALL) pkiDebug("decrypt %d needs %d\n", (int) data_len, (int) len); return KRB5KDC_ERR_PREAUTH_FAILED; @@ -3194,7 +3424,7 @@ pkinit_sign_data_pkcs11(krb5_context context, if ((r = id_cryptoctx->p11->C_SignInit(id_cryptoctx->session, &mech, obj)) != CKR_OK) { - pkiDebug("fail C_SignInit %x\n", (int) r); + pkiDebug("C_SignInit: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } @@ -3217,7 +3447,7 @@ pkinit_sign_data_pkcs11(krb5_context context, (CK_ULONG) data_len, cp, &len); } if (r != CKR_OK) { - pkiDebug("fail C_Sign %x\n", (int) r); + pkiDebug("C_Sign: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } pkiDebug("sign %d -> %d\n", (int) data_len, (int) len); @@ -3310,28 +3540,6 @@ create_signature(unsigned char **sig, unsigned int *sig_len, return retval; } -static EVP_PKEY * -get_key(char *filename) -{ - BIO *tmp = NULL; - EVP_PKEY *pkey = NULL; - - if (filename == NULL) - return NULL; - - if ((tmp = BIO_new(BIO_s_file())) - && (BIO_read_filename(tmp, filename) > 0)) - pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL); - if (pkey == NULL) { - pkiDebug("failed to get private key from %s\n", filename); - return NULL; - } - if (tmp != NULL) - BIO_free(tmp); - - return pkey; -} - /* * Note: * This is not the routine the KDC uses to get its certificate. @@ -3425,10 +3633,10 @@ pkinit_get_certs_pkcs12(krb5_context context, prompt_type = KRB5_PROMPT_TYPE_PREAUTH; /* PROMPTER_INVOCATION */ - krb5int_set_prompt_types(context, &prompt_type); + k5int_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); + k5int_set_prompt_types(context, 0); ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL); if (ret == 0) { @@ -3468,25 +3676,27 @@ pkinit_load_fs_cert_and_key(krb5_context context, char *keyname, int cindex) { - krb5_error_code retval = ENOMEM; + krb5_error_code retval; X509 *x = NULL; EVP_PKEY *y = NULL; /* load the certificate */ - x = get_cert(certname); - if (x == NULL) { + retval = get_cert(certname, &x); + if (retval != 0 || x == NULL) { pkiDebug("failed to load user's certificate from '%s'\n", certname); goto cleanup; } - y = get_key(keyname); - if (y == NULL) { + retval = get_key(keyname, &y); + if (retval != 0 || y == NULL) { pkiDebug("failed to load user's private key from '%s'\n", keyname); goto cleanup; } id_cryptoctx->creds[cindex] = malloc(sizeof(struct _pkinit_cred_info)); - if (id_cryptoctx->creds[cindex] == NULL) + if (id_cryptoctx->creds[cindex] == NULL) { + retval = ENOMEM; goto cleanup; + } id_cryptoctx->creds[cindex]->cert = x; #ifndef WITHOUT_PKCS11 id_cryptoctx->creds[cindex]->cert_id = NULL; @@ -3698,7 +3908,7 @@ pkinit_get_certs_pkcs11(krb5_context context, #else if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid, NULL, &count)) != CKR_OK || count <= 0) { - pkiDebug("can't find any mechanisms %x\n", r); + pkiDebug("C_GetMechanismList: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } mechp = (CK_MECHANISM_TYPE_PTR) malloc(count * sizeof (CK_MECHANISM_TYPE)); @@ -3753,9 +3963,9 @@ pkinit_get_certs_pkcs11(krb5_context context, nattrs++; } - if (id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, - attrs, nattrs) != CKR_OK) { - pkiDebug("fail C_FindObjectsInit\n"); + r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs); + if (r != CKR_OK) { + pkiDebug("C_FindObjectsInit: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } @@ -3781,7 +3991,7 @@ pkinit_get_certs_pkcs11(krb5_context context, if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session, obj, attrs, 2)) != CKR_OK && r != CKR_BUFFER_TOO_SMALL) { - pkiDebug("fail C_GetAttributeValue len %x\n", r); + pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } cert = (CK_BYTE_PTR) malloc((size_t) attrs[0].ulValueLen + 1); @@ -3799,7 +4009,7 @@ pkinit_get_certs_pkcs11(krb5_context context, if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session, obj, attrs, 2)) != CKR_OK) { - pkiDebug("fail C_GetAttributeValue %x\n", r); + pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } @@ -4276,8 +4486,7 @@ crypto_cert_select(krb5_context context, /* copy the selected cert into our id_cryptoctx */ if (cd->idctx->my_certs != NULL) { - /* XXX free existing cert stack! */ - return 9999; + sk_X509_pop_free(cd->idctx->my_certs, X509_free); } cd->idctx->my_certs = sk_X509_new_null(); sk_X509_push(cd->idctx->my_certs, cd->cred->cert); @@ -4307,10 +4516,26 @@ crypto_cert_select_default(krb5_context context, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx) { + krb5_error_code retval; + int cert_count = 0; + + retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx, + id_cryptoctx, &cert_count); + if (retval) { + pkiDebug("%s: crypto_cert_get_count error %d, %s\n", + __FUNCTION__, retval, error_message(retval)); + goto errout; + } + if (cert_count != 1) { + pkiDebug("%s: ERROR: There are %d certs to choose from, " + "but there must be exactly one.\n", + __FUNCTION__, cert_count); + retval = EINVAL; + goto errout; + } /* copy the selected cert into our id_cryptoctx */ if (id_cryptoctx->my_certs != NULL) { - /* XXX free existing cert stack! */ - return 9999; + sk_X509_pop_free(id_cryptoctx->my_certs, X509_free); } id_cryptoctx->my_certs = sk_X509_new_null(); sk_X509_push(id_cryptoctx->my_certs, id_cryptoctx->creds[0]->cert); @@ -4328,7 +4553,9 @@ crypto_cert_select_default(krb5_context context, id_cryptoctx->cert_id_len = id_cryptoctx->creds[0]->cert_id_len; } #endif - return 0; + retval = 0; +errout: + return retval; } @@ -4552,8 +4779,8 @@ crypto_load_cas_and_crls(krb5_context context, int catype, char *id) { - pkiDebug("%s: called with idtype %d and catype %d\n", - __FUNCTION__, idtype, catype); + pkiDebug("%s: called with idtype %s and catype %s\n", + __FUNCTION__, idtype2string(idtype), catype2string(catype)); switch (idtype) { case IDTYPE_FILE: return load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx, @@ -4699,6 +4926,37 @@ cleanup: } krb5_error_code +create_krb5_supportedCMSTypes(krb5_context context, + pkinit_plg_crypto_context plg_cryptoctx, + pkinit_req_crypto_context req_cryptoctx, + pkinit_identity_crypto_context id_cryptoctx, + krb5_algorithm_identifier ***oids) +{ + + krb5_error_code retval = ENOMEM; + krb5_algorithm_identifier **loids = NULL; + krb5_octet_data des3oid = {0, 8, (unsigned char *)"\x2A\x86\x48\x86\xF7\x0D\x03\x07" }; + + *oids = NULL; + loids = malloc(2 * sizeof(krb5_algorithm_identifier *)); + if (loids == NULL) + goto cleanup; + loids[1] = NULL; + loids[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); + if (loids[0] == NULL) + goto cleanup; + loids[0]->algorithm = des3oid; + loids[0]->parameters.length = 0; + loids[0]->parameters.data = NULL; + + *oids = loids; + retval = 0; +cleanup: + + return retval; +} + +krb5_error_code create_krb5_trustedCertifiers(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, @@ -4727,7 +4985,7 @@ create_krb5_trustedCas(krb5_context context, krb5_trusted_ca *** ids) { krb5_error_code retval = ENOMEM; - STACK_OF(X509) *sk = id_cryptoctx->trustedCAs;; + STACK_OF(X509) *sk = id_cryptoctx->trustedCAs; int i = 0, len = 0, sk_size = sk_X509_num(sk); krb5_trusted_ca **krb5_cas = NULL; X509 *x = NULL; @@ -5136,23 +5394,6 @@ der_decode_data(unsigned char *data, long data_len, return retval; } -static X509 * -get_cert(char *filename) -{ - X509 *cert = NULL; - BIO *tmp = NULL; - - if (filename == NULL) - return NULL; - - if ((tmp = BIO_new(BIO_s_file())) - && (BIO_read_filename(tmp, filename) > 0)) - cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL); - if (tmp != NULL) - BIO_free(tmp); - - return cert; -} #ifdef DEBUG_DH static void @@ -5192,3 +5433,18 @@ print_pubkey(BIGNUM * key, char *msg) } #endif + +static char * +pkinit_pkcs11_code_to_text(int err) +{ + int i; + static char uc[32]; + + for (i = 0; pkcs11_errstrings[i].text != NULL; i++) + if (pkcs11_errstrings[i].code == err) + break; + if (pkcs11_errstrings[i].text != NULL) + return (pkcs11_errstrings[i].text); + sprintf(uc, "unknown code 0x%x", err); + return (uc); +} diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h index 9976bf3..7bb2dbc 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h +++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h @@ -170,9 +170,6 @@ static void print_dh(DH *, char *); static void print_pubkey(BIGNUM *, char *); #endif -static X509 *get_cert(char *filename); -static EVP_PKEY *get_key(char *filename); - static int prepare_enc_data (unsigned char *indata, int indata_len, unsigned char **outdata, int *outdata_len); @@ -256,4 +253,7 @@ wrap_signeddata(unsigned char *data, unsigned int data_len, #define ku_reject(x, usage) \ (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage))) +static char * +pkinit_pkcs11_code_to_text(int err); + #endif /* _PKINIT_CRYPTO_OPENSSL_H */ diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c index 267e858..227c55d 100644 --- a/src/plugins/preauth/pkinit/pkinit_identity.c +++ b/src/plugins/preauth/pkinit/pkinit_identity.c @@ -83,6 +83,30 @@ cleanup: return ENOMEM; } +char * +idtype2string(int idtype) +{ + switch(idtype) { + case IDTYPE_FILE: return "FILE"; break; + case IDTYPE_DIR: return "DIR"; break; + case IDTYPE_PKCS11: return "PKCS11"; break; + case IDTYPE_PKCS12: return "PKCS12"; break; + case IDTYPE_ENVVAR: return "ENV"; break; + default: return "INVALID"; break; + } +} + +char * +catype2string(int catype) +{ + switch(catype) { + case CATYPE_ANCHORS: return "ANCHORS"; break; + case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; break; + case CATYPE_CRLS: return "CRLS"; break; + default: return "INVALID"; break; + } +} + krb5_error_code pkinit_init_identity_opts(pkinit_identity_opts **idopts) { @@ -301,7 +325,6 @@ parse_pkcs11_options(krb5_context context, retval = 0; cleanup: free(s); - /* XXX Clean up other stuff too? */ return retval; } #endif @@ -413,7 +436,7 @@ process_option_identity(krb5_context context, } idopts->idtype = idtype; - pkiDebug("%s: idtype is %d\n", __FUNCTION__, idopts->idtype); + pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype)); switch (idtype) { case IDTYPE_ENVVAR: return process_option_identity(context, plg_cryptoctx, req_cryptoctx, @@ -457,12 +480,12 @@ process_option_ca_crl(krb5_context context, unsigned int typelen; int idtype; - pkiDebug("%s: processing catype %d, value '%s'\n", - __FUNCTION__, catype, value); + pkiDebug("%s: processing catype %s, value '%s'\n", + __FUNCTION__, catype2string(catype), value); residual = strchr(value, ':'); if (residual == NULL) { pkiDebug("No type given for '%s'\n", value); - return EINVAL; /* XXX */ + return EINVAL; } residual++; /* skip past colon */ typelen = residual - value; diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c index 21a880d..9745a83 100644 --- a/src/plugins/preauth/pkinit/pkinit_lib.c +++ b/src/plugins/preauth/pkinit/pkinit_lib.c @@ -56,11 +56,10 @@ krb5_error_code pkinit_init_req_opts(pkinit_req_opts **reqopts) 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->dh_size = 1024; + opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS; opts->win2k_target = 0; opts->win2k_require_cksum = 0; @@ -88,12 +87,11 @@ krb5_error_code pkinit_init_plg_opts(pkinit_plg_opts **plgopts) 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->dh_min_bits = 0; + opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; *plgopts = opts; @@ -153,6 +151,7 @@ void free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in) void free_krb5_auth_pack(krb5_auth_pack **in) { + int i = 0; if ((*in) == NULL) return; if ((*in)->clientPublicValue != NULL) { /* not freeing clientPublicValue->algorithm.algorithm.data because @@ -167,6 +166,13 @@ void free_krb5_auth_pack(krb5_auth_pack **in) } if ((*in)->pkAuthenticator.paChecksum.contents != NULL) free((*in)->pkAuthenticator.paChecksum.contents); + if ((*in)->supportedCMSTypes != NULL) { + while ((*in)->supportedCMSTypes[i] != NULL) { + free((*in)->supportedCMSTypes[i]); + i++; + } + free((*in)->supportedCMSTypes); + } free(*in); } diff --git a/src/plugins/preauth/pkinit/pkinit_matching.c b/src/plugins/preauth/pkinit/pkinit_matching.c index ebd57f5..f5601ab 100644 --- a/src/plugins/preauth/pkinit/pkinit_matching.c +++ b/src/plugins/preauth/pkinit/pkinit_matching.c @@ -45,7 +45,7 @@ typedef enum { kw_issuer = 2, kw_san = 3, kw_eku = 4, - kw_ku = 5, + kw_ku = 5 } keyword_type; static char * @@ -64,7 +64,7 @@ keyword2string(unsigned int kw) typedef enum { relation_none = 0, relation_and = 1, - relation_or = 2, + relation_or = 2 } relation_type; static char * @@ -81,7 +81,7 @@ relation2string(unsigned int rel) typedef enum { kwvaltype_undefined = 0, kwvaltype_regexp = 1, - kwvaltype_list = 2, + kwvaltype_list = 2 } kw_value_type; static char * @@ -188,7 +188,7 @@ parse_list_value(krb5_context context, { krb5_error_code retval; char *comma; - struct ku_desc *ku; + struct ku_desc *ku = NULL; int found; size_t len; unsigned int *bitptr; @@ -382,7 +382,7 @@ parse_rule_set(krb5_context context, const char *rule; int remaining, totlen; krb5_error_code ret, retval; - rule_component *rc, *trc; + rule_component *rc = NULL, *trc; rule_set *rs; @@ -485,6 +485,8 @@ component_match(krb5_context context, match = regexp_match(context, rc, md->issuer_dn); break; case kw_san: + if (md->sans == NULL) + break; for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) { krb5_unparse_name(context, p, &princ_string); match = regexp_match(context, rc, princ_string); @@ -665,7 +667,7 @@ obtain_all_cert_matching_data(krb5_context context, int i, cert_count; pkinit_cert_iter_handle ih = NULL; pkinit_cert_handle ch; - pkinit_cert_matching_data **matchdata; + pkinit_cert_matching_data **matchdata = NULL; retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, &cert_count); @@ -740,7 +742,7 @@ pkinit_cert_matching(krb5_context context, rule_set *rs = NULL; int match_found = 0; pkinit_cert_matching_data **matchdata = NULL; - pkinit_cert_matching_data *the_matching_cert; + pkinit_cert_matching_data *the_matching_cert = NULL; /* If no matching rules, select the default cert and we're done */ pkinit_libdefault_strings(context, krb5_princ_realm(context, princ), diff --git a/src/plugins/preauth/pkinit/pkinit_profile.c b/src/plugins/preauth/pkinit/pkinit_profile.c index abdd9cc..403068a 100644 --- a/src/plugins/preauth/pkinit/pkinit_profile.c +++ b/src/plugins/preauth/pkinit/pkinit_profile.c @@ -247,7 +247,6 @@ pkinit_libdefault_strings(krb5_context context, const krb5_data *realm, profile = context->profile; - names[0] = "libdefaults"; if (realm != NULL) { /* @@ -259,6 +258,24 @@ pkinit_libdefault_strings(krb5_context context, const krb5_data *realm, * } */ + names[0] = "libdefaults"; + names[1] = realmstr; + names[2] = option; + names[3] = 0; + retval = profile_get_values(profile, names, &values); + if (retval == 0 && values != NULL && values[0] != NULL) + goto goodbye; + + /* + * Try number two: + * + * [realms] + * REALM = { + * option = <value> + * } + */ + + names[0] = "realms"; names[1] = realmstr; names[2] = option; names[3] = 0; @@ -266,13 +283,15 @@ pkinit_libdefault_strings(krb5_context context, const krb5_data *realm, if (retval == 0 && values != NULL && values[0] != NULL) goto goodbye; } + /* - * Try number two: + * Try number three: * * [libdefaults] * option = <value> */ + names[0] = "libdefaults"; names[1] = option; names[2] = 0; retval = profile_get_values(profile, names, &values); diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c index 63f47a6..787c4a1 100644 --- a/src/plugins/preauth/pkinit/pkinit_srv.c +++ b/src/plugins/preauth/pkinit/pkinit_srv.c @@ -345,7 +345,6 @@ pkinit_server_verify_padata(krb5_context context, krb5_data k5data; pkiDebug("pkinit_verify_padata: entered!\n"); - if (data == NULL || data->length <= 0 || data->contents == NULL) return 0; @@ -377,7 +376,11 @@ pkinit_server_verify_padata(krb5_context context, pkiDebug("decode_krb5_pa_pk_as_req failed\n"); goto cleanup; } - +#ifdef DEBUG_ASN1 + print_buffer_bin(reqp->signedAuthPack.data, + reqp->signedAuthPack.length, + "/tmp/kdc_signed_data"); +#endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT, plgctx->opts->require_crl_checking, @@ -394,6 +397,11 @@ pkinit_server_verify_padata(krb5_context context, pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n"); goto cleanup; } +#ifdef DEBUG_ASN1 + print_buffer_bin(reqp9->signedAuthPack.data, + reqp9->signedAuthPack.length, + "/tmp/kdc_signed_data_draft9"); +#endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, @@ -416,14 +424,12 @@ pkinit_server_verify_padata(krb5_context context, &valid_san); if (retval) goto cleanup; - if (!valid_san) { pkiDebug("%s: did not find an acceptable SAN in user certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; goto cleanup; } - retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); if (retval) goto cleanup; @@ -466,13 +472,13 @@ pkinit_server_verify_padata(krb5_context context, * came from the client. Therefore, we use the original * packet contents. */ - retval = decode_krb5_as_req(req_pkt, &tmp_as_req); + retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req); if (retval) { pkiDebug("decode_krb5_as_req returned %d\n", (int)retval); goto cleanup; } - retval = encode_krb5_kdc_req_body(tmp_as_req, &der_req); + retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req); if (retval) { pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); goto cleanup; @@ -548,29 +554,36 @@ pkinit_server_verify_padata(krb5_context context, } /* return authorization data to be included in the ticket */ - my_authz_data = malloc(2 * sizeof(*my_authz_data)); - if (my_authz_data == NULL) { - retval = ENOMEM; - pkiDebug("Couldn't allocate krb5_authdata ptr array\n"); - goto cleanup; - } - my_authz_data[1] = NULL; - my_authz_data[0] = malloc(sizeof(krb5_authdata)); - if (my_authz_data[0] == NULL) { - retval = ENOMEM; - pkiDebug("Couldn't allocate krb5_authdata\n"); - free(my_authz_data); - goto cleanup; + switch ((int)data->pa_type) { + case KRB5_PADATA_PK_AS_REQ: +#if 1 + my_authz_data = malloc(2 * sizeof(*my_authz_data)); + if (my_authz_data == NULL) { + retval = ENOMEM; + pkiDebug("Couldn't allocate krb5_authdata ptr array\n"); + goto cleanup; + } + my_authz_data[1] = NULL; + my_authz_data[0] = malloc(sizeof(krb5_authdata)); + if (my_authz_data[0] == NULL) { + retval = ENOMEM; + pkiDebug("Couldn't allocate krb5_authdata\n"); + free(my_authz_data); + goto cleanup; + } + my_authz_data[0]->magic = KV5M_AUTHDATA; + my_authz_data[0]->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS; + my_authz_data[0]->contents = krb5_authz.data; + krb5_authz.data = NULL; /* Don't free during cleanup! */ + my_authz_data[0]->length = krb5_authz.length; + *authz_data = my_authz_data; + pkiDebug("Returning %d bytes of authorization data\n", + krb5_authz.length); +#endif + break; + default: + *authz_data = NULL; } - my_authz_data[0]->magic = KV5M_AUTHDATA; - my_authz_data[0]->ad_type = 1; - my_authz_data[0]->contents = krb5_authz.data; - krb5_authz.data = NULL; /* Don't free during cleanup! */ - my_authz_data[0]->length = krb5_authz.length; - *authz_data = my_authz_data; - pkiDebug("Returning %d bytes of authorization data\n", krb5_authz.length); - *authz_data = my_authz_data; - /* remember to set the PREAUTH flag in the reply */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; *pa_request_context = reqctx; @@ -597,7 +610,7 @@ pkinit_server_verify_padata(krb5_context context, free_krb5_pa_pk_as_req_draft9(&reqp9); } if (tmp_as_req != NULL) - krb5_free_kdc_req(context, tmp_as_req); + k5int_krb5_free_kdc_req(context, tmp_as_req); if (authp_data.data != NULL) free(authp_data.data); if (krb5_authz.data != NULL) @@ -693,6 +706,7 @@ pkinit_server_return_padata(krb5_context context, break; } } + if (i == request->nktypes) { retval = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; @@ -822,9 +836,7 @@ pkinit_server_return_padata(krb5_context context, for (i = 0; request->padata[i] != NULL; i++) { pkiDebug("%s: Checking pa_type 0x%08x\n", __FUNCTION__, request->padata[i]->pa_type); - if (request->padata[i]->pa_type == 132 - /* XXX The asn1 encoding from Apple seems to be incorrect? */ - || request->padata[i]->pa_type == 0xffffff84) + if (request->padata[i]->pa_type == 132) fixed_keypack = 1; } pkiDebug("%s: return checksum instead of nonce = %d\n", @@ -1088,12 +1100,23 @@ pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) pkinit_kdcdefault_integer(context, plgctx->realmname, "pkinit_dh_min_bits", - 0, &plgctx->opts->dh_min_bits); + PKINIT_DEFAULT_DH_MIN_BITS, + &plgctx->opts->dh_min_bits); + if (plgctx->opts->dh_min_bits < 1024) { + pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, " + "using default value (%d) instead\n", __FUNCTION__, + plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS); + plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; + } pkinit_kdcdefault_boolean(context, plgctx->realmname, "pkinit_allow_upn", 0, &plgctx->opts->allow_upn); + pkinit_kdcdefault_boolean(context, plgctx->realmname, + "pkinit_require_crl_checking", + 0, &plgctx->opts->require_crl_checking); + pkinit_kdcdefault_string(context, plgctx->realmname, "pkinit_eku_checking", &eku_string); |