aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/Makefile2
-rw-r--r--doc/admin.texinfo402
-rw-r--r--doc/copyright.texinfo66
-rw-r--r--src/Makefile.in4
-rw-r--r--src/clients/kinit/kinit.M26
-rw-r--r--src/clients/kinit/kinit.c6
-rw-r--r--src/configure.in6
-rw-r--r--src/include/k5-int-pkinit.h270
-rw-r--r--src/include/k5-int.h145
-rw-r--r--src/include/krb5/krb5.hin11
-rw-r--r--src/include/krb5/preauth_plugin.h391
-rw-r--r--src/kdc/do_as_req.c3
-rw-r--r--src/kdc/kdc_preauth.c155
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.c453
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.h45
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.c434
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.h67
-rw-r--r--src/lib/krb5/asn.1/krb5_decode.c147
-rw-r--r--src/lib/krb5/asn.1/krb5_encode.c125
-rw-r--r--src/lib/krb5/krb/preauth2.c57
-rw-r--r--src/lib/krb5/os/accessor.c29
-rw-r--r--src/plugins/preauth/cksum_body/cksum_body_main.c140
-rw-r--r--src/plugins/preauth/pkinit/Makefile.in109
-rw-r--r--src/plugins/preauth/pkinit/README263
-rw-r--r--src/plugins/preauth/pkinit/README.developers18
-rw-r--r--src/plugins/preauth/pkinit/configure.in19
-rw-r--r--src/plugins/preauth/pkinit/pkcs11.h1357
-rw-r--r--src/plugins/preauth/pkinit/pkinit.exports2
-rw-r--r--src/plugins/preauth/pkinit/pkinit.h354
-rw-r--r--src/plugins/preauth/pkinit/pkinit_accessor.c118
-rw-r--r--src/plugins/preauth/pkinit/pkinit_accessor.h83
-rw-r--r--src/plugins/preauth/pkinit/pkinit_clnt.c1492
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto.h623
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c5612
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.h265
-rw-r--r--src/plugins/preauth/pkinit/pkinit_identity.c668
-rw-r--r--src/plugins/preauth/pkinit/pkinit_lib.c477
-rw-r--r--src/plugins/preauth/pkinit/pkinit_matching.c830
-rw-r--r--src/plugins/preauth/pkinit/pkinit_profile.c376
-rw-r--r--src/plugins/preauth/pkinit/pkinit_srv.c1399
-rw-r--r--src/plugins/preauth/wpse/wpse_main.c87
41 files changed, 16847 insertions, 289 deletions
diff --git a/doc/Makefile b/doc/Makefile
index 8be5b3e..1bfaaa5 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,5 +1,5 @@
SRCDIR=../src
-DVI=texi2dvi
+DVI=texi2dvi4a2ps # texi2dvi
DVIPS=dvips -o "$@"
PSPDF=ps2pdf
INFO=makeinfo
diff --git a/doc/admin.texinfo b/doc/admin.texinfo
index fdc0b2b..79608f3 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
@@ -399,6 +399,7 @@ salt. The supported values for salts are as follows.
* capaths::
* dbdefaults::
* dbmodules::
+* pkinit client options::
* Sample krb5.conf File::
@end menu
@@ -592,6 +593,8 @@ The default value for this flag is @value{DefaultProxiable}.
@end table
+
+
@node appdefaults, login, libdefaults, krb5.conf
@subsection [appdefaults]
@@ -1072,7 +1075,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 client 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 +1106,280 @@ This LDAP specific tags indicates the number of connections to be maintained per
@end table
+@node pkinit client options, Sample krb5.conf File, dbmodules, krb5.conf
+@subsection pkinit options
+
+@menu
+* pkinit identity syntax::
+* pkinit krb5.conf options::
+@end menu
+
+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 client options, pkinit client 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 user'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 client 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 client options, krb5.conf
@subsection Sample krb5.conf File
Here is an example of a generic @code{krb5.conf} file:
@@ -1189,6 +1464,7 @@ Here is an example of a generic @code{krb5.conf} file:
@menu
* kdcdefaults::
* realms (kdc.conf)::
+* pkinit kdc options::
* Sample kdc.conf File::
@end menu
@@ -1225,7 +1501,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 kdc options, kdcdefaults, kdc.conf
@subsection [realms]
Each tag in the [realms] section of the file names a Kerberos realm.
@@ -1409,7 +1685,123 @@ This option defaults to @code{true}.
@end table
-@node Sample kdc.conf File, , realms (kdc.conf), kdc.conf
+@node pkinit kdc options, Sample kdc.conf File, realms (kdc.conf), kdc.conf
+@subsection pkinit options
+
+@menu
+* pkinit kdc.conf options::
+@end menu
+
+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 kdc options, pkinit kdc 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 kdc 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..d4a8543 100644
--- a/doc/copyright.texinfo
+++ b/doc/copyright.texinfo
@@ -435,6 +435,72 @@ POSSIBILITY OF SUCH DAMAGE.
@vskip 12pt
@end iftex
+Portions funded by Sandia National Laboratory
+and developed by the University of Michigan's
+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
+
+The pkcs11.h file included in the PKINIT code has the
+following license:
+
+@quotation
+@iftex
+@smallfonts @rm
+@end iftex
+
+Copyright 2006 g10 Code GmbH
+Copyright 2006 Andreas Jellinghaus
+
+This file is free software; as a special exception the author gives
+unlimited permission to copy and/or distribute it, with or without
+modifications, as long as this notice is preserved.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE.
+
+@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/Makefile.in b/src/Makefile.in
index ad808c3..ce37a78 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -6,6 +6,7 @@ mydir=.
# Don't build sample by default: plugins/locate/python plugins/preauth/wpse plugins/preauth/cksum_body
SUBDIRS=util include lib @krb524@ kdc kadmin @ldap_plugin_dir@ slave clients \
plugins/kdb/db2 \
+ plugins/preauth/pkinit \
appl tests \
config-files gen-manpages
BUILDTOP=$(REL)$(C)
@@ -67,7 +68,7 @@ INSTALLMKDIRS = $(KRB5ROOT) $(KRB5MANROOT) $(KRB5OTHERMKDIRS) \
$(ADMIN_BINDIR) $(SERVER_BINDIR) $(CLIENT_BINDIR) \
$(ADMIN_MANDIR) $(SERVER_MANDIR) $(CLIENT_MANDIR) \
$(FILE_MANDIR) $(KRB5_LIBDIR) $(KRB5_INCDIR) \
- $(KRB5_DB_MODULE_DIR) \
+ $(KRB5_DB_MODULE_DIR) $(KRB5_PA_MODULE_DIR) \
$(KRB5_LIBKRB5_MODULE_DIR) \
@localstatedir@ @localstatedir@/krb5kdc \
$(KRB5_INCSUBDIRS) $(datadir) $(EXAMPLEDIR)
@@ -100,6 +101,7 @@ fake-install:
(w=`pwd`; cd util && $(MAKE) install DESTDIR="$$w/util/fakedest")
(w=`pwd`; cd lib && $(MAKE) install DESTDIR="$$w/util/fakedest")
(w=`pwd`; cd plugins/kdb/db2 && $(MAKE) install DESTDIR="$$w/util/fakedest")
+ (w=`pwd`; cd plugins/preauth/pkinit && $(MAKE) install DESTDIR="$$w/util/fakedest")
# (w=`pwd`; cd plugins/locate/python && $(MAKE) install DESTDIR="$$w/util/fakedest")
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 43dbf6d..05b50bf 100644
--- a/src/clients/kinit/kinit.c
+++ b/src/clients/kinit/kinit.c
@@ -220,9 +220,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);
@@ -273,6 +274,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 3e5d3f5..d94786e 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -919,6 +919,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)
@@ -1090,6 +1095,7 @@ V5_AC_OUTPUT_MAKEFILE(.
plugins/kdb/db2/libdb2/test
plugins/preauth/cksum_body
plugins/preauth/wpse
+ plugins/preauth/pkinit
clients clients/klist clients/kinit clients/kvno
clients/kdestroy clients/kpasswd clients/ksu
diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h
new file mode 100644
index 0000000..e75c803
--- /dev/null
+++ b/src/include/k5-int-pkinit.h
@@ -0,0 +1,270 @@
+/*
+ * COPYRIGHT (C) 2006
+ * 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.
+ */
+
+#ifndef _KRB5_INT_PKINIT_H
+#define _KRB5_INT_PKINIT_H
+
+/*
+ * pkinit structures
+ */
+
+/* PKAuthenticator */
+typedef struct _krb5_pk_authenticator {
+ krb5_int32 cusec; /* (0..999999) */
+ krb5_timestamp ctime;
+ krb5_int32 nonce; /* (0..4294967295) */
+ krb5_checksum paChecksum;
+} krb5_pk_authenticator;
+
+/* PKAuthenticator draft9 */
+typedef struct _krb5_pk_authenticator_draft9 {
+ krb5_principal kdcName;
+ krb5_octet_data kdcRealm;
+ krb5_int32 cusec; /* (0..999999) */
+ krb5_timestamp ctime;
+ krb5_int32 nonce; /* (0..4294967295) */
+} krb5_pk_authenticator_draft9;
+
+/* AlgorithmIdentifier */
+typedef struct _krb5_algorithm_identifier {
+ krb5_octet_data algorithm; /* OID */
+ krb5_octet_data parameters; /* Optional */
+} krb5_algorithm_identifier;
+
+/* SubjectPublicKeyInfo */
+typedef struct _krb5_subject_pk_info {
+ krb5_algorithm_identifier algorithm;
+ krb5_octet_data subjectPublicKey; /* BIT STRING */
+} krb5_subject_pk_info;
+
+/* AuthPack */
+typedef struct _krb5_auth_pack {
+ krb5_pk_authenticator pkAuthenticator;
+ krb5_subject_pk_info *clientPublicValue; /* Optional */
+ krb5_algorithm_identifier **supportedCMSTypes; /* Optional */
+ krb5_octet_data clientDHNonce; /* Optional */
+} krb5_auth_pack;
+
+/* AuthPack draft9 */
+typedef struct _krb5_auth_pack_draft9 {
+ krb5_pk_authenticator_draft9 pkAuthenticator;
+ krb5_subject_pk_info *clientPublicValue; /* Optional */
+} krb5_auth_pack_draft9;
+
+/* ExternalPrincipalIdentifier */
+typedef struct _krb5_external_principal_identifier {
+ krb5_octet_data subjectName; /* Optional */
+ krb5_octet_data issuerAndSerialNumber; /* Optional */
+ krb5_octet_data subjectKeyIdentifier; /* Optional */
+} krb5_external_principal_identifier;
+
+/* TrustedCas */
+typedef struct _krb5_trusted_ca {
+ enum {
+ choice_trusted_cas_UNKNOWN = -1,
+ choice_trusted_cas_principalName = 0,
+ choice_trusted_cas_caName = 1,
+ choice_trusted_cas_issuerAndSerial = 2
+ } choice;
+ union {
+ krb5_principal principalName;
+ krb5_octet_data caName; /* fully-qualified X.500 "Name" as defined by X.509 (der-encoded) */
+ krb5_octet_data issuerAndSerial; /* Optional -- IssuerAndSerialNumber (der-encoded) */
+ } u;
+} krb5_trusted_ca;
+
+/* typed data */
+typedef struct _krb5_typed_data {
+ krb5_magic magic;
+ krb5_int32 type;
+ unsigned int length;
+ krb5_octet *data;
+} krb5_typed_data;
+
+/* PA-PK-AS-REQ (Draft 9 -- PA TYPE 14) */
+typedef struct _krb5_pa_pk_as_req_draft9 {
+ krb5_octet_data signedAuthPack;
+ krb5_trusted_ca **trustedCertifiers; /* Optional array */
+ krb5_octet_data kdcCert; /* Optional */
+ krb5_octet_data encryptionCert;
+} krb5_pa_pk_as_req_draft9;
+
+/* PA-PK-AS-REQ (rfc4556 -- PA TYPE 16) */
+typedef struct _krb5_pa_pk_as_req {
+ krb5_octet_data signedAuthPack;
+ krb5_external_principal_identifier **trustedCertifiers; /* Optional array */
+ krb5_octet_data kdcPkId; /* Optional */
+} krb5_pa_pk_as_req;
+
+/* DHRepInfo */
+typedef struct _krb5_dh_rep_info {
+ krb5_octet_data dhSignedData;
+ krb5_octet_data serverDHNonce; /* Optional */
+} krb5_dh_rep_info;
+
+/* KDCDHKeyInfo */
+typedef struct _krb5_kdc_dh_key_info {
+ krb5_octet_data subjectPublicKey; /* BIT STRING */
+ krb5_int32 nonce; /* (0..4294967295) */
+ krb5_timestamp dhKeyExpiration; /* Optional */
+} krb5_kdc_dh_key_info;
+
+/* KDCDHKeyInfo draft9*/
+typedef struct _krb5_kdc_dh_key_info_draft9 {
+ krb5_octet_data subjectPublicKey; /* BIT STRING */
+ krb5_int32 nonce; /* (0..4294967295) */
+} krb5_kdc_dh_key_info_draft9;
+
+/* ReplyKeyPack */
+typedef struct _krb5_reply_key_pack {
+ krb5_keyblock replyKey;
+ krb5_checksum asChecksum;
+} krb5_reply_key_pack;
+
+/* ReplyKeyPack */
+typedef struct _krb5_reply_key_pack_draft9 {
+ krb5_keyblock replyKey;
+ krb5_int32 nonce;
+} krb5_reply_key_pack_draft9;
+
+/* PA-PK-AS-REP (Draft 9 -- PA TYPE 15) */
+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;
+ union {
+ krb5_octet_data dhSignedData;
+ krb5_octet_data encKeyPack;
+ } u;
+} krb5_pa_pk_as_rep_draft9;
+
+/* PA-PK-AS-REP (rfc4556 -- PA TYPE 17) */
+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;
+ union {
+ krb5_dh_rep_info dh_Info;
+ krb5_octet_data encKeyPack;
+ } u;
+} krb5_pa_pk_as_rep;
+
+/*
+ * Begin "asn1.h"
+ */
+
+/*************************************************************************
+ * Prototypes for pkinit asn.1 encode routines
+ *************************************************************************/
+
+krb5_error_code encode_krb5_pa_pk_as_req
+ (const krb5_pa_pk_as_req *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pa_pk_as_req_draft9
+ (const krb5_pa_pk_as_req_draft9 *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pa_pk_as_rep
+ (const krb5_pa_pk_as_rep *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pa_pk_as_rep_draft9
+ (const krb5_pa_pk_as_rep_draft9 *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_auth_pack
+ (const krb5_auth_pack *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_auth_pack_draft9
+ (const krb5_auth_pack_draft9 *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_kdc_dh_key_info
+ (const krb5_kdc_dh_key_info *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_reply_key_pack
+ (const krb5_reply_key_pack *, krb5_data **code);
+
+krb5_error_code encode_krb5_reply_key_pack_draft9
+ (const krb5_reply_key_pack_draft9 *, krb5_data **code);
+
+krb5_error_code encode_krb5_typed_data
+ (const krb5_typed_data **, krb5_data **code);
+
+krb5_error_code encode_krb5_td_trusted_certifiers
+ (const krb5_external_principal_identifier **, krb5_data **code);
+
+krb5_error_code encode_krb5_td_dh_parameters
+ (const krb5_algorithm_identifier **, krb5_data **code);
+
+/*************************************************************************
+ * Prototypes for pkinit asn.1 decode routines
+ *************************************************************************/
+
+krb5_error_code decode_krb5_pa_pk_as_req
+ (const krb5_data *, krb5_pa_pk_as_req **);
+
+krb5_error_code decode_krb5_pa_pk_as_req_draft9
+ (const krb5_data *, krb5_pa_pk_as_req_draft9 **);
+
+krb5_error_code decode_krb5_pa_pk_as_rep
+ (const krb5_data *, krb5_pa_pk_as_rep **);
+
+krb5_error_code decode_krb5_pa_pk_as_rep_draft9
+ (const krb5_data *, krb5_pa_pk_as_rep_draft9 **);
+
+krb5_error_code decode_krb5_auth_pack
+ (const krb5_data *, krb5_auth_pack **);
+
+krb5_error_code decode_krb5_auth_pack_draft9
+ (const krb5_data *, krb5_auth_pack_draft9 **);
+
+krb5_error_code decode_krb5_kdc_dh_key_info
+ (const krb5_data *, krb5_kdc_dh_key_info **);
+
+krb5_error_code decode_krb5_principal_name
+ (const krb5_data *, krb5_principal_data **);
+
+krb5_error_code decode_krb5_reply_key_pack
+ (const krb5_data *, krb5_reply_key_pack **);
+
+krb5_error_code decode_krb5_reply_key_pack_draft9
+ (const krb5_data *, krb5_reply_key_pack_draft9 **);
+
+krb5_error_code decode_krb5_typed_data
+ (const krb5_data *, krb5_typed_data ***);
+
+krb5_error_code decode_krb5_td_trusted_certifiers
+ (const krb5_data *, krb5_external_principal_identifier ***);
+
+krb5_error_code decode_krb5_td_dh_parameters
+ (const krb5_data *, krb5_algorithm_identifier ***);
+
+#endif /* _KRB5_INT_PKINIT_H */
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 2b97960..d5a8d47 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -252,6 +252,23 @@ typedef INT64_TYPE krb5_int64;
/* in e-text) */
#define KRB_ERR_FIELD_TOOLONG 61 /* Field is too long for impl. */
+/* PKINIT server-reported errors */
+#define KDC_ERR_CLIENT_NOT_TRUSTED 62 /* client cert not trusted */
+#define KDC_ERR_INVALID_SIG 64 /* client signature verify failed */
+#define KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED 65 /* invalid Diffie-Hellman parameters */
+#define KDC_ERR_CANT_VERIFY_CERTIFICATE 70 /* client cert not verifiable to */
+ /* trusted root cert */
+#define KDC_ERR_INVALID_CERTIFICATE 71 /* client cert had invalid signature */
+#define KDC_ERR_REVOKED_CERTIFICATE 72 /* client cert was revoked */
+#define KDC_ERR_REVOCATION_STATUS_UNKNOWN 73 /* client cert revoked, reason unknown */
+#define KDC_ERR_CLIENT_NAME_MISMATCH 75 /* mismatch between client cert and */
+ /* principal name */
+#define KDC_ERR_INCONSISTENT_KEY_PURPOSE 77 /* bad extended key use */
+#define KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED 78 /* bad digest algorithm in client cert */
+#define KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED 79 /* missing paChecksum in PA-PK-AS-REQ */
+#define KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED 80 /* bad digest algorithm in SignedData */
+#define KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED 81
+
#endif /* KRB5_ERRORS__ */
/*
* End "k5-errors.h"
@@ -417,6 +434,13 @@ typedef struct _krb5_enc_sam_response_enc_2 {
} krb5_enc_sam_response_enc_2;
/*
+ * Keep the pkinit definitions in a separate file so that the plugin
+ * only has to include k5-int-pkinit.h rather than k5-int.h
+ */
+
+#include "k5-int-pkinit.h"
+
+/*
* Begin "ext-proto.h"
*/
#ifndef KRB5_EXT_PROTO__
@@ -869,54 +893,17 @@ typedef struct _krb5_preauth_context {
krb5_enctype *enctypes;
/* The plugin's per-plugin context and a function to clear it. */
void *plugin_context;
- void (*client_fini)(krb5_context context, void *plugin_context);
+ preauth_client_plugin_fini_proc client_fini;
/* The module's table, and some of its members, copied here for
* convenience when we populated the list. */
struct krb5plugin_preauth_client_ftable_v0 *ftable;
const char *name;
int flags, use_count;
- krb5_error_code (*client_process)(krb5_context context,
- void *plugin_context,
- void *request_context,
- krb5_get_init_creds_opt *opt,
- preauth_get_client_data_proc get_data_proc,
- krb5_preauth_client_rock *rock,
- krb5_kdc_req *request,
- krb5_data *encoded_request_body,
- krb5_data *encoded_previous_request,
- krb5_pa_data *pa_data,
- krb5_prompter_fct prompter,
- void *prompter_data,
- preauth_get_as_key_proc gak_fct,
- void *gak_data,
- krb5_data *salt,
- krb5_data *s2kparams,
- krb5_keyblock *as_key,
- krb5_pa_data **out_pa_data);
- krb5_error_code (*client_tryagain)(krb5_context context,
- void *plugin_context,
- void *request_context,
- krb5_get_init_creds_opt *opt,
- preauth_get_client_data_proc get_data_proc,
- krb5_preauth_client_rock *rock,
- krb5_kdc_req *request,
- krb5_data *encoded_request_body,
- krb5_data *encoded_previous_request,
- krb5_pa_data *old_pa_data,
- krb5_error *err_reply,
- krb5_prompter_fct prompter,
- void *prompter_data,
- preauth_get_as_key_proc gak_fct,
- void *gak_data,
- krb5_data *salt,
- krb5_data *s2kparams,
- krb5_keyblock *as_key,
- krb5_pa_data **new_pa_data);
- supply_gic_opts_proc client_supply_gic_opts;
- void (*client_req_init)(krb5_context context, void *plugin_context,
- void **request_context);
- void (*client_req_fini)(krb5_context context, void *plugin_context,
- void *request_context);
+ preauth_client_process_proc client_process;
+ preauth_client_tryagain_proc client_tryagain;
+ preauth_client_supply_gic_opts_proc client_supply_gic_opts;
+ preauth_client_request_init_proc client_req_init;
+ preauth_client_request_fini_proc client_req_fini;
/* The per-request context which the client_req_init() function
* might allocate, which we'll need to clean up later by
* calling the client_req_fini() function. */
@@ -1450,6 +1437,9 @@ krb5_error_code encode_krb5_error
krb5_error_code encode_krb5_authdata
(const krb5_authdata **rep, krb5_data **code);
+krb5_error_code encode_krb5_authdata_elt
+ (const krb5_authdata *rep, krb5_data **code);
+
krb5_error_code encode_krb5_pwd_sequence
(const passwd_phrase_element *rep, krb5_data **code);
@@ -1863,7 +1853,7 @@ void krb5int_free_srv_dns_data(struct srv_dns_entry *);
/* To keep happy libraries which are (for now) accessing internal stuff */
/* Make sure to increment by one when changing the struct */
-#define KRB5INT_ACCESS_STRUCT_VERSION 10
+#define KRB5INT_ACCESS_STRUCT_VERSION 11
#ifndef ANAME_SZ
struct ktext; /* from krb.h, for krb524 support */
@@ -1919,6 +1909,73 @@ typedef struct _krb5int_access {
struct _krb5_key_data **out,
krb5_int16 *n_key_data,
int *mkvno);
+
+ /*
+ * pkinit asn.1 encode/decode functions
+ */
+ krb5_error_code (*encode_krb5_auth_pack)
+ (const krb5_auth_pack *rep, krb5_data **code);
+ krb5_error_code (*encode_krb5_auth_pack_draft9)
+ (const krb5_auth_pack_draft9 *rep, krb5_data **code);
+ krb5_error_code (*encode_krb5_kdc_dh_key_info)
+ (const krb5_kdc_dh_key_info *rep, krb5_data **code);
+ krb5_error_code (*encode_krb5_pa_pk_as_rep)
+ (const krb5_pa_pk_as_rep *rep, krb5_data **code);
+ krb5_error_code (*encode_krb5_pa_pk_as_rep_draft9)
+ (const krb5_pa_pk_as_rep_draft9 *rep, krb5_data **code);
+ krb5_error_code (*encode_krb5_pa_pk_as_req)
+ (const krb5_pa_pk_as_req *rep, krb5_data **code);
+ krb5_error_code (*encode_krb5_pa_pk_as_req_draft9)
+ (const krb5_pa_pk_as_req_draft9 *rep, krb5_data **code);
+ krb5_error_code (*encode_krb5_reply_key_pack)
+ (const krb5_reply_key_pack *, krb5_data **code);
+ krb5_error_code (*encode_krb5_reply_key_pack_draft9)
+ (const krb5_reply_key_pack_draft9 *, krb5_data **code);
+ krb5_error_code (*encode_krb5_td_dh_parameters)
+ (const krb5_algorithm_identifier **, krb5_data **code);
+ krb5_error_code (*encode_krb5_td_trusted_certifiers)
+ (const krb5_external_principal_identifier **, krb5_data **code);
+ krb5_error_code (*encode_krb5_typed_data)
+ (const krb5_typed_data **, krb5_data **code);
+
+ krb5_error_code (*decode_krb5_auth_pack)
+ (const krb5_data *, krb5_auth_pack **);
+ krb5_error_code (*decode_krb5_auth_pack_draft9)
+ (const krb5_data *, krb5_auth_pack_draft9 **);
+ krb5_error_code (*decode_krb5_pa_pk_as_req)
+ (const krb5_data *, krb5_pa_pk_as_req **);
+ krb5_error_code (*decode_krb5_pa_pk_as_req_draft9)
+ (const krb5_data *, krb5_pa_pk_as_req_draft9 **);
+ krb5_error_code (*decode_krb5_pa_pk_as_rep)
+ (const krb5_data *, krb5_pa_pk_as_rep **);
+ krb5_error_code (*decode_krb5_pa_pk_as_rep_draft9)
+ (const krb5_data *, krb5_pa_pk_as_rep_draft9 **);
+ krb5_error_code (*decode_krb5_kdc_dh_key_info)
+ (const krb5_data *, krb5_kdc_dh_key_info **);
+ krb5_error_code (*decode_krb5_principal_name)
+ (const krb5_data *, krb5_principal_data **);
+ krb5_error_code (*decode_krb5_reply_key_pack)
+ (const krb5_data *, krb5_reply_key_pack **);
+ krb5_error_code (*decode_krb5_reply_key_pack_draft9)
+ (const krb5_data *, krb5_reply_key_pack_draft9 **);
+ krb5_error_code (*decode_krb5_td_dh_parameters)
+ (const krb5_data *, krb5_algorithm_identifier ***);
+ krb5_error_code (*decode_krb5_td_trusted_certifiers)
+ (const krb5_data *, krb5_external_principal_identifier ***);
+ 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 *);
+ krb5_error_code (*encode_krb5_authdata_elt)
+ (const krb5_authdata *rep, krb5_data **code);
+
} krb5int_access;
#define KRB5INT_ACCESS_VERSION \
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 88154f8..ea5d0fc 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -202,6 +202,12 @@ typedef struct _krb5_data {
char *data;
} krb5_data;
+typedef struct _krb5_octet_data {
+ krb5_magic magic;
+ unsigned int length;
+ krb5_octet *data;
+} krb5_octet_data;
+
/*
* Hack length for crypto library to use the afs_string_to_key It is
* equivalent to -1 without possible sign extension
@@ -890,6 +896,11 @@ krb5_error_code KRB5_CALLCONV krb5_verify_checksum
#define KRB5_ALTAUTH_ATT_CHALLENGE_RESPONSE 64
/* authorization data types */
+#define KRB5_AUTHDATA_IF_RELEVANT 1
+#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/include/krb5/preauth_plugin.h b/src/include/krb5/preauth_plugin.h
index 7243a00..2429560 100644
--- a/src/include/krb5/preauth_plugin.h
+++ b/src/include/krb5/preauth_plugin.h
@@ -93,34 +93,30 @@ struct _krb5_preauth_client_rock;
* which gets sent over the wire. */
#define PA_PSEUDO 0x00000080
+
+/***************************************************************************
+ *
+ * Client-side preauthentication plugin interface definition.
+ *
+ ***************************************************************************/
+
/*
- * A server module's callback functions are allowed to request specific types
- * of information about the given client or server record or request, even
- * though the database records themselves are opaque to the module.
+ * A callback which will obtain the user's long-term AS key by prompting the
+ * user for the password, then salting it properly, and so on. For the moment,
+ * it's identical to the get_as_key callback used inside of libkrb5, but we
+ * define a new typedef here instead of making the existing one public to
+ * isolate ourselves from potential future changes.
*/
-enum krb5plugin_preauth_entry_request_type {
- /* The returned krb5_data item holds a DER-encoded X.509 certificate. */
- krb5plugin_preauth_entry_request_certificate = 1,
- /* The returned krb5_data_item holds a krb5_deltat. */
- krb5plugin_preauth_entry_max_time_skew = 2,
- /* The returned krb5_data_item holds an array of krb5_keyblock structures,
- * terminated by an entry with key type = 0.
- * Each keyblock should have its contents freed in turn, and then the data
- * item itself should be freed. */
- krb5plugin_preauth_keys = 3,
- /* The returned krb5_data_item holds the request structure, re-encoded
- * using DER. Unless the client implementation is the same as the server
- * implementation, there's a good chance that the result will not match
- * what the client sent, so don't go creating any fatal errors if it
- * doesn't match up. */
- krb5plugin_preauth_request_body = 4
-};
typedef krb5_error_code
-(*preauth_get_entry_data_proc)(krb5_context,
- krb5_kdc_req *,
- struct _krb5_db_entry_new *,
- krb5_int32 request_type,
- krb5_data **);
+(*preauth_get_as_key_proc)(krb5_context,
+ krb5_principal,
+ krb5_enctype,
+ krb5_prompter_fct,
+ void *prompter_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ void *gak_data);
/*
* A client module's callback functions are allowed to request various
@@ -139,23 +135,92 @@ typedef krb5_error_code
krb5_int32 request_type,
krb5_data **);
-/*
- * A callback which will obtain the user's long-term AS key by prompting the
- * user for the password, then salting it properly, and so on. For the moment,
- * it's identical to the get_as_key callback used inside of libkrb5, but we
- * define a new typedef here instead of making the existing one public to
- * isolate ourselves from potential future changes.
- */
+/* Per-plugin initialization/cleanup. The init function is called
+ * by libkrb5 when the plugin is loaded, and the fini function is
+ * called before the plugin is unloaded. Both are optional and
+ * may be called multiple times in case the plugin is used in
+ * multiple contexts. The returned context lives the lifetime of
+ * the krb5_context */
typedef krb5_error_code
-(*preauth_get_as_key_proc)(krb5_context,
- krb5_principal,
- krb5_enctype,
- krb5_prompter_fct,
- void *prompter_data,
- krb5_data *salt,
- krb5_data *s2kparams,
- krb5_keyblock *as_key,
- void *gak_data);
+(*preauth_client_plugin_init_proc)(krb5_context context,
+ void **plugin_context);
+typedef void
+(*preauth_client_plugin_fini_proc)(krb5_context context,
+ void *plugin_context);
+
+/* A callback which returns flags indicating if the module is a "real" or
+ * an "info" mechanism, and so on. This function is called for each entry
+ * in the client_pa_type_list. */
+typedef int
+(*preauth_client_get_flags_proc)(krb5_context context,
+ krb5_preauthtype pa_type);
+
+/* Per-request initialization/cleanup. The request_init function is
+ * called when beginning to process a get_init_creds request and the
+ * request_fini function is called when processing of the request is
+ * complete. This is optional. It may be called multiple times in
+ * the lifetime of a krb5_context. */
+typedef void
+(*preauth_client_request_init_proc)(krb5_context context,
+ void *plugin_context,
+ void **request_context);
+typedef void
+(*preauth_client_request_fini_proc)(krb5_context context,
+ void *plugin_context,
+ void *request_context);
+
+/* Client function which processes server-supplied data in pa_data,
+ * returns created data in out_pa_data, storing any of its own state in
+ * client_context if data for the associated preauthentication type is
+ * needed. It is also called after the AS-REP is received if the AS-REP
+ * includes preauthentication data of the associated type.
+ * NOTE! the encoded_previous_request will be NULL the first time this
+ * function is called, because it is expected to only ever contain the data
+ * obtained from a previous call to this function. */
+typedef krb5_error_code
+(*preauth_client_process_proc)(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_get_init_creds_opt *opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *pa_data,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ void *gak_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ krb5_pa_data ***out_pa_data);
+
+/* Client function which can attempt to use e-data in the error response to
+ * try to recover from the given error. If this function is not NULL, and
+ * it stores data in out_pa_data which is different data from the contents
+ * of in_pa_data, then the client library will retransmit the request. */
+typedef krb5_error_code
+(*preauth_client_tryagain_proc)(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_get_init_creds_opt *opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_pa_data,
+ krb5_error *error,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ void *gak_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ krb5_pa_data ***out_pa_data);
/*
* Client function which receives krb5_get_init_creds_opt information.
@@ -163,18 +228,19 @@ typedef krb5_error_code
* the module if it wishes to reference it after returning from this call.
*/
typedef krb5_error_code
-(*supply_gic_opts_proc)(krb5_context context,
- void *plugin_context,
- krb5_get_init_creds_opt *opt,
- const char *attr,
- const char *value);
+(*preauth_client_supply_gic_opts_proc)(krb5_context context,
+ void *plugin_context,
+ krb5_get_init_creds_opt *opt,
+ const char *attr,
+ const char *value);
+
/*
* The function table / structure which a preauth client module must export as
* "preauthentication_client_0". If the interfaces work correctly, future
* versions of the table will add either more callbacks or more arguments to
* callbacks, and in both cases we'll be able to wrap the v0 functions.
*/
-typedef struct krb5plugin_preauth_client_ftable_v0 {
+typedef struct krb5plugin_preauth_client_ftable_v1 {
/* Not-usually-visible name. */
char *name;
@@ -192,21 +258,22 @@ typedef struct krb5plugin_preauth_client_ftable_v0 {
* may be called multiple times in case the plugin is used in
* multiple contexts. The returned context lives the lifetime of
* the krb5_context */
- krb5_error_code (*init)(krb5_context context, void **plugin_context);
- void (*fini)(krb5_context context, void *plugin_context);
+ preauth_client_plugin_init_proc init;
+ preauth_client_plugin_fini_proc fini;
+
/* A callback which returns flags indicating if the module is a "real" or
* an "info" mechanism, and so on. This function is called for each entry
* in the client_pa_type_list. */
- int (*flags)(krb5_context context, krb5_preauthtype pa_type);
+ preauth_client_get_flags_proc flags;
+
/* Per-request initialization/cleanup. The request_init function is
* called when beginning to process a get_init_creds request and the
* request_fini function is called when processing of the request is
* complete. This is optional. It may be called multiple times in
* the lifetime of a krb5_context. */
- void (*request_init)(krb5_context context, void *plugin_context,
- void **request_context);
- void (*request_fini)(krb5_context context, void *plugin_context,
- void *request_context);
+ preauth_client_request_init_proc request_init;
+ preauth_client_request_fini_proc request_fini;
+
/* Client function which processes server-supplied data in pa_data,
* returns created data in out_pa_data, storing any of its own state in
* client_context if data for the associated preauthentication type is
@@ -215,52 +282,140 @@ typedef struct krb5plugin_preauth_client_ftable_v0 {
* NOTE! the encoded_previous_request will be NULL the first time this
* function is called, because it is expected to only ever contain the data
* obtained from a previous call to this function. */
- krb5_error_code (*process)(krb5_context context,
- void *plugin_context,
- void *request_context,
- krb5_get_init_creds_opt *opt,
- preauth_get_client_data_proc get_data_proc,
- struct _krb5_preauth_client_rock *rock,
- krb5_kdc_req *request,
- krb5_data *encoded_request_body,
- krb5_data *encoded_previous_request,
- krb5_pa_data *pa_data,
- krb5_prompter_fct prompter,
- void *prompter_data,
- preauth_get_as_key_proc gak_fct,
- void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key,
- krb5_pa_data **out_pa_data);
+ preauth_client_process_proc process;
+
/* Client function which can attempt to use e-data in the error response to
* try to recover from the given error. If this function is not NULL, and
* it stores data in out_pa_data which is different data from the contents
* of in_pa_data, then the client library will retransmit the request. */
- krb5_error_code (*tryagain)(krb5_context context,
- void *plugin_context,
- void *request_context,
- krb5_get_init_creds_opt *opt,
- preauth_get_client_data_proc get_data_proc,
- struct _krb5_preauth_client_rock *rock,
- krb5_kdc_req *request,
- krb5_data *encoded_request_body,
- krb5_data *encoded_previous_request,
- krb5_pa_data *in_pa_data,
- krb5_error *error,
- krb5_prompter_fct prompter,
- void *prompter_data,
- preauth_get_as_key_proc gak_fct,
- void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key,
- krb5_pa_data **out_pa_data);
+ preauth_client_tryagain_proc tryagain;
+
/*
* Client function which receives krb5_get_init_creds_opt information.
* The attr and value information supplied should be copied locally by
* the module if it wishes to reference it after returning from this call.
*/
- supply_gic_opts_proc gic_opts;
-} krb5plugin_preauth_client_ftable_v0;
+ preauth_client_supply_gic_opts_proc gic_opts;
+
+} krb5plugin_preauth_client_ftable_v1;
+
+
+/***************************************************************************
+ *
+ * Server-side preauthentication plugin interface definition.
+ *
+ ***************************************************************************/
+
+/*
+ * A server module's callback functions are allowed to request specific types
+ * of information about the given client or server record or request, even
+ * though the database records themselves are opaque to the module.
+ */
+enum krb5plugin_preauth_entry_request_type {
+ /* The returned krb5_data item holds a DER-encoded X.509 certificate. */
+ krb5plugin_preauth_entry_request_certificate = 1,
+ /* The returned krb5_data_item holds a krb5_deltat. */
+ krb5plugin_preauth_entry_max_time_skew = 2,
+ /* The returned krb5_data_item holds an array of krb5_keyblock structures,
+ * terminated by an entry with key type = 0.
+ * Each keyblock should have its contents freed in turn, and then the data
+ * item itself should be freed. */
+ krb5plugin_preauth_keys = 3,
+ /* The returned krb5_data_item holds the request structure, re-encoded
+ * using DER. Unless the client implementation is the same as the server
+ * implementation, there's a good chance that the result will not match
+ * what the client sent, so don't go creating any fatal errors if it
+ * doesn't match up. */
+ krb5plugin_preauth_request_body = 4
+};
+
+typedef krb5_error_code
+(*preauth_get_entry_data_proc)(krb5_context,
+ krb5_kdc_req *,
+ struct _krb5_db_entry_new *,
+ krb5_int32 request_type,
+ krb5_data **);
+
+/* Preauth plugin initialization function */
+typedef krb5_error_code
+(*preauth_server_init_proc)(krb5_context context,
+ void **plugin_context,
+ const char** realmnames);
+
+/* Preauth plugin cleanup function */
+typedef void
+(*preauth_server_fini_proc)(krb5_context context, void *plugin_context);
+
+/* Return the flags which the KDC should use for this module. This is a
+ * callback instead of a static value because the module may or may not
+ * wish to count itself as a hardware preauthentication module (in other
+ * words, the flags may be affected by the configuration, for example if a
+ * site administrator can force a particular preauthentication type to be
+ * supported using only hardware). This function is called for each entry
+ * entry in the server_pa_type_list. */
+typedef int
+(*preauth_server_flags_proc)(krb5_context context, krb5_preauthtype patype);
+
+/* Get preauthentication data to send to the client as part of the "you
+ * need to use preauthentication" error. The module doesn't need to
+ * actually provide data if the protocol doesn't require it, but it should
+ * return either zero or non-zero to control whether its padata type is
+ * included in the list which is sent back to the client. Is not allowed
+ * to create a context because we have no guarantee that the client will
+ * ever call again (or that it will hit this server if it does), in which
+ * case a context might otherwise hang around forever. */
+typedef krb5_error_code
+(*preauth_server_edata_proc)(krb5_context,
+ krb5_kdc_req *request,
+ struct _krb5_db_entry_new *client,
+ struct _krb5_db_entry_new *server,
+ preauth_get_entry_data_proc,
+ void *pa_module_context,
+ krb5_pa_data *data);
+
+/* Verify preauthentication data sent by the client, setting the
+ * TKT_FLG_PRE_AUTH or TKT_FLG_HW_AUTH flag in the enc_tkt_reply's "flags"
+ * field as appropriate, and returning nonzero on failure. Can create
+ * context data for consumption by the return_proc or freepa_proc below. */
+typedef krb5_error_code
+(*preauth_server_verify_proc)(krb5_context context,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_pa_data *data,
+ preauth_get_entry_data_proc,
+ void *pa_module_context,
+ void **pa_request_context,
+ krb5_data **e_data,
+ krb5_authdata ***authz_data);
+
+/* Generate preauthentication response data to send to the client as part
+ * of the AS-REP. If it needs to override the key which is used to encrypt
+ * the response, it can do so. The module is expected (but not required,
+ * if a preauth_server_free_reqcontext_proc is also provided) to free any
+ * context data it saved in "pa_request_context". */
+typedef krb5_error_code
+(*preauth_server_return_proc)(krb5_context context,
+ krb5_pa_data * padata,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ struct _krb5_key_data *client_keys,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc,
+ void *pa_module_context,
+ void **pa_request_context);
+
+/* Free up the server-side per-request context, in cases where
+ * server_return_proc() didn't or for whatever reason was not called.
+ * Can be NULL. */
+typedef krb5_error_code
+(*preauth_server_free_reqcontext_proc)(krb5_context,
+ void *pa_module_context,
+ void **request_pa_context);
/*
* The function table / structure which a preauth server module must export as
@@ -270,7 +425,7 @@ typedef struct krb5plugin_preauth_client_ftable_v0 {
* more arguments to callbacks, and in both cases we'll be able to wrap the v0
* functions.
*/
-typedef struct krb5plugin_preauth_server_ftable_v0 {
+typedef struct krb5plugin_preauth_server_ftable_v1 {
/* Not-usually-visible name. */
char *name;
@@ -281,8 +436,9 @@ typedef struct krb5plugin_preauth_server_ftable_v0 {
/* Per-plugin initialization/cleanup. The init function is called by the
* KDC when the plugin is loaded, and the fini function is called before
* the plugin is unloaded. Both are optional. */
- krb5_error_code (*init_proc)(krb5_context, void **);
- void (*fini_proc)(krb5_context, void *);
+ preauth_server_init_proc init_proc;
+ preauth_server_fini_proc fini_proc;
+
/* Return the flags which the KDC should use for this module. This is a
* callback instead of a static value because the module may or may not
* wish to count itself as a hardware preauthentication module (in other
@@ -290,7 +446,8 @@ typedef struct krb5plugin_preauth_server_ftable_v0 {
* site administrator can force a particular preauthentication type to be
* supported using only hardware). This function is called for each entry
* entry in the server_pa_type_list. */
- int (*flags_proc)(krb5_context, krb5_preauthtype);
+ preauth_server_flags_proc flags_proc;
+
/* Get preauthentication data to send to the client as part of the "you
* need to use preauthentication" error. The module doesn't need to
* actually provide data if the protocol doesn't require it, but it should
@@ -299,49 +456,27 @@ typedef struct krb5plugin_preauth_server_ftable_v0 {
* to create a context because we have no guarantee that the client will
* ever call again (or that it will hit this server if it does), in which
* case a context might otherwise hang around forever. */
- krb5_error_code (*edata_proc)(krb5_context, krb5_kdc_req *request,
- struct _krb5_db_entry_new *client,
- struct _krb5_db_entry_new *server,
- preauth_get_entry_data_proc,
- void *pa_module_context,
- krb5_pa_data *data);
+ preauth_server_edata_proc edata_proc;
+
/* Verify preauthentication data sent by the client, setting the
* TKT_FLG_PRE_AUTH or TKT_FLG_HW_AUTH flag in the enc_tkt_reply's "flags"
* field as appropriate, and returning nonzero on failure. Can create
* context data for consumption by the return_proc or freepa_proc below. */
- krb5_error_code (*verify_proc)(krb5_context,
- struct _krb5_db_entry_new *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request,
- krb5_enc_tkt_part *enc_tkt_reply,
- krb5_pa_data *data,
- preauth_get_entry_data_proc,
- void *pa_module_context,
- void **pa_request_context,
- krb5_data **e_data);
+ preauth_server_verify_proc verify_proc;
+
/* Generate preauthentication response data to send to the client as part
* of the AS-REP. If it needs to override the key which is used to encrypt
* the response, it can do so. The module is expected (but not required,
* if a freepa_proc is also provided) to free any context data it saved in
* "request_pa_context". */
- krb5_error_code (*return_proc)(krb5_context, krb5_pa_data * padata,
- struct _krb5_db_entry_new *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request,
- krb5_kdc_rep *reply,
- struct _krb5_key_data *client_keys,
- krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa,
- preauth_get_entry_data_proc,
- void *pa_module_context,
- void **pa_request_context);
+ preauth_server_return_proc return_proc;
+
/* Free up the server-side per-request context, in cases where
- * server_return_proc() didn't or for whatever reason was not called. Can
- * be NULL. */
- krb5_error_code (*freepa_reqcontext_proc)(krb5_context,
- void *pa_module_context,
- void **request_pa_context);
-} krb5plugin_preauth_server_ftable_v0;
+ * server_return_proc() didn't or for whatever reason was not called.
+ * Can be NULL. */
+ preauth_server_free_reqcontext_proc freepa_reqcontext_proc;
+
+} krb5plugin_preauth_server_ftable_v1;
/*
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 37bd9e3..afc7d52 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -85,6 +85,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
encrypting_key.contents = 0;
reply.padata = 0;
session_key.contents = 0;
+ enc_tkt_reply.authorization_data = NULL;
ktypes2str(ktypestr, sizeof(ktypestr),
request->nktypes, request->ktype);
@@ -465,6 +466,8 @@ errout:
}
}
+ if (enc_tkt_reply.authorization_data != NULL)
+ krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data);
if (encrypting_key.contents)
krb5_free_keyblock_contents(kdc_context, &encrypting_key);
if (reply.padata)
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 7d06d99..13a450e 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -77,54 +77,17 @@ typedef des_cblock mit_des_cblock;
extern void mit_des_fixup_key_parity (mit_des_cblock );
extern int mit_des_is_weak_key (mit_des_cblock );
-typedef krb5_error_code (*verify_proc)
- (krb5_context, krb5_db_entry *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
- preauth_get_entry_data_proc get_entry_data,
- void *pa_module_context,
- void **pa_request_context,
- krb5_data **e_data);
-
-typedef krb5_error_code (*edata_proc)
- (krb5_context, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- preauth_get_entry_data_proc get_entry_data,
- void *pa_module_context,
- krb5_pa_data *data);
-
-typedef krb5_error_code (*return_proc)
- (krb5_context, krb5_pa_data * padata,
- krb5_db_entry *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_key_data *client_key,
- krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa,
- preauth_get_entry_data_proc get_entry_data,
- void *pa_module_context,
- void **pa_request_context);
-
-typedef krb5_error_code (*freepa_proc)
- (krb5_context, void *pa_module_context, void **pa_request_context);
-
-typedef krb5_error_code (*init_proc)
- (krb5_context, void **);
-typedef void (*fini_proc)
- (krb5_context, void *);
-
typedef struct _krb5_preauth_systems {
const char *name;
int type;
int flags;
void *plugin_context;
- init_proc init;
- fini_proc fini;
- edata_proc get_edata;
- verify_proc verify_padata;
- return_proc return_padata;
- freepa_proc free_pa_request_context;
+ preauth_server_init_proc init;
+ preauth_server_fini_proc fini;
+ preauth_server_edata_proc get_edata;
+ preauth_server_verify_proc verify_padata;
+ preauth_server_return_proc return_padata;
+ preauth_server_free_reqcontext_proc free_pa_reqctx;
} krb5_preauth_systems;
static krb5_error_code verify_enc_timestamp
@@ -135,7 +98,8 @@ static krb5_error_code verify_enc_timestamp
preauth_get_entry_data_proc get_entry_data,
void *pa_system_context,
void **pa_request_context,
- krb5_data **e_data);
+ krb5_data **e_data,
+ krb5_authdata ***authz_data);
static krb5_error_code get_etype_info
(krb5_context, krb5_kdc_req *request,
@@ -203,7 +167,8 @@ static krb5_error_code verify_sam_response
preauth_get_entry_data_proc get_entry_data,
void *pa_module_context,
void **pa_request_context,
- krb5_data **e_data);
+ krb5_data **e_data,
+ krb5_authdata ***authz_data);
static krb5_error_code get_sam_edata
(krb5_context, krb5_kdc_req *request,
@@ -302,10 +267,11 @@ load_preauth_plugins(krb5_context context)
{
struct errinfo err;
void **preauth_plugins_ftables;
- struct krb5plugin_preauth_server_ftable_v0 *ftable;
+ struct krb5plugin_preauth_server_ftable_v1 *ftable;
int module_count, i, j, k;
void *plugin_context;
- init_proc server_init_proc = NULL;
+ preauth_server_init_proc server_init_proc = NULL;
+ char **kdc_realm_names = NULL;
memset(&err, 0, sizeof(err));
@@ -321,7 +287,7 @@ load_preauth_plugins(krb5_context context)
/* Get the method tables provided by the loaded plugins. */
preauth_plugins_ftables = NULL;
if (krb5int_get_plugin_dir_data(&preauth_plugins,
- "preauthentication_server_0",
+ "preauthentication_server_1",
&preauth_plugins_ftables, &err) != 0) {
return KRB5_PLUGIN_NO_HANDLE;
}
@@ -355,6 +321,18 @@ load_preauth_plugins(krb5_context context)
return ENOMEM;
}
+ /* Build a list of the names of the supported realms for this KDC.
+ * The list of names is terminated with a NULL. */
+ kdc_realm_names = malloc(sizeof(char *) * (kdc_numrealms + 1));
+ if (kdc_realm_names == NULL) {
+ krb5int_free_plugin_dir_data(preauth_plugins_ftables);
+ return ENOMEM;
+ }
+ for (i = 0; i < kdc_numrealms; i++) {
+ kdc_realm_names[i] = kdc_realmlist[i]->realm_name;
+ }
+ kdc_realm_names[i] = NULL;
+
/* Add the locally-supplied mechanisms to the dynamic list first. */
for (i = 0, k = 0;
i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
@@ -367,7 +345,7 @@ load_preauth_plugins(krb5_context context)
plugin_context = NULL;
server_init_proc = static_preauth_systems[i].init;
if ((server_init_proc != NULL) &&
- ((*server_init_proc)(context, &plugin_context) != 0)) {
+ ((*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names) != 0)) {
memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
continue;
}
@@ -396,7 +374,7 @@ load_preauth_plugins(krb5_context context)
server_init_proc = ftable->init_proc;
if (server_init_proc != NULL) {
krb5_error_code initerr;
- initerr = (*server_init_proc)(context, &plugin_context);
+ initerr = (*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names);
if (initerr) {
const char *emsg;
emsg = krb5_get_error_message(context, initerr);
@@ -428,13 +406,14 @@ load_preauth_plugins(krb5_context context)
preauth_systems[k].get_edata = ftable->edata_proc;
preauth_systems[k].verify_padata = ftable->verify_proc;
preauth_systems[k].return_padata = ftable->return_proc;
- preauth_systems[k].free_pa_request_context =
+ preauth_systems[k].free_pa_reqctx =
ftable->freepa_reqcontext_proc;
k++;
}
}
krb5int_free_plugin_dir_data(preauth_plugins_ftables);
}
+ free(kdc_realm_names);
n_preauth_systems = k;
/* Add the end-of-list marker. */
preauth_systems[k].name = "[end]";
@@ -527,10 +506,9 @@ free_padata_context(krb5_context kcontext, void **padata_context)
if (context->contexts[i].pa_context != NULL) {
preauth_system = context->contexts[i].pa_system;
mctx = preauth_system->plugin_context;
- if (preauth_system->free_pa_request_context != NULL) {
+ if (preauth_system->free_pa_reqctx != NULL) {
pctx = &context->contexts[i].pa_context;
- (*preauth_system->free_pa_request_context)(kcontext, mctx,
- pctx);
+ (*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx);
}
context->contexts[i].pa_context = NULL;
}
@@ -878,6 +856,55 @@ errout:
}
/*
+ * Add authorization data returned from preauth modules to the ticket
+ * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
+ */
+static krb5_error_code
+add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
+{
+ krb5_authdata **newad;
+ int oldones, newones;
+ int i;
+
+ if (enc_tkt_part == NULL || ad == NULL)
+ return EINVAL;
+
+ for (newones = 0; ad[newones] != NULL; newones++);
+ if (newones == 0)
+ return 0; /* nothing to add */
+
+ if (enc_tkt_part->authorization_data == NULL)
+ oldones = 0;
+ else
+ for (oldones = 0;
+ enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
+
+ newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
+ if (newad == NULL)
+ return ENOMEM;
+
+ /* Copy any existing pointers */
+ for (i = 0; i < oldones; i++)
+ newad[i] = enc_tkt_part->authorization_data[i];
+
+ /* Add the new ones */
+ for (i = 0; i < newones; i++)
+ newad[oldones+i] = ad[i];
+
+ /* Terminate the new list */
+ newad[oldones+i] = NULL;
+
+ /* Free any existing list */
+ if (enc_tkt_part->authorization_data != NULL)
+ free(enc_tkt_part->authorization_data);
+
+ /* Install our new list */
+ enc_tkt_part->authorization_data = newad;
+
+ return 0;
+}
+
+/*
* This routine is called to verify the preauthentication information
* for a V5 request.
*
@@ -899,6 +926,7 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
krb5_error_code saved_retval = 0;
int use_saved_retval = 0;
const char *emsg;
+ krb5_authdata **tmp_authz_data = NULL;
if (request->padata == 0)
return 0;
@@ -927,12 +955,17 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
retval = pa_sys->verify_padata(context, client, req_pkt, request,
enc_tkt_reply, *padata,
get_entry_data, pa_sys->plugin_context,
- pa_context, &tmp_e_data);
+ pa_context, &tmp_e_data, &tmp_authz_data);
if (retval) {
emsg = krb5_get_error_message (context, retval);
krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
pa_sys->name, emsg);
krb5_free_error_message (context, emsg);
+ /* Ignore authorization data returned from modules that fail */
+ if (tmp_authz_data != NULL) {
+ krb5_free_authdata(context, tmp_authz_data);
+ tmp_authz_data = NULL;
+ }
if (pa_sys->flags & PA_REQUIRED) {
/* free up any previous edata we might have been saving */
if (pa_e_data != NULL)
@@ -971,6 +1004,12 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
krb5_free_data(context, tmp_e_data);
tmp_e_data = NULL;
}
+ /* Add any authorization data to the ticket */
+ if (tmp_authz_data != NULL) {
+ add_authorization_data(enc_tkt_reply, tmp_authz_data);
+ free(tmp_authz_data);
+ tmp_authz_data = NULL;
+ }
pa_ok = 1;
if (pa_sys->flags & PA_SUFFICIENT)
break;
@@ -1188,7 +1227,8 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
preauth_get_entry_data_proc ets_get_entry_data,
void *pa_system_context,
void **pa_request_context,
- krb5_data **e_data)
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
{
krb5_pa_enc_ts * pa_enc = 0;
krb5_error_code retval;
@@ -2176,7 +2216,8 @@ verify_sam_response(krb5_context context, krb5_db_entry *client,
preauth_get_entry_data_proc sam_get_entry_data,
void *pa_system_context,
void **pa_request_context,
- krb5_data **e_data)
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
{
krb5_error_code retval;
krb5_data scratch;
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index 6c20147..33b0f17 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.c
+++ b/src/lib/krb5/asn.1/asn1_k_decode.c
@@ -160,6 +160,28 @@
} else { len = 0; var = 0; }
/*
+ * Deal with implicitly tagged fields
+ */
+#define get_implicit_octet_string(len, var, tagexpect) \
+ if (tagnum != (tagexpect)) return ASN1_MISSING_FIELD; \
+ if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE) \
+ return ASN1_BAD_ID; \
+ retval = asn1buf_remove_octetstring(&subbuf, taglen, &(var)); \
+ if (retval) return retval; \
+ (len) = taglen; \
+ next_tag()
+
+#define opt_implicit_octet_string(len, var, tagexpect) \
+ if (tagnum == (tagexpect)) { \
+ if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE) \
+ return ASN1_BAD_ID; \
+ retval = asn1buf_remove_octetstring(&subbuf, taglen, &(var)); \
+ if (retval) return retval; \
+ (len) = taglen; \
+ next_tag(); \
+ } else { (len) = 0; (var) = NULL; }
+
+/*
* begin_structure
*
* Declares some variables for decoding SEQUENCE types. This is meant
@@ -176,6 +198,22 @@
if (retval) return retval; \
next_tag()
+/*
+ * This is used for structures which have no tagging.
+ * It is the same as begin_structure() except next_tag()
+ * is not called.
+ */
+#define begin_structure_no_tag() \
+ asn1buf subbuf; \
+ int seqindef; \
+ int indef; \
+ unused_var(taglen); \
+ unused_var(construction); \
+ retval = asn1_get_sequence(buf, &length, &seqindef); \
+ if (retval) return retval; \
+ retval = asn1buf_imbed(&subbuf, buf, length, seqindef); \
+ if (retval) return retval
+
/* skip trailing garbage */
#define end_structure() \
retval = asn1buf_sync(buf, &subbuf, asn1class, tagnum, \
@@ -183,6 +221,37 @@
if (retval) return retval
/*
+ * begin_choice
+ *
+ * Declares some variables for decoding CHOICE types. This is meant
+ * to be called in an inner block that ends with a call to
+ * end_choice().
+ */
+#define begin_choice() \
+ asn1buf subbuf; \
+ int seqindef; \
+ int indef; \
+ taginfo t; \
+ retval = asn1_get_tag_2(buf, &t); \
+ if (retval) return retval; \
+ tagnum = t.tagnum; \
+ taglen = t.length; \
+ indef = t.indef; \
+ length = t.length; \
+ seqindef = t.indef; \
+ asn1class = t.asn1class; \
+ construction = t.construction; \
+ retval = asn1buf_imbed(&subbuf, buf, length, seqindef); \
+ if (retval) return retval
+
+/* skip trailing garbage */
+#define end_choice() \
+ length -= t.length; \
+ retval = asn1buf_sync(buf, &subbuf, t.asn1class, t.tagnum, \
+ length, t.indef, seqindef); \
+ if (retval) return retval
+
+/*
* sequence_of
*
* Declares some variables for decoding SEQUENCE OF types. This is
@@ -1094,3 +1163,387 @@ asn1_error_code asn1_decode_predicted_sam_response(asn1buf *buf, krb5_predicted_
}
cleanup();
}
+
+/* PKINIT */
+
+asn1_error_code asn1_decode_external_principal_identifier(asn1buf *buf, krb5_external_principal_identifier *val)
+{
+ setup();
+ {
+ begin_structure();
+ opt_implicit_octet_string(val->subjectName.length, val->subjectName.data, 0);
+ opt_implicit_octet_string(val->issuerAndSerialNumber.length, val->issuerAndSerialNumber.data, 1);
+ opt_implicit_octet_string(val->subjectKeyIdentifier.length, val->subjectKeyIdentifier.data, 2);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_sequence_of_external_principal_identifier(asn1buf *buf, krb5_external_principal_identifier ***val)
+{
+ decode_array_body(krb5_external_principal_identifier,asn1_decode_external_principal_identifier);
+}
+
+asn1_error_code asn1_decode_pa_pk_as_req(asn1buf *buf, krb5_pa_pk_as_req *val)
+{
+ setup();
+ {
+ begin_structure();
+ get_implicit_octet_string(val->signedAuthPack.length, val->signedAuthPack.data, 0);
+ opt_field(val->trustedCertifiers, 1, asn1_decode_sequence_of_external_principal_identifier, NULL);
+ opt_implicit_octet_string(val->kdcPkId.length, val->kdcPkId.data, 2);
+ end_structure();
+ }
+ cleanup();
+}
+
+#if 0 /* XXX This needs to be tested!!! XXX */
+asn1_error_code asn1_decode_trusted_ca(asn1buf *buf, krb5_trusted_ca *val)
+{
+ setup();
+ {
+ char *start, *end;
+ size_t alloclen;
+
+ begin_explicit_choice();
+ if (t.tagnum == choice_trusted_cas_principalName) {
+ val->choice = choice_trusted_cas_principalName;
+ } else if (t.tagnum == choice_trusted_cas_caName) {
+ val->choice = choice_trusted_cas_caName;
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->u.caName.data = malloc(alloclen);
+ if (val->u.caName.data == NULL)
+ return ENOMEM;
+ memcpy(val->u.caName.data, start, alloclen);
+ val->u.caName.length = alloclen;
+ next_tag();
+ } else if (t.tagnum == choice_trusted_cas_issuerAndSerial) {
+ val->choice = choice_trusted_cas_issuerAndSerial;
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->u.issuerAndSerial.data = malloc(alloclen);
+ if (val->u.issuerAndSerial.data == NULL)
+ return ENOMEM;
+ memcpy(val->u.issuerAndSerial.data, start, alloclen);
+ val->u.issuerAndSerial.length = alloclen;
+ next_tag();
+ } else return ASN1_BAD_ID;
+ end_explicit_choice();
+ }
+ cleanup();
+}
+#else
+asn1_error_code asn1_decode_trusted_ca(asn1buf *buf, krb5_trusted_ca *val)
+{
+ setup();
+ { begin_choice();
+ if (tagnum == choice_trusted_cas_principalName) {
+ val->choice = choice_trusted_cas_principalName;
+ asn1_decode_krb5_principal_name(&subbuf, &(val->u.principalName));
+ } else if (tagnum == choice_trusted_cas_caName) {
+ val->choice = choice_trusted_cas_caName;
+ get_implicit_octet_string(val->u.caName.length, val->u.caName.data, choice_trusted_cas_caName);
+ } else if (tagnum == choice_trusted_cas_issuerAndSerial) {
+ val->choice = choice_trusted_cas_issuerAndSerial;
+ get_implicit_octet_string(val->u.issuerAndSerial.length, val->u.issuerAndSerial.data,
+ choice_trusted_cas_issuerAndSerial);
+ } else return ASN1_BAD_ID;
+ end_choice();
+ }
+ cleanup();
+}
+#endif
+
+asn1_error_code asn1_decode_sequence_of_trusted_ca(asn1buf *buf, krb5_trusted_ca ***val)
+{
+ decode_array_body(krb5_trusted_ca, asn1_decode_trusted_ca);
+}
+
+asn1_error_code asn1_decode_pa_pk_as_req_draft9(asn1buf *buf, krb5_pa_pk_as_req_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ get_implicit_octet_string(val->signedAuthPack.length, val->signedAuthPack.data, 0);
+ opt_field(val->trustedCertifiers, 1, asn1_decode_sequence_of_trusted_ca, NULL);
+ opt_lenfield(val->kdcCert.length, val->kdcCert.data, 2, asn1_decode_octetstring);
+ opt_lenfield(val->encryptionCert.length, val->encryptionCert.data, 2, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_dh_rep_info(asn1buf *buf, krb5_dh_rep_info *val)
+{
+ setup();
+ { begin_structure();
+ get_implicit_octet_string(val->dhSignedData.length, val->dhSignedData.data, 0);
+
+ opt_lenfield(val->serverDHNonce.length, val->serverDHNonce.data, 1, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_pk_authenticator(asn1buf *buf, krb5_pk_authenticator *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->cusec, 0, asn1_decode_int32);
+ get_field(val->ctime, 1, asn1_decode_kerberos_time);
+ get_field(val->nonce, 2, asn1_decode_int32);
+ opt_lenfield(val->paChecksum.length, val->paChecksum.contents, 3, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_pk_authenticator_draft9(asn1buf *buf, krb5_pk_authenticator_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ alloc_field(val->kdcName,krb5_principal_data);
+ get_field(val->kdcName, 0, asn1_decode_principal_name);
+ get_field(val->kdcName, 1, asn1_decode_realm);
+ get_field(val->cusec, 2, asn1_decode_int32);
+ get_field(val->ctime, 3, asn1_decode_kerberos_time);
+ get_field(val->nonce, 4, asn1_decode_int32);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_algorithm_identifier(asn1buf *buf, krb5_algorithm_identifier *val) {
+
+ setup();
+ { begin_structure_no_tag();
+ retval = asn1_decode_oid(&subbuf, &val->algorithm.length,
+ &val->algorithm.data);
+ if(retval) return retval;
+ val->parameters.length = 0;
+ val->parameters.data = NULL;
+
+ if(length > subbuf.next - subbuf.base) {
+ unsigned int size = length - (subbuf.next - subbuf.base);
+ retval = asn1buf_remove_octetstring(&subbuf, size,
+ &val->parameters.data);
+ if(retval) return retval;
+ val->parameters.length = size;
+ }
+
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_subject_pk_info(asn1buf *buf, krb5_subject_pk_info *val)
+{
+ asn1_octet unused;
+ setup();
+ { begin_structure_no_tag();
+
+ retval = asn1_decode_algorithm_identifier(&subbuf, &val->algorithm);
+ if (retval) return retval;
+
+ /* SubjectPublicKey encoded as a BIT STRING */
+ next_tag();
+ if (asn1class != UNIVERSAL || construction != PRIMITIVE ||
+ tagnum != ASN1_BITSTRING)
+ return ASN1_BAD_ID;
+
+ retval = asn1buf_remove_octet(&subbuf, &unused);
+ if(retval) return retval;
+
+ /* Number of unused bits must be between 0 and 7. */
+ /* What to do if unused is not zero? */
+ if (unused > 7) return ASN1_BAD_FORMAT;
+ taglen--;
+
+ val->subjectPublicKey.length = 0;
+ val->subjectPublicKey.data = NULL;
+ retval = asn1buf_remove_octetstring(&subbuf, taglen,
+ &val->subjectPublicKey.data);
+ if(retval) return retval;
+ val->subjectPublicKey.length = taglen;
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_sequence_of_algorithm_identifier(asn1buf *buf, krb5_algorithm_identifier ***val)
+{
+ decode_array_body(krb5_algorithm_identifier, asn1_decode_algorithm_identifier);
+}
+
+asn1_error_code asn1_decode_kdc_dh_key_info (asn1buf *buf, krb5_kdc_dh_key_info *val)
+{
+ setup();
+ { begin_structure();
+ retval = asn1buf_remove_octetstring(&subbuf, taglen, &val->subjectPublicKey.data);
+ if(retval) return retval;
+ val->subjectPublicKey.length = taglen;
+ next_tag();
+ get_field(val->nonce, 1, asn1_decode_int32);
+ opt_field(val->dhKeyExpiration, 2, asn1_decode_kerberos_time, 0);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_reply_key_pack (asn1buf *buf, krb5_reply_key_pack *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->replyKey, 0, asn1_decode_encryption_key);
+ get_field(val->asChecksum, 1, asn1_decode_checksum);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_reply_key_pack_draft9 (asn1buf *buf, krb5_reply_key_pack_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->replyKey, 0, asn1_decode_encryption_key);
+ get_field(val->nonce, 1, asn1_decode_int32);
+ end_structure();
+ }
+ cleanup();
+}
+
+
+asn1_error_code asn1_decode_krb5_principal_name (asn1buf *buf, krb5_principal *val)
+{
+ setup();
+ { begin_structure();
+ get_field(*val, 0, asn1_decode_realm);
+ get_field(*val, 1, asn1_decode_principal_name);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_auth_pack(asn1buf *buf, krb5_auth_pack *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->pkAuthenticator, 0, asn1_decode_pk_authenticator);
+ if (tagnum == 1) { alloc_field(val->clientPublicValue, krb5_subject_pk_info); }
+ /* can't call opt_field because it does decoder(&subbuf, &(val)); */
+ if (asn1buf_remains(&subbuf, seqindef)) {
+ if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
+ && (tagnum || taglen || asn1class != UNIVERSAL))
+ return ASN1_BAD_ID;
+ if (tagnum == 1) {
+ retval = asn1_decode_subject_pk_info(&subbuf,
+ val->clientPublicValue);
+ if (!taglen && indef) { get_eoc(); }
+ next_tag();
+ } else val->clientPublicValue = NULL;
+ }
+ /* can't call opt_field because it does decoder(&subbuf, &(val)); */
+ if (asn1buf_remains(&subbuf, seqindef)) {
+ if (tagnum == 2) {
+ asn1_decode_sequence_of_algorithm_identifier(&subbuf, &val->supportedCMSTypes);
+ if (!taglen && indef) { get_eoc(); }
+ next_tag();
+ } else val->supportedCMSTypes = NULL;
+ }
+ opt_lenfield(val->clientDHNonce.length, val->clientDHNonce.data, 3, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_auth_pack_draft9(asn1buf *buf, krb5_auth_pack_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->pkAuthenticator, 0, asn1_decode_pk_authenticator_draft9);
+ if (tagnum == 1) {
+ alloc_field(val->clientPublicValue, krb5_subject_pk_info);
+ /* can't call opt_field because it does decoder(&subbuf, &(val)); */
+ if (asn1buf_remains(&subbuf, seqindef)) {
+ if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
+ && (tagnum || taglen || asn1class != UNIVERSAL))
+ return ASN1_BAD_ID;
+ if (tagnum == 1) {
+ retval = asn1_decode_subject_pk_info(&subbuf,
+ val->clientPublicValue);
+ if (!taglen && indef) { get_eoc(); }
+ next_tag();
+ } else val->clientPublicValue = NULL;
+ }
+ }
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_pa_pk_as_rep(asn1buf *buf, krb5_pa_pk_as_rep *val)
+{
+ setup();
+ { begin_choice();
+ if (tagnum == choice_pa_pk_as_rep_dhInfo) {
+ val->choice = choice_pa_pk_as_rep_dhInfo;
+ get_field_body(val->u.dh_Info, asn1_decode_dh_rep_info);
+ } else if (tagnum == choice_pa_pk_as_rep_encKeyPack) {
+ val->choice = choice_pa_pk_as_rep_encKeyPack;
+ get_implicit_octet_string(val->u.encKeyPack.length, val->u.encKeyPack.data,
+ choice_pa_pk_as_rep_encKeyPack);
+ } else {
+ val->choice = choice_pa_pk_as_rep_UNKNOWN;
+ }
+ end_choice();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_pa_pk_as_rep_draft9(asn1buf *buf, krb5_pa_pk_as_rep_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ if (tagnum == choice_pa_pk_as_rep_draft9_dhSignedData) {
+ val->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
+ get_lenfield(val->u.dhSignedData.length, val->u.dhSignedData.data,
+ choice_pa_pk_as_rep_draft9_dhSignedData, asn1_decode_octetstring);
+ } else if (tagnum == choice_pa_pk_as_rep_draft9_encKeyPack) {
+ val->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+ get_lenfield(val->u.encKeyPack.length, val->u.encKeyPack.data,
+ choice_pa_pk_as_rep_draft9_encKeyPack, asn1_decode_octetstring);
+ } else {
+ val->choice = choice_pa_pk_as_rep_UNKNOWN;
+ }
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_sequence_of_typed_data(asn1buf *buf, krb5_typed_data ***val)
+{
+ decode_array_body(krb5_typed_data,asn1_decode_typed_data);
+}
+
+asn1_error_code asn1_decode_typed_data(asn1buf *buf, krb5_typed_data *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->type,0,asn1_decode_int32);
+ get_lenfield(val->length,val->data,1,asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h
index 22e43fd..72c4e29 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.h
+++ b/src/lib/krb5/asn.1/asn1_k_decode.h
@@ -159,6 +159,44 @@ asn1_error_code asn1_decode_sam_response_2
(asn1buf *buf, krb5_sam_response_2 *val);
asn1_error_code asn1_decode_predicted_sam_response
(asn1buf *buf, krb5_predicted_sam_response *val);
+asn1_error_code asn1_decode_external_principal_identifier
+ (asn1buf *buf, krb5_external_principal_identifier *val);
+asn1_error_code asn1_decode_pa_pk_as_req
+ (asn1buf *buf, krb5_pa_pk_as_req *val);
+asn1_error_code asn1_decode_trusted_ca
+ (asn1buf *buf, krb5_trusted_ca *val);
+asn1_error_code asn1_decode_pa_pk_as_req_draft9
+ (asn1buf *buf, krb5_pa_pk_as_req_draft9 *val);
+asn1_error_code asn1_decode_dh_rep_info
+ (asn1buf *buf, krb5_dh_rep_info *val);
+asn1_error_code asn1_decode_pk_authenticator
+ (asn1buf *buf, krb5_pk_authenticator *val);
+asn1_error_code asn1_decode_pk_authenticator_draft9
+ (asn1buf *buf, krb5_pk_authenticator_draft9 *val);
+asn1_error_code asn1_decode_subject_pk_info
+ (asn1buf *buf, krb5_subject_pk_info *val);
+asn1_error_code asn1_decode_algorithm_identifier
+ (asn1buf *buf, krb5_algorithm_identifier *val);
+asn1_error_code asn1_decode_auth_pack
+ (asn1buf *buf, krb5_auth_pack *val);
+asn1_error_code asn1_decode_auth_pack_draft9
+ (asn1buf *buf, krb5_auth_pack_draft9 *val);
+asn1_error_code asn1_decode_pa_pk_as_rep
+ (asn1buf *buf, krb5_pa_pk_as_rep *val);
+asn1_error_code asn1_decode_pa_pk_as_rep_draft9
+ (asn1buf *buf, krb5_pa_pk_as_rep_draft9 *val);
+asn1_error_code asn1_decode_kdc_dh_key_info
+ (asn1buf *buf, krb5_kdc_dh_key_info *val);
+asn1_error_code asn1_decode_krb5_principal_name
+ (asn1buf *buf, krb5_principal *val);
+asn1_error_code asn1_decode_reply_key_pack
+ (asn1buf *buf, krb5_reply_key_pack *val);
+asn1_error_code asn1_decode_reply_key_pack_draft9
+ (asn1buf *buf, krb5_reply_key_pack_draft9 *val);
+asn1_error_code asn1_decode_sequence_of_typed_data
+ (asn1buf *buf, krb5_typed_data ***val);
+asn1_error_code asn1_decode_typed_data
+ (asn1buf *buf, krb5_typed_data *val);
/* arrays */
asn1_error_code asn1_decode_authorization_data
@@ -187,6 +225,11 @@ asn1_error_code asn1_decode_etype_info
(asn1buf *buf, krb5_etype_info_entry ***val);
asn1_error_code asn1_decode_etype_info2
(asn1buf *buf, krb5_etype_info_entry ***val, krb5_boolean v1_3_behavior);
-
+asn1_error_code asn1_decode_sequence_of_external_principal_identifier
+ (asn1buf *buf, krb5_external_principal_identifier ***val);
+asn1_error_code asn1_decode_sequence_of_trusted_ca
+ (asn1buf *buf, krb5_trusted_ca ***val);
+asn1_error_code asn1_decode_sequence_of_algorithm_identifier
+ (asn1buf *buf, krb5_algorithm_identifier ***val);
#endif
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 00cfab0..4869ea7 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -103,6 +103,50 @@
return retval; }\
sum += length; }
+/* asn1_addfield_implicit -- add an implicitly tagged field, or component, to the encoding */
+#define asn1_addfield_implicit(value,tag,encoder)\
+{ retval = encoder(buf,value,&length);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length;\
+ retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,length,&length); \
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length; }
+
+/* asn1_insert_implicit_octetstring -- add an octet string with implicit tagging */
+#define asn1_insert_implicit_octetstring(len,value,tag)\
+{ retval = asn1buf_insert_octetstring(buf,len,value);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += len;\
+ retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,len,&length); \
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length; }
+
+/* asn1_insert_implicit_bitstring -- add a bitstring with implicit tagging */
+#define asn1_insert_implicit_bitstring(len,value,tag)\
+{ retval = asn1buf_insert_octetstring(buf,len,value);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += len;\
+ retval = asn1buf_insert_octet(buf, 0);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum++;\
+ retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,tag,len+1,&length); \
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length; }
+
/* form a sequence (by adding a sequence header to the current encoding) */
#define asn1_makeseq()\
retval = asn1_make_sequence(buf,sum,&length);\
@@ -959,3 +1003,393 @@ asn1_error_code asn1_encode_krb_saved_safe_body(asn1buf *buf, const krb5_data *b
*retlen = body->length;
return 0;
}
+
+/*
+ * PKINIT
+ */
+
+asn1_error_code asn1_encode_pk_authenticator(asn1buf *buf, const krb5_pk_authenticator *val, unsigned int *retlen)
+{
+ asn1_setup();
+ asn1_addlenfield(val->paChecksum.length, val->paChecksum.contents, 3, asn1_encode_octetstring);
+ asn1_addfield(val->nonce, 2, asn1_encode_integer);
+ asn1_addfield(val->ctime, 1, asn1_encode_kerberos_time);
+ asn1_addfield(val->cusec, 0, asn1_encode_integer);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pk_authenticator_draft9(asn1buf *buf, const krb5_pk_authenticator_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_addfield(val->nonce, 4, asn1_encode_integer);
+ asn1_addfield(val->ctime, 3, asn1_encode_kerberos_time);
+ asn1_addfield(val->cusec, 2, asn1_encode_integer);
+ asn1_addfield(val->kdcName, 1, asn1_encode_realm);
+ asn1_addfield(val->kdcName, 0, asn1_encode_principal_name);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+
+asn1_error_code asn1_encode_algorithm_identifier(asn1buf *buf, const krb5_algorithm_identifier *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->parameters.length != 0) {
+ retval = asn1buf_insert_octetstring(buf, val->parameters.length,
+ val->parameters.data);
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += val->parameters.length;
+ }
+
+ retval = asn1_encode_oid(buf, val->algorithm.length,
+ val->algorithm.data,
+ &length);
+
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_subject_pk_info(asn1buf *buf, const krb5_subject_pk_info *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,ASN1_BITSTRING);
+
+ if (val->algorithm.parameters.length != 0) {
+ retval = asn1buf_insert_octetstring(buf, val->algorithm.parameters.length,
+ val->algorithm.parameters.data);
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += val->algorithm.parameters.length;
+ }
+
+ retval = asn1_encode_oid(buf, val->algorithm.algorithm.length,
+ val->algorithm.algorithm.data,
+ &length);
+
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ retval = asn1_make_etag(buf, UNIVERSAL, ASN1_SEQUENCE,
+ val->algorithm.parameters.length + length,
+ &length);
+
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_sequence_of_algorithm_identifier(asn1buf *buf, const krb5_algorithm_identifier **val, unsigned int *retlen)
+{
+ asn1_setup();
+ int i;
+
+ if(val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+
+ for(i=0; val[i] != NULL; i++);
+ for(i--; i>=0; i--){
+ retval = asn1_encode_algorithm_identifier(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_auth_pack(asn1buf *buf, const krb5_auth_pack *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->clientDHNonce.length != 0)
+ asn1_addlenfield(val->clientDHNonce.length, val->clientDHNonce.data, 3, asn1_encode_octetstring);
+ if (val->supportedCMSTypes != NULL)
+ asn1_addfield((const krb5_algorithm_identifier **)val->supportedCMSTypes,2,asn1_encode_sequence_of_algorithm_identifier);
+ if (val->clientPublicValue != NULL)
+ asn1_addfield(val->clientPublicValue,1,asn1_encode_subject_pk_info);
+ asn1_addfield(&(val->pkAuthenticator),0,asn1_encode_pk_authenticator);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_auth_pack_draft9(asn1buf *buf, const krb5_auth_pack_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->clientPublicValue != NULL)
+ asn1_addfield(val->clientPublicValue, 1, asn1_encode_subject_pk_info);
+ asn1_addfield(&(val->pkAuthenticator), 0, asn1_encode_pk_authenticator_draft9);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_external_principal_identifier(asn1buf *buf, const krb5_external_principal_identifier *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ /* Verify there is something to encode */
+ if (val->subjectKeyIdentifier.length == 0 && val->issuerAndSerialNumber.length == 0 && val->subjectName.length == 0)
+ return ASN1_MISSING_FIELD;
+
+ if (val->subjectKeyIdentifier.length != 0)
+ asn1_insert_implicit_octetstring(val->subjectKeyIdentifier.length,val->subjectKeyIdentifier.data,2);
+
+ if (val->issuerAndSerialNumber.length != 0)
+ asn1_insert_implicit_octetstring(val->issuerAndSerialNumber.length,val->issuerAndSerialNumber.data,1);
+
+ if (val->subjectName.length != 0)
+ asn1_insert_implicit_octetstring(val->subjectName.length,val->subjectName.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_sequence_of_external_principal_identifier(asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen)
+{
+ asn1_setup();
+ int i;
+
+ if(val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+
+ for(i=0; val[i] != NULL; i++);
+ for(i--; i>=0; i--){
+ retval = asn1_encode_external_principal_identifier(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_req(asn1buf *buf, const krb5_pa_pk_as_req *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->kdcPkId.length != 0)
+ asn1_insert_implicit_octetstring(val->kdcPkId.length,val->kdcPkId.data,2);
+
+ if (val->trustedCertifiers != NULL)
+ asn1_addfield((const krb5_external_principal_identifier **)val->trustedCertifiers,1,asn1_encode_sequence_of_external_principal_identifier);
+
+ asn1_insert_implicit_octetstring(val->signedAuthPack.length,val->signedAuthPack.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_trusted_ca(asn1buf *buf, const krb5_trusted_ca *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ switch (val->choice) {
+ case choice_trusted_cas_issuerAndSerial:
+ asn1_insert_implicit_octetstring(val->u.issuerAndSerial.length,val->u.issuerAndSerial.data,2);
+ break;
+ case choice_trusted_cas_caName:
+ asn1_insert_implicit_octetstring(val->u.caName.length,val->u.caName.data,1);
+ break;
+ case choice_trusted_cas_principalName:
+ asn1_addfield_implicit(val->u.principalName,0,asn1_encode_principal_name);
+ break;
+ default:
+ return ASN1_MISSING_FIELD;
+ }
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_sequence_of_trusted_ca(asn1buf *buf, const krb5_trusted_ca **val, unsigned int *retlen)
+{
+ asn1_setup();
+ int i;
+
+ if(val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+
+ for(i=0; val[i] != NULL; i++);
+ for(i--; i>=0; i--){
+ retval = asn1_encode_trusted_ca(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_req_draft9(asn1buf *buf, const krb5_pa_pk_as_req_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->encryptionCert.length != 0)
+ asn1_insert_implicit_octetstring(val->encryptionCert.length,val->encryptionCert.data,3);
+
+ if (val->kdcCert.length != 0)
+ asn1_insert_implicit_octetstring(val->kdcCert.length,val->kdcCert.data,2);
+
+ if (val->trustedCertifiers != NULL)
+ asn1_addfield((const krb5_trusted_ca **)val->trustedCertifiers,1,asn1_encode_sequence_of_trusted_ca);
+
+ asn1_insert_implicit_octetstring(val->signedAuthPack.length,val->signedAuthPack.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_dh_rep_info(asn1buf *buf, const krb5_dh_rep_info *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->serverDHNonce.length != 0)
+ asn1_insert_implicit_octetstring(val->serverDHNonce.length,val->serverDHNonce.data,1);
+
+ asn1_insert_implicit_octetstring(val->dhSignedData.length,val->dhSignedData.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_kdc_dh_key_info(asn1buf *buf, const krb5_kdc_dh_key_info *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->dhKeyExpiration != 0)
+ asn1_addfield(val->dhKeyExpiration, 2, asn1_encode_kerberos_time);
+ asn1_addfield(val->nonce, 1, asn1_encode_integer);
+
+ asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,3);
+ retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
+ val->subjectPublicKey.length + 1 + length,
+ &length);
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_reply_key_pack(asn1buf *buf, const krb5_reply_key_pack *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_addfield(&(val->asChecksum), 1, asn1_encode_checksum);
+ asn1_addfield(&(val->replyKey), 0, asn1_encode_encryption_key);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_reply_key_pack_draft9(asn1buf *buf, const krb5_reply_key_pack_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_addfield(val->nonce, 1, asn1_encode_integer);
+ asn1_addfield(&(val->replyKey), 0, asn1_encode_encryption_key);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_rep(asn1buf *buf, const krb5_pa_pk_as_rep *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ switch (val->choice)
+ {
+ case choice_pa_pk_as_rep_dhInfo:
+ asn1_addfield(&(val->u.dh_Info), choice_pa_pk_as_rep_dhInfo, asn1_encode_dh_rep_info);
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ asn1_insert_implicit_octetstring(val->u.encKeyPack.length,val->u.encKeyPack.data,1);
+ break;
+ default:
+ return ASN1_MISSING_FIELD;
+ }
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_rep_draft9(asn1buf *buf, const krb5_pa_pk_as_rep_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ switch (val->choice)
+ {
+ case choice_pa_pk_as_rep_draft9_dhSignedData:
+ asn1_insert_implicit_octetstring(val->u.dhSignedData.length,val->u.dhSignedData.data,0);
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ asn1_insert_implicit_octetstring(val->u.encKeyPack.length,val->u.encKeyPack.data,1);
+ break;
+ default:
+ return ASN1_MISSING_FIELD;
+ }
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_td_trusted_certifiers(asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen)
+{
+ asn1_setup();
+ retval = asn1_encode_sequence_of_external_principal_identifier(buf, val, &length);
+ if (retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_sequence_of_typed_data(asn1buf *buf, const krb5_typed_data **val, unsigned int *retlen)
+{
+ asn1_setup();
+ int i;
+
+ if(val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+
+ for(i=0; val[i] != NULL; i++);
+ for(i--; i>=0; i--){
+ retval = asn1_encode_typed_data(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_typed_data(asn1buf *buf, const krb5_typed_data *val, unsigned int *retlen)
+{
+ asn1_setup();
+ asn1_addlenfield(val->length, val->data, 1, asn1_encode_octetstring);
+ asn1_addfield(val->type, 0, asn1_encode_integer);
+ asn1_makeseq();
+ asn1_cleanup();
+}
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.h b/src/lib/krb5/asn.1/asn1_k_encode.h
index caa46c5..b5f24c4 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.h
+++ b/src/lib/krb5/asn.1/asn1_k_encode.h
@@ -269,4 +269,71 @@ asn1_error_code asn1_encode_predicted_sam_response
asn1_error_code asn1_encode_krb_saved_safe_body
(asn1buf *buf, const krb5_data *body, unsigned int *retlen);
+/* PKINIT */
+
+asn1_error_code asn1_encode_pk_authenticator
+ (asn1buf *buf, const krb5_pk_authenticator *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pk_authenticator_draft9
+ (asn1buf *buf, const krb5_pk_authenticator_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_algorithm_identifier
+ (asn1buf *buf, const krb5_algorithm_identifier *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_subject_pk_info
+ (asn1buf *buf, const krb5_subject_pk_info *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_algorithm_identifier
+ (asn1buf *buf, const krb5_algorithm_identifier **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_auth_pack
+ (asn1buf *buf, const krb5_auth_pack *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_auth_pack_draft9
+ (asn1buf *buf, const krb5_auth_pack_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_external_principal_identifier
+ (asn1buf *buf, const krb5_external_principal_identifier *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_external_principal_identifier
+ (asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_req
+ (asn1buf *buf, const krb5_pa_pk_as_req *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_trusted_ca
+ (asn1buf *buf, const krb5_trusted_ca *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_trusted_ca
+ (asn1buf *buf, const krb5_trusted_ca **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_req_draft9
+ (asn1buf *buf, const krb5_pa_pk_as_req_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_dh_rep_info
+ (asn1buf *buf, const krb5_dh_rep_info *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_kdc_dh_key_info
+ (asn1buf *buf, const krb5_kdc_dh_key_info *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_reply_key_pack
+ (asn1buf *buf, const krb5_reply_key_pack *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_reply_key_pack_draft9
+ (asn1buf *buf, const krb5_reply_key_pack_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_rep
+ (asn1buf *buf, const krb5_pa_pk_as_rep *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_rep_draft9
+ (asn1buf *buf, const krb5_pa_pk_as_rep_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_td_trusted_certifiers
+ (asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_typed_data
+ (asn1buf *buf, const krb5_typed_data *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_typed_data
+ (asn1buf *buf, const krb5_typed_data **val, unsigned int *retlen);
#endif
diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c
index e5cd4f8..cbd6a12 100644
--- a/src/lib/krb5/asn.1/krb5_decode.c
+++ b/src/lib/krb5/asn.1/krb5_decode.c
@@ -934,3 +934,150 @@ krb5_error_code decode_krb5_predicted_sam_response(const krb5_data *code, krb5_p
cleanup(free);
}
+krb5_error_code decode_krb5_pa_pk_as_req(const krb5_data *code, krb5_pa_pk_as_req **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_req);
+
+ retval = asn1_decode_pa_pk_as_req(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_pa_pk_as_req_draft9(const krb5_data *code, krb5_pa_pk_as_req_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_req_draft9);
+
+ retval = asn1_decode_pa_pk_as_req_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_pa_pk_as_rep(const krb5_data *code, krb5_pa_pk_as_rep **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_rep);
+
+ retval = asn1_decode_pa_pk_as_rep(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_pa_pk_as_rep_draft9(const krb5_data *code, krb5_pa_pk_as_rep_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_rep_draft9);
+
+ retval = asn1_decode_pa_pk_as_rep_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_auth_pack(const krb5_data *code, krb5_auth_pack **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_auth_pack);
+
+ retval = asn1_decode_auth_pack(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_auth_pack_draft9(const krb5_data *code, krb5_auth_pack_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_auth_pack_draft9);
+
+ retval = asn1_decode_auth_pack_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_kdc_dh_key_info(const krb5_data *code, krb5_kdc_dh_key_info **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_kdc_dh_key_info);
+
+ retval = asn1_decode_kdc_dh_key_info(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_principal_name(const krb5_data *code, krb5_principal_data **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_principal_data);
+
+ retval = asn1_decode_krb5_principal_name(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_reply_key_pack(const krb5_data *code, krb5_reply_key_pack **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_reply_key_pack);
+
+ retval = asn1_decode_reply_key_pack(&buf, *rep);
+ if (retval)
+ goto error_out;
+
+ cleanup_manual();
+error_out:
+ if (rep && *rep) {
+ if ((*rep)->replyKey.contents)
+ free((*rep)->replyKey.contents);
+ if ((*rep)->asChecksum.contents)
+ free((*rep)->asChecksum.contents);
+ free(*rep);
+ *rep = NULL;
+ }
+ return retval;
+}
+
+krb5_error_code decode_krb5_reply_key_pack_draft9(const krb5_data *code, krb5_reply_key_pack_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_reply_key_pack_draft9);
+
+ retval = asn1_decode_reply_key_pack_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_typed_data(const krb5_data *code, krb5_typed_data ***rep)
+{
+ setup_buf_only();
+ retval = asn1_decode_sequence_of_typed_data(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_td_trusted_certifiers(const krb5_data *code, krb5_external_principal_identifier ***rep)
+{
+ setup_buf_only();
+ retval = asn1_decode_sequence_of_external_principal_identifier(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_td_dh_parameters(const krb5_data *code, krb5_algorithm_identifier ***rep)
+{
+ setup_buf_only();
+ retval = asn1_decode_sequence_of_algorithm_identifier(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
diff --git a/src/lib/krb5/asn.1/krb5_encode.c b/src/lib/krb5/asn.1/krb5_encode.c
index 639db43..682e9a2 100644
--- a/src/lib/krb5/asn.1/krb5_encode.c
+++ b/src/lib/krb5/asn.1/krb5_encode.c
@@ -697,6 +697,23 @@ krb5_error_code encode_krb5_authdata(const krb5_authdata **rep, krb5_data **code
krb5_cleanup();
}
+krb5_error_code encode_krb5_authdata_elt(const krb5_authdata *rep, krb5_data **code)
+{
+ asn1_error_code retval;
+ asn1buf *buf=NULL;
+ unsigned int length;
+
+ if(rep == NULL) return ASN1_MISSING_FIELD;
+
+ retval = asn1buf_create(&buf);
+ if(retval) return retval;
+
+ retval = asn1_encode_krb5_authdata_elt(buf,rep, &length);
+ if(retval) return retval;
+
+ krb5_cleanup();
+}
+
krb5_error_code encode_krb5_alt_method(const krb5_alt_method *rep, krb5_data **code)
{
krb5_setup();
@@ -887,3 +904,111 @@ krb5_error_code encode_krb5_setpw_req(const krb5_principal target,
krb5_cleanup();
}
+
+krb5_error_code encode_krb5_pa_pk_as_req(const krb5_pa_pk_as_req *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_req(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_pa_pk_as_req_draft9(const krb5_pa_pk_as_req_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_req_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_pa_pk_as_rep(const krb5_pa_pk_as_rep *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_rep(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_pa_pk_as_rep_draft9(const krb5_pa_pk_as_rep_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_rep_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_auth_pack(const krb5_auth_pack *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_auth_pack(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_auth_pack_draft9(const krb5_auth_pack_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_auth_pack_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_kdc_dh_key_info(const krb5_kdc_dh_key_info *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_kdc_dh_key_info(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_reply_key_pack(const krb5_reply_key_pack *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_reply_key_pack(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_reply_key_pack_draft9(const krb5_reply_key_pack_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_reply_key_pack_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_td_trusted_certifiers(const krb5_external_principal_identifier **rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_td_trusted_certifiers(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_typed_data(const krb5_typed_data **rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_sequence_of_typed_data(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_td_dh_parameters(const krb5_algorithm_identifier **rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_sequence_of_algorithm_identifier(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
index 7be2bec..03bc2d3 100644
--- a/src/lib/krb5/krb/preauth2.c
+++ b/src/lib/krb5/krb/preauth2.c
@@ -71,7 +71,7 @@ krb5_init_preauth_context(krb5_context kcontext)
{
int n_modules, n_tables, i, j, k;
void **tables;
- struct krb5plugin_preauth_client_ftable_v0 *table;
+ struct krb5plugin_preauth_client_ftable_v1 *table;
krb5_preauth_context *context = NULL;
void *plugin_context;
krb5_preauthtype pa_type;
@@ -93,7 +93,7 @@ krb5_init_preauth_context(krb5_context kcontext)
/* pull out the module function tables for all of the modules */
tables = NULL;
if (krb5int_get_plugin_dir_data(&kcontext->preauth_plugins,
- "preauthentication_client_0",
+ "preauthentication_client_1",
&tables,
&kcontext->err) != 0) {
return;
@@ -352,37 +352,45 @@ grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
}
}
-/* Add the given pa_data item to the list of items. Factored out here to make
- * reading the do_preauth logic easier to read. */
+/*
+ * Add the given list of pa_data items to the existing list of items.
+ * Factored out here to make reading the do_preauth logic easier to read.
+ */
static int
grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
- krb5_pa_data *addition)
+ krb5_pa_data **addition, int num_addition)
{
krb5_pa_data **pa_list;
- int i;
+ int i, j;
- if (out_pa_list == NULL) {
+ if (out_pa_list == NULL || addition == NULL) {
return EINVAL;
}
if (*out_pa_list == NULL) {
- /* Allocate room for one entry and a NULL terminator. */
- pa_list = malloc(2 * sizeof(krb5_pa_data *));
+ /* Allocate room for the new additions and a NULL terminator. */
+ pa_list = malloc((num_addition + 1) * sizeof(krb5_pa_data *));
if (pa_list == NULL)
return ENOMEM;
- pa_list[0] = addition;
- pa_list[1] = NULL;
+ for (i = 0; i < num_addition; i++)
+ pa_list[i] = addition[i];
+ pa_list[i] = NULL;
*out_pa_list = pa_list;
- *out_pa_list_size = 1;
+ *out_pa_list_size = num_addition;
} else {
- /* Allocate room for one more entry and a NULL terminator. */
- pa_list = malloc((*out_pa_list_size + 2) * sizeof(krb5_pa_data *));
+ /*
+ * Allocate room for the existing entries plus
+ * the new additions and a NULL terminator.
+ */
+ pa_list = malloc((*out_pa_list_size + num_addition + 1)
+ * sizeof(krb5_pa_data *));
if (pa_list == NULL)
return ENOMEM;
for (i = 0; i < *out_pa_list_size; i++)
pa_list[i] = (*out_pa_list)[i];
- pa_list[i++] = addition;
- pa_list[i++] = NULL;
+ for (j = 0; j < num_addition;)
+ pa_list[i++] = addition[j++];
+ pa_list[i] = NULL;
free(*out_pa_list);
*out_pa_list = pa_list;
*out_pa_list_size = i;
@@ -502,7 +510,7 @@ krb5_run_preauth_plugins(krb5_context kcontext,
krb5_gic_opt_ext *opte)
{
int i;
- krb5_pa_data *out_pa_data;
+ krb5_pa_data **out_pa_data;
krb5_error_code ret;
struct _krb5_preauth_context_module *module;
@@ -554,7 +562,10 @@ krb5_run_preauth_plugins(krb5_context kcontext,
*module_ret = ret;
/* Save the new preauth data item. */
if (out_pa_data != NULL) {
- ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data);
+ int i;
+ for (i = 0; out_pa_data[i] != NULL; i++);
+ ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, i);
+ free(out_pa_data);
if (ret != 0)
return ret;
}
@@ -1363,7 +1374,7 @@ krb5_do_preauth_tryagain(krb5_context kcontext,
krb5_gic_opt_ext *opte)
{
krb5_error_code ret;
- krb5_pa_data *out_padata;
+ krb5_pa_data **out_padata;
krb5_preauth_context *context;
struct _krb5_preauth_context_module *module;
int i, j;
@@ -1404,7 +1415,11 @@ krb5_do_preauth_tryagain(krb5_context kcontext,
as_key,
&out_padata) == 0) {
if (out_padata != NULL) {
- grow_pa_list(return_padata, &out_pa_list_size, out_padata);
+ int i;
+ for (i = 0; out_padata[i] != NULL; i++);
+ grow_pa_list(return_padata, &out_pa_list_size,
+ out_padata, i);
+ free(out_padata);
return 0;
}
}
@@ -1584,7 +1599,7 @@ krb5_do_preauth(krb5_context context,
}
ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
- out_pa);
+ &out_pa, 1);
if (ret != 0) {
goto cleanup;
}
diff --git a/src/lib/krb5/os/accessor.c b/src/lib/krb5/os/accessor.c
index 95f0f0f..66541e4 100644
--- a/src/lib/krb5/os/accessor.c
+++ b/src/lib/krb5/os/accessor.c
@@ -80,6 +80,35 @@ krb5int_accessor(krb5int_access *internals, krb5_int32 version)
S (krb5_ser_unpack_int64, krb5_ser_unpack_int64),
S (asn1_ldap_encode_sequence_of_keys, krb5int_ldap_encode_sequence_of_keys),
S (asn1_ldap_decode_sequence_of_keys, krb5int_ldap_decode_sequence_of_keys),
+ S (encode_krb5_pa_pk_as_req, encode_krb5_pa_pk_as_req),
+ S (encode_krb5_pa_pk_as_req_draft9, encode_krb5_pa_pk_as_req_draft9), S (encode_krb5_pa_pk_as_rep, encode_krb5_pa_pk_as_rep),
+ S (encode_krb5_pa_pk_as_rep_draft9, encode_krb5_pa_pk_as_rep_draft9),
+ S (encode_krb5_auth_pack, encode_krb5_auth_pack),
+ S (encode_krb5_auth_pack_draft9, encode_krb5_auth_pack_draft9),
+ S (encode_krb5_kdc_dh_key_info, encode_krb5_kdc_dh_key_info),
+ S (encode_krb5_reply_key_pack, encode_krb5_reply_key_pack),
+ S (encode_krb5_reply_key_pack_draft9, encode_krb5_reply_key_pack_draft9),
+ S (encode_krb5_typed_data, encode_krb5_typed_data),
+ S (encode_krb5_td_trusted_certifiers, encode_krb5_td_trusted_certifiers),
+ S (encode_krb5_td_dh_parameters, encode_krb5_td_dh_parameters),
+ S (decode_krb5_pa_pk_as_req, decode_krb5_pa_pk_as_req),
+ S (decode_krb5_pa_pk_as_req_draft9, decode_krb5_pa_pk_as_req_draft9),
+ S (decode_krb5_pa_pk_as_rep, decode_krb5_pa_pk_as_rep),
+ S (decode_krb5_pa_pk_as_rep_draft9, decode_krb5_pa_pk_as_rep_draft9),
+ S (decode_krb5_auth_pack, decode_krb5_auth_pack),
+ S (decode_krb5_auth_pack_draft9, decode_krb5_auth_pack_draft9),
+ S (decode_krb5_kdc_dh_key_info, decode_krb5_kdc_dh_key_info),
+ S (decode_krb5_principal_name, decode_krb5_principal_name),
+ S (decode_krb5_reply_key_pack, decode_krb5_reply_key_pack),
+ S (decode_krb5_reply_key_pack_draft9, decode_krb5_reply_key_pack_draft9),
+ 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),
+ S (encode_krb5_authdata_elt, encode_krb5_authdata_elt)
#if DESIGNATED_INITIALIZERS
};
#else
diff --git a/src/plugins/preauth/cksum_body/cksum_body_main.c b/src/plugins/preauth/cksum_body/cksum_body_main.c
index 8efda23..66b1620 100644
--- a/src/plugins/preauth/cksum_body/cksum_body_main.c
+++ b/src/plugins/preauth/cksum_body/cksum_body_main.c
@@ -41,7 +41,7 @@
* The AS-REP carries no preauthentication data for this scheme.
*/
-#ident "$Id$"
+#ident "$Id: cksum_body_main.c,v 1.4 2007/01/02 22:33:50 kwc Exp $"
#include "autoconf.h"
@@ -66,6 +66,11 @@ struct server_stats{
int successes, failures;
};
+typedef struct _test_svr_req_ctx {
+ int value1;
+ int value2;
+} test_svr_req_ctx;
+
static int
client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
@@ -89,9 +94,9 @@ client_process(krb5_context kcontext,
void *gak_data,
krb5_data *salt, krb5_data *s2kparams,
krb5_keyblock *as_key,
- krb5_pa_data **out_pa_data)
+ krb5_pa_data ***out_pa_data)
{
- krb5_pa_data *send_pa;
+ krb5_pa_data **send_pa;
krb5_checksum checksum;
krb5_enctype enctype;
krb5_cksumtype *cksumtypes;
@@ -188,23 +193,31 @@ client_process(krb5_context kcontext,
}
/* Allocate the preauth data structure. */
- send_pa = malloc(sizeof(krb5_pa_data));
+ send_pa = malloc(2 * sizeof(krb5_pa_data *));
if (send_pa == NULL) {
krb5_free_checksum_contents(kcontext, &checksum);
return ENOMEM;
}
- send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
- send_pa->length = 4 + checksum.length;
- send_pa->contents = malloc(4 + checksum.length);
- if (send_pa->contents == NULL) {
+ send_pa[1] = NULL; /* Terminate list */
+ send_pa[0] = malloc(sizeof(krb5_pa_data));
+ if (send_pa[0] == NULL) {
+ krb5_free_checksum_contents(kcontext, &checksum);
+ free(send_pa);
+ return ENOMEM;
+ }
+ send_pa[0]->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
+ send_pa[0]->length = 4 + checksum.length;
+ send_pa[0]->contents = malloc(4 + checksum.length);
+ if (send_pa[0]->contents == NULL) {
krb5_free_checksum_contents(kcontext, &checksum);
+ free(send_pa[0]);
free(send_pa);
return ENOMEM;
}
/* Store the checksum. */
- memcpy(send_pa->contents, &cksumtype, 4);
- memcpy(send_pa->contents + 4, checksum.contents, checksum.length);
+ memcpy(send_pa[0]->contents, &cksumtype, 4);
+ memcpy(send_pa[0]->contents + 4, checksum.contents, checksum.length);
*out_pa_data = send_pa;
/* Clean up. */
@@ -229,7 +242,7 @@ client_gic_opt(krb5_context kcontext,
/* Initialize and tear down the server-side module, and do stat tracking. */
static krb5_error_code
-server_init(krb5_context kcontext, void **module_context)
+server_init(krb5_context kcontext, void **module_context, const char **realmnames)
{
struct server_stats *stats;
stats = malloc(sizeof(struct server_stats));
@@ -324,7 +337,8 @@ server_verify(krb5_context kcontext,
preauth_get_entry_data_proc server_get_entry_data,
void *pa_module_context,
void **pa_request_context,
- krb5_data **e_data)
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
{
krb5_int32 cksumtype;
krb5_checksum checksum;
@@ -338,9 +352,14 @@ server_verify(krb5_context kcontext,
krb5_error_code status;
struct server_stats *stats;
krb5_data *test_edata;
+ test_svr_req_ctx *svr_req_ctx;
+ krb5_authdata **my_authz_data = NULL;
stats = pa_module_context;
+#ifdef DEBUG
+ fprintf(stderr, "cksum_body: server_verify\n");
+#endif
/* Verify the preauth data. Start with the checksum type. */
if (data->length < 4) {
stats->failures++;
@@ -477,6 +496,54 @@ server_verify(krb5_context kcontext,
return KRB5KDC_ERR_PREAUTH_FAILED;
}
+ /*
+ * Return some junk authorization data just to exercise the
+ * code path handling the returned authorization data.
+ *
+ * NOTE that this is NOT VALID authorization data!
+ */
+#ifdef DEBUG
+ fprintf(stderr, "cksum_body: doing authorization data!\n");
+#endif
+#if 1 /* USE_5000_AD */
+#define AD_ALLOC_SIZE 5000
+ /* ad_header consists of a sequence tag (0x30) and length (0x82 0x1384)
+ * followed by octet string tag (0x04) and length (0x82 0x1380) */
+ krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80};
+#else
+#define AD_ALLOC_SIZE 100
+ /* ad_header consists of a sequence tag (0x30) and length (0x62)
+ * followed by octet string tag (0x04) and length (0x60) */
+ krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60};
+#endif
+ my_authz_data = malloc(2 * sizeof(*my_authz_data));
+ if (my_authz_data != NULL) {
+ my_authz_data[1] = NULL;
+ my_authz_data[0] = malloc(sizeof(krb5_authdata));
+ if (my_authz_data[0] == NULL) {
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
+ if (my_authz_data[0]->contents == NULL) {
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE);
+ my_authz_data[0]->magic = KV5M_AUTHDATA;
+ my_authz_data[0]->ad_type = 1;
+ my_authz_data[0]->length = AD_ALLOC_SIZE;
+ memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header));
+ sprintf(my_authz_data[0]->contents + sizeof(ad_header),
+ "cksum authorization data: %d bytes worth!\n", AD_ALLOC_SIZE);
+ *authz_data = my_authz_data;
+#ifdef DEBUG
+ fprintf(stderr, "Returning %d bytes of authorization data\n",
+ AD_ALLOC_SIZE);
+#endif
+ }
+
/* Return edata to exercise code that handles edata... */
test_edata = malloc(sizeof(*test_edata));
if (test_edata != NULL) {
@@ -490,6 +557,18 @@ server_verify(krb5_context kcontext,
}
}
+ /* Return a request context to exercise code that handles it */
+ svr_req_ctx = malloc(sizeof(*svr_req_ctx));
+ if (svr_req_ctx != NULL) {
+ svr_req_ctx->value1 = 111111;
+ svr_req_ctx->value2 = 222222;
+#ifdef DEBUG
+ fprintf(stderr, "server_verify: returning context at %p\n",
+ svr_req_ctx);
+#endif
+ }
+ *pa_request_context = svr_req_ctx;
+
/* Note that preauthentication succeeded. */
enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
stats->successes++;
@@ -516,6 +595,37 @@ server_return(krb5_context kcontext,
return 0;
}
+/* Test server request context freeing */
+static krb5_error_code
+server_free_reqctx(krb5_context kcontext,
+ void *pa_module_context,
+ void **pa_request_context)
+{
+ test_svr_req_ctx *svr_req_ctx;
+#ifdef DEBUG
+ fprintf(stderr, "server_free_reqctx: entered!\n");
+#endif
+ if (pa_request_context == NULL)
+ return 0;
+
+ svr_req_ctx = *pa_request_context;
+ if (svr_req_ctx == NULL)
+ return 0;
+
+ if (svr_req_ctx->value1 != 111111 || svr_req_ctx->value2 != 222222) {
+ fprintf(stderr, "server_free_reqctx: got invalid req context "
+ "at %p with values %d and %d\n",
+ svr_req_ctx, svr_req_ctx->value1, svr_req_ctx->value2);
+ return EINVAL;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "server_free_reqctx: freeing context at %p\n", svr_req_ctx);
+#endif
+ free(svr_req_ctx);
+ *pa_request_context = NULL;
+ return 0;
+}
+
static int
server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
@@ -529,7 +639,7 @@ static krb5_preauthtype supported_server_pa_types[] = {
KRB5_PADATA_CKSUM_BODY_REQ, 0,
};
-struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
+struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
"cksum_body", /* name */
&supported_client_pa_types[0], /* pa_type_list */
NULL, /* enctype_list */
@@ -543,7 +653,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
client_gic_opt /* get init creds opt function */
};
-struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
+struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
"cksum_body",
&supported_server_pa_types[0],
server_init,
@@ -552,5 +662,5 @@ struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
server_get_edata,
server_verify,
server_return,
- NULL
+ server_free_reqctx
};
diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in
new file mode 100644
index 0000000..7542e3c
--- /dev/null
+++ b/src/plugins/preauth/pkinit/Makefile.in
@@ -0,0 +1,109 @@
+thisconfigdir=../../..
+myfulldir=plugins/preauth/pkinit
+mydir=plugins/preauth/pkinit
+BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
+DEFS=@DEFS@
+
+LOCALINCLUDES = -I../../../include/krb5 -I.
+
+LIBBASE=pkinit
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+RELDIR=../plugins/preauth/pkinit
+# Depends on libk5crypto and libkrb5
+SHLIB_EXPDEPS = \
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+ $(TOPLIBD)/libkrb5$(SHLIBEXT)
+LIBS+= -lcrypto
+SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto -ldl $(SUPPORT_LIB) $(LIBS)
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+STOBJLISTS=OBJS.ST
+STLIBOBJS= \
+ pkinit_accessor.o \
+ pkinit_srv.o \
+ pkinit_lib.o \
+ pkinit_clnt.o \
+ pkinit_profile.o \
+ pkinit_identity.o \
+ pkinit_matching.o \
+ pkinit_crypto_openssl.o
+
+SRCS= \
+ $(srcdir)/pkinit_accessor.c \
+ $(srcdir)/pkinit_srv.c \
+ $(srcdir)/pkinit_lib.c \
+ $(srcdir)/pkinit_clnt.c \
+ $(srcdir)/pkinit_profile.c \
+ $(srcdir)/pkinit_identity.c \
+ $(srcdir)/pkinit_matching.c \
+ $(srcdir)/pkinit_crypto_openssl.c
+
+all-unix:: $(LIBBASE)$(SO_EXT)
+install-unix:: install-libs
+clean-unix:: clean-libs clean-libobjs
+
+clean::
+ $(RM) lib$(LIBBASE)$(SO_EXT)
+
+@libnover_frag@
+@libobj_frag@
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+pkinit_accessor.so pkinit_accessor.po $(OUTPRE)pkinit_accessor.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h pkinit_accessor.c \
+ pkinit_accessor.h
+pkinit_srv.so pkinit_srv.po $(OUTPRE)pkinit_srv.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_srv.c
+pkinit_lib.so pkinit_lib.po $(OUTPRE)pkinit_lib.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_lib.c
+pkinit_clnt.so pkinit_clnt.po $(OUTPRE)pkinit_clnt.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_clnt.c pkinit_crypto.h
+pkinit_profile.so pkinit_profile.po $(OUTPRE)pkinit_profile.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h pkinit.h pkinit_accessor.h \
+ pkinit_crypto.h pkinit_profile.c
+pkinit_identity.so pkinit_identity.po $(OUTPRE)pkinit_identity.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_identity.c
+pkinit_matching.so pkinit_matching.po $(OUTPRE)pkinit_matching.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h pkinit.h pkinit_accessor.h \
+ pkinit_crypto.h pkinit_matching.c
+pkinit_crypto_openssl.so pkinit_crypto_openssl.po $(OUTPRE)pkinit_crypto_openssl.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_crypto_openssl.c \
+ pkinit_crypto_openssl.h
diff --git a/src/plugins/preauth/pkinit/README b/src/plugins/preauth/pkinit/README
new file mode 100644
index 0000000..90522a7
--- /dev/null
+++ b/src/plugins/preauth/pkinit/README
@@ -0,0 +1,263 @@
+Building with pkinit enabled:
+=============================
+To build the code you will need OpenSSL headers. To build
+with smartcard support, OpenSC is required. The code can
+currently be built w/o smartcard support by defining
+WITHOUT_PKCS11 in the CFLAGS.
+
+Although you will need OpenSC to build the code, at run time you just need a
+pkcs11 module. It may be possible to build without having OpenSC installed
+by changing the path to the pkcs11 include file and the name of the default
+module. We have not tried this.
+
+Running with pkinit enabled:
+=============================
+In the following descriptions, options specified with "DIR:"
+are assumed to point to an OpenSSL-style hashed CA directory
+where each CA cert is stored in a file "<hash of CA cert>.0".
+We assume that a CRL directory (pkinit_revoke) will contain CRL files
+"<hash of CA cert>.r0". We encourage that users have such
+infrastructure but we will also allow files that contain
+certificates (or crls) in that directory that are not named
+in this style. Basically, for a given directory, the code
+will read each file in the directory and if the file contains
+a certificate (or crl), we will include it.
+
+Configuration for a pkinit-enabled KDC:
+---------------------------------------
+The following pkinit-specific configuration parameters can be specified
+in kdc.conf in either the [kdcdefaults] stanza or the appropriate
+realm entry:
+
+"pkinit_identity"
+ Specifies where to find the KDC's certificate and private key.
+ It is REQUIRED, and can be defined only once. It takes the form:
+
+ FILE:<cert-file-name>[,<key-file-name>]
+
+ If <key-file-name> is not specified, the private key is assumed
+ to be located in the same file with the certificate.
+
+"pkinit_anchors"
+ Specifies where the KDC will find trusted root CA certficates
+ to verify client certificates it receives. This parameter is
+ REQUIRED and may be specified multiple times. It can take the
+ forms:
+
+ FILE:<ca-bundle-file-name>
+ DIR:<openssl-style-hashed-directory>
+
+"pkinit_pool"
+ Specifies where the KDC will find other (intermediate) CA
+ certificates it can use to build trust paths to the trusted
+ roots defined in "pkinit_anchors", while verifying client
+ certificates. This parameter is OPTIONAL and may be specified
+ multiple times. It can take the same forms as "pkinit_anchors".
+
+"pkinit_revoke"
+ Specifies where CRL information can be found to be used when
+ verifying client certificates. This parameter is OPTIONAL and
+ may by specified multiple times. It can take the same forms
+ as "pkinit_anchors". XXX???XXX
+
+"pkinit_dh_min_bits"
+ Specifies the minimum number of bits the KDC is willing to accept
+ for a client's Diffie-Hellman key. This is an OPTIONAL integer
+ value. The default is 1024.
+
+
+Configuration for pkinit-enabled client:
+----------------------------------------
+The following pkinit-specific configuration parameters can be specified
+in krb5.conf in the [libdefaults] stanza. They may be specified as
+"global" libdefaults or specified by realm within the [libdefaults]
+stanza.
+
+i.e.
+ [libdefaults]
+ pkinit_anchors = DIR:/etc/ssl/trusted-anchors
+ pkinit_win2k = false
+
+ EXAMPLE.COM = {
+ pkinit_win2k = true
+ pkinit_win2k_require_binding = true
+ pkinit_anchors = FILE:/etc/ssl/example.com.cas
+ }
+
+"pkinit_identity"
+ See the discussion for this option in the KDC description above.
+ For the client, this is OPTIONAL. The client can use either
+ of the following to specify its certificate and private key
+ locations:
+
+ FILE:<cert-file-name>[,<key-file-name>]
+
+ PKCS11:[module_name=]<module-name>[:slotid=<slot-id>]
+ [:token=<token-label>][:certid=<cert-id>]
+ [:certlabel=<cert-label>]
+
+"pkinit_anchors"
+ See the discussion for this option in the KDC description above.
+ For the client, this option is considered OPTIONAL, however it
+ is needed in practice to verify a KDC's certificate.
+
+"pkinit_pool"
+ See the discussion for these options in the KDC descriptions above.
+
+"pkinit_revoke"
+ See the discussion for these options in the KDC descriptions above.
+
+"pkinit_win2k"
+ This BOOLEAN option specifies whether the target realm is assumed
+ to support only the "old" version of the protocol. The default
+ is false.
+
+"pkinit_win2k_require_binding"
+ If this BOOLEAN option 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.
+
+"pkinit_require_eku"
+ This BOOLEAN specifies whether the client insists that the KDC
+ certificate contain the appropriate Extended Key Usage value.
+ The default is true.
+
+"pkinit_require_krbtgt_otherName"
+ This BOOLEAN specifies whether the client insists that the KDC
+ certificate contain the appropriate SubjectAlternativeName
+ value. The default is true.
+
+"pkinit_require_hostname_match"
+ This BOOLEAN specifies whether the client insists that the
+ hostname within the SubjectAlternativeName must match the
+ hostname the client believes it is talking to. The default
+ is false.
+
+
+Command-line options for kinit:
+-------------------------------
+Command-line options may be passed via the -X option to kinit.
+The following values are currently accepted:
+
+X509_user_identity=<value>
+ Where <value> has the same format as the "pkinit_identity"
+ config option described above.
+
+X509_anchors=<value>
+ Where <value> has the same format as the "pkinit_anchors"
+ config option described above.
+
+flag_RSA_PROTOCOL
+ This boolean specifies that the RSA protocol (rather than
+ the Diffie-Hellman protocol) should be used while authenticating
+ with the KDC.
+
+
+For a pkinit-enabled client using smartcards:
+---------------------------------------------
+1. Install a pkcs11 module (smartcard enabled or otherwise).
+ We use OpenSC but any module that implements pkcs11 should work.
+ There are some instructions for setting up OpenSC at
+ http://www.citi.umich.edu/projects/pkinit/smartcard_setup.html
+2. set the location of your trusted cas either by setting the
+ appropriate configuration file parameter or by specifying
+ "-X X509_anchors=<value>" on the command line.
+3. Select a pkcs11 module. The default is opensc-pkcs11.so. To use a
+ different one, use the "module_name=" option (see below).
+4. Select a pkcs11 slot/token. If you only have one with a token available,
+ that one will be used. Otherwise use the "slotid=" option. Or you can
+ specify a token label with the "token=" option and pkinit will attempt to
+ locate that token.
+5. Select a client certificate to use. You can specify a certificate ID
+ with "certid=" or a certificate label with "certlabel=". If multiple
+ certs fit the selection criteria, the first one with the appropriate
+ capabilities will be used.
+6. Options can be set either via the "pkcs11_identity" krb5.conf
+ parameter or via the -X parameter to kinit.
+ These two examples should produce the same result:
+
+ pkinit_identity=PKCS11:module_name=/usr/local/VendorX/lib/libpkcs11.so:slotid=1:certid=4
+
+ kinit -X X509_user_identity=PKCS11:module_name=/usr/local/VendorX/lib/libpkcs11.so:slotid=1:certid=4
+
+
+For a pkinit-enabled client using the filesystem for Cert and Key:
+------------------------------------------------------------------
+1. verify that the proper krb5.conf options are set
+2. Specify the certificate and private key locations either via the
+ pkinit_identity config option, or via the "-X X509_user_identity="
+ command line option.
+ These two examples should produce the same result:
+
+ pkinit_identity=FILE:/home/bubba/ssl/foo.crt,/home/bubba/ssl/foo.key
+
+ kinit -X X509_user_identity=FILE:/home/bubba/ssl/foo.crt,/home/bubba/ssl/foo.key
+
+
+Testing
+=======
+We have tested the client code against our server, Heimdal server,
+and Windows 2003 servers, but not yet against a Vista/Longhorn server.
+
+We have not yet been able to test a Windows client against our server.
+
+Most of our smartcard testing has been with Cryptoflex.
+
+Known Issues
+============
+- The client principal must currently have the REQUIRES_PREAUTH attribute
+ set to cause the use of pkinit
+- Some error reporting is incomplete
+
+
+Information about using CRLs
+============================
+N.B. The following describes a "pkinit_require_crl_checking" option
+which currently doesn't actually exist. The current behavior is
+as if this option were always set to false.
+
+We assume that the user will acquire needed CRLs and place them
+in a local directory (using OpenSSL style) or a single file. During
+verification of a certificate for each CA, the code looks for a CRL
+issued by that CA. 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
+pkinit_require_crl_checking is false, then verification succeeds.
+However, if pkinit_require_crl_checking is true and there is no
+CRL for the issuing CA, then verification fails.
+
+Basically, pkinit_require_crl_checking should be set to true if the
+policy is such that up-to-date CRLs must be present for every CA.
+
+
+===============
+NOT IMPLEMENTED
+===============
+The following KDC options allowed by Heimdal are NOT CURRENTLY IMPLEMENTED:
+
+"pkinit_kdc_ocsp"
+ Specifies where the KDC will find information to use the Online
+ Certificate Status Protocol while verifying client certificates.
+ This parameter is OPTIONAL and there is no default.
+
+"pkinit_mappings_file"
+ Specifies where the KDC will find information to use to map the
+ DN information in a certificate to a KDC principal. This would
+ be used when the client certificate does not contain the pkinit-SAN
+ information defined by RFC 4556.
+ This parameter is OPTIONAL and there is no default.
+
+"pkinit_allow_proxy_certificate"
+ Specifies whether the KDC should accept proxy client certificates
+ for authentication. This is an OPTIONAL boolean parameter. The
+ default is false.
+
+"pkinit_principal_in_certificate"
+ Specifies whether the KDC should obtain the client principal name
+ from the SubjectAlternativeName in the certificate presented
+ for authentication. This is an OPTIONAL boolean parameter. The
+ default is true.
+
+pkinit_identity=PKCS11:[module_name=]<module-name>[:slotid=<slot-id>]
+ [:token=<token-label>][:certid=<cert-id>]
+ [:certlabel=<cert-label>]
diff --git a/src/plugins/preauth/pkinit/README.developers b/src/plugins/preauth/pkinit/README.developers
new file mode 100644
index 0000000..e095327
--- /dev/null
+++ b/src/plugins/preauth/pkinit/README.developers
@@ -0,0 +1,18 @@
+Experimental features:
+1. If you want trustedCertifiers to be sent by the client, then set
+X509_CA_BUNDLE to a ca-bundle file.
+2. If you want to make our KDC act like a draft9 KDC, then modify pkinit_src.c
+file. there is an "#if 0" for "supported_server_pa_types". if you change "if 0"
+to "if 1", then the kdc will become draft9-only KDC.
+3. If you like more debugging output, add "-DDEBUG" to CFLAGS and recompile
+the code.
+4. If you are debugging ASN1 encoding, add "-DDEBUG_ASN1" to CFLAGS and
+recompile the code. After running, you'll get DER encoded structures stored
+in /tmp. For example, /tmp/client_as_req will contains DER encoding of the
+pkinit part of the AS-REQ.
+5. Prior to having config options that manage EKU/SAN/CRL checking, you can
+modify pkinit_lib.c in function pkinit_lib_init(), set
+ plgctx->require_eku = 1 -- will require presence of EKU in certs
+ plgctx->require_san = 1 -- will require presence of SAN in KDC's cert
+ plgctx->require_crl_checking = 1 -- will require presence of CRLs to
+ verify every certificate
diff --git a/src/plugins/preauth/pkinit/configure.in b/src/plugins/preauth/pkinit/configure.in
new file mode 100644
index 0000000..5819289
--- /dev/null
+++ b/src/plugins/preauth/pkinit/configure.in
@@ -0,0 +1,19 @@
+K5_AC_INIT(configure.in)
+enable_shared=yes
+build_dynobj=yes
+CONFIG_RULES
+AC_CHECK_HEADERS(unistd.h)
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+
+AC_CHECK_FUNCS()
+
+# XXX This is incorrect, but should cause -lcrypto to be included by default
+AC_CHECK_LIB(crypto, PKCS7_get_signer_info)
+
+KRB5_RUN_FLAGS
+dnl The following is for check...
+KRB5_BUILD_PROGRAM
+KRB5_BUILD_LIBOBJS
+KRB5_BUILD_LIBRARY
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/plugins/preauth/pkinit/pkcs11.h b/src/plugins/preauth/pkinit/pkcs11.h
new file mode 100644
index 0000000..7d18f23
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkcs11.h
@@ -0,0 +1,1357 @@
+/* pkcs11.h
+ Copyright 2006 g10 Code GmbH
+ Copyright 2006 Andreas Jellinghaus
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. */
+
+/* Please submit changes back to the Scute project at
+ http://www.scute.org/ (or send them to marcus@g10code.com), so that
+ they can be picked up by other projects from there as well. */
+
+/* This file is a modified implementation of the PKCS #11 standard by
+ RSA Security Inc. It is mostly a drop-in replacement, with the
+ following change:
+
+ This header file does not require any macro definitions by the user
+ (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros
+ for you (if useful, some are missing, let me know if you need
+ more).
+
+ There is an additional API available that does comply better to the
+ GNU coding standard. It can be switched on by defining
+ CRYPTOKI_GNU before including this header file. For this, the
+ following changes are made to the specification:
+
+ All structure types are changed to a "struct ck_foo" where CK_FOO
+ is the type name in PKCS #11.
+
+ All non-structure types are changed to ck_foo_t where CK_FOO is the
+ lowercase version of the type name in PKCS #11. The basic types
+ (CK_ULONG et al.) are removed without substitute.
+
+ All members of structures are modified in the following way: Type
+ indication prefixes are removed, and underscore characters are
+ inserted before words. Then the result is lowercased.
+
+ Note that function names are still in the original case, as they
+ need for ABI compatibility.
+
+ CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use
+ <stdbool.h>.
+
+ If CRYPTOKI_COMPAT is defined before including this header file,
+ then none of the API changes above take place, and the API is the
+ one defined by the PKCS #11 standard. */
+
+#ifndef PKCS11_H
+#define PKCS11_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* The version of cryptoki we implement. The revision is changed with
+ each modification of this file. If you do not use the "official"
+ version of this file, please consider deleting the revision macro
+ (you may use a macro with a different name to keep track of your
+ versions). */
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 20
+#define CRYPTOKI_VERSION_REVISION 6
+
+
+/* Compatibility interface is default, unless CRYPTOKI_GNU is
+ given. */
+#ifndef CRYPTOKI_GNU
+#ifndef CRYPTOKI_COMPAT
+#define CRYPTOKI_COMPAT 1
+#endif
+#endif
+
+/* System dependencies. */
+
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+
+/* There is a matching pop below. */
+#pragma pack(push, cryptoki, 1)
+
+#ifdef CRYPTOKI_EXPORTS
+#define CK_SPEC __declspec(dllexport)
+#else
+#define CK_SPEC __declspec(dllimport)
+#endif
+
+#else
+
+#define CK_SPEC
+
+#endif
+
+
+#ifdef CRYPTOKI_COMPAT
+ /* If we are in compatibility mode, switch all exposed names to the
+ PKCS #11 variant. There are corresponding #undefs below. */
+
+#define ck_flags_t CK_FLAGS
+#define ck_version _CK_VERSION
+
+#define ck_info _CK_INFO
+#define cryptoki_version cryptokiVersion
+#define manufacturer_id manufacturerID
+#define library_description libraryDescription
+#define library_version libraryVersion
+
+#define ck_notification_t CK_NOTIFICATION
+#define ck_slot_id_t CK_SLOT_ID
+
+#define ck_slot_info _CK_SLOT_INFO
+#define slot_description slotDescription
+#define hardware_version hardwareVersion
+#define firmware_version firmwareVersion
+
+#define ck_token_info _CK_TOKEN_INFO
+#define serial_number serialNumber
+#define max_session_count ulMaxSessionCount
+#define session_count ulSessionCount
+#define max_rw_session_count ulMaxRwSessionCount
+#define rw_session_count ulRwSessionCount
+#define max_pin_len ulMaxPinLen
+#define min_pin_len ulMinPinLen
+#define total_public_memory ulTotalPublicMemory
+#define free_public_memory ulFreePublicMemory
+#define total_private_memory ulTotalPrivateMemory
+#define free_private_memory ulFreePrivateMemory
+#define utc_time utcTime
+
+#define ck_session_handle_t CK_SESSION_HANDLE
+#define ck_user_type_t CK_USER_TYPE
+#define ck_state_t CK_STATE
+
+#define ck_session_info _CK_SESSION_INFO
+#define slot_id slotID
+#define device_error ulDeviceError
+
+#define ck_object_handle_t CK_OBJECT_HANDLE
+#define ck_object_class_t CK_OBJECT_CLASS
+#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE
+#define ck_key_type_t CK_KEY_TYPE
+#define ck_certificate_type_t CK_CERTIFICATE_TYPE
+#define ck_attribute_type_t CK_ATTRIBUTE_TYPE
+
+#define ck_attribute _CK_ATTRIBUTE
+#define value pValue
+#define value_len ulValueLen
+
+#define ck_date _CK_DATE
+
+#define ck_mechanism_type_t CK_MECHANISM_TYPE
+
+#define ck_mechanism _CK_MECHANISM
+#define parameter pParameter
+#define parameter_len ulParameterLen
+
+#define ck_mechanism_info _CK_MECHANISM_INFO
+#define min_key_size ulMinKeySize
+#define max_key_size ulMaxKeySize
+
+#define ck_rv_t CK_RV
+#define ck_notify_t CK_NOTIFY
+
+#define ck_function_list _CK_FUNCTION_LIST
+
+#define ck_createmutex_t CK_CREATEMUTEX
+#define ck_destroymutex_t CK_DESTROYMUTEX
+#define ck_lockmutex_t CK_LOCKMUTEX
+#define ck_unlockmutex_t CK_UNLOCKMUTEX
+
+#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS
+#define create_mutex CreateMutex
+#define destroy_mutex DestroyMutex
+#define lock_mutex LockMutex
+#define unlock_mutex UnlockMutex
+#define reserved pReserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+
+typedef unsigned long ck_flags_t;
+
+struct ck_version
+{
+ unsigned char major;
+ unsigned char minor;
+};
+
+
+struct ck_info
+{
+ struct ck_version cryptoki_version;
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ unsigned char library_description[32];
+ struct ck_version library_version;
+};
+
+
+typedef unsigned long ck_notification_t;
+
+#define CKN_SURRENDER (0)
+
+
+typedef unsigned long ck_slot_id_t;
+
+
+struct ck_slot_info
+{
+ unsigned char slot_description[64];
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+};
+
+
+#define CKF_TOKEN_PRESENT (1 << 0)
+#define CKF_REMOVABLE_DEVICE (1 << 1)
+#define CKF_HW_SLOT (1 << 2)
+#define CKF_ARRAY_ATTRIBUTE (1 << 30)
+
+
+struct ck_token_info
+{
+ unsigned char label[32];
+ unsigned char manufacturer_id[32];
+ unsigned char model[16];
+ unsigned char serial_number[16];
+ ck_flags_t flags;
+ unsigned long max_session_count;
+ unsigned long session_count;
+ unsigned long max_rw_session_count;
+ unsigned long rw_session_count;
+ unsigned long max_pin_len;
+ unsigned long min_pin_len;
+ unsigned long total_public_memory;
+ unsigned long free_public_memory;
+ unsigned long total_private_memory;
+ unsigned long free_private_memory;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+ unsigned char utc_time[16];
+};
+
+
+#define CKF_RNG (1 << 0)
+#define CKF_WRITE_PROTECTED (1 << 1)
+#define CKF_LOGIN_REQUIRED (1 << 2)
+#define CKF_USER_PIN_INITIALIZED (1 << 3)
+#define CKF_RESTORE_KEY_NOT_NEEDED (1 << 5)
+#define CKF_CLOCK_ON_TOKEN (1 << 6)
+#define CKF_PROTECTED_AUTHENTICATION_PATH (1 << 8)
+#define CKF_DUAL_CRYPTO_OPERATIONS (1 << 9)
+#define CKF_TOKEN_INITIALIZED (1 << 10)
+#define CKF_SECONDARY_AUTHENTICATION (1 << 11)
+#define CKF_USER_PIN_COUNT_LOW (1 << 16)
+#define CKF_USER_PIN_FINAL_TRY (1 << 17)
+#define CKF_USER_PIN_LOCKED (1 << 18)
+#define CKF_USER_PIN_TO_BE_CHANGED (1 << 19)
+#define CKF_SO_PIN_COUNT_LOW (1 << 20)
+#define CKF_SO_PIN_FINAL_TRY (1 << 21)
+#define CKF_SO_PIN_LOCKED (1 << 22)
+#define CKF_SO_PIN_TO_BE_CHANGED (1 << 23)
+
+#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1)
+#define CK_EFFECTIVELY_INFINITE (0)
+
+
+typedef unsigned long ck_session_handle_t;
+
+#define CK_INVALID_HANDLE (0)
+
+
+typedef unsigned long ck_user_type_t;
+
+#define CKU_SO (0)
+#define CKU_USER (1)
+#define CKU_CONTEXT_SPECIFIC (2)
+
+
+typedef unsigned long ck_state_t;
+
+#define CKS_RO_PUBLIC_SESSION (0)
+#define CKS_RO_USER_FUNCTIONS (1)
+#define CKS_RW_PUBLIC_SESSION (2)
+#define CKS_RW_USER_FUNCTIONS (3)
+#define CKS_RW_SO_FUNCTIONS (4)
+
+
+struct ck_session_info
+{
+ ck_slot_id_t slot_id;
+ ck_state_t state;
+ ck_flags_t flags;
+ unsigned long device_error;
+};
+
+#define CKF_RW_SESSION (1 << 1)
+#define CKF_SERIAL_SESSION (1 << 2)
+
+
+typedef unsigned long ck_object_handle_t;
+
+
+typedef unsigned long ck_object_class_t;
+
+#define CKO_DATA (0)
+#define CKO_CERTIFICATE (1)
+#define CKO_PUBLIC_KEY (2)
+#define CKO_PRIVATE_KEY (3)
+#define CKO_SECRET_KEY (4)
+#define CKO_HW_FEATURE (5)
+#define CKO_DOMAIN_PARAMETERS (6)
+#define CKO_MECHANISM (7)
+#define CKO_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_hw_feature_type_t;
+
+#define CKH_MONOTONIC_COUNTER (1)
+#define CKH_CLOCK (2)
+#define CKH_USER_INTERFACE (3)
+#define CKH_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_key_type_t;
+
+#define CKK_RSA (0)
+#define CKK_DSA (1)
+#define CKK_DH (2)
+#define CKK_ECDSA (3)
+#define CKK_EC (3)
+#define CKK_X9_42_DH (4)
+#define CKK_KEA (5)
+#define CKK_GENERIC_SECRET (0x10)
+#define CKK_RC2 (0x11)
+#define CKK_RC4 (0x12)
+#define CKK_DES (0x13)
+#define CKK_DES2 (0x14)
+#define CKK_DES3 (0x15)
+#define CKK_CAST (0x16)
+#define CKK_CAST3 (0x17)
+#define CKK_CAST128 (0x18)
+#define CKK_RC5 (0x19)
+#define CKK_IDEA (0x1a)
+#define CKK_SKIPJACK (0x1b)
+#define CKK_BATON (0x1c)
+#define CKK_JUNIPER (0x1d)
+#define CKK_CDMF (0x1e)
+#define CKK_AES (0x1f)
+#define CKK_BLOWFISH (0x20)
+#define CKK_TWOFISH (0x21)
+#define CKK_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_certificate_type_t;
+
+#define CKC_X_509 (0)
+#define CKC_X_509_ATTR_CERT (1)
+#define CKC_WTLS (2)
+#define CKC_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_attribute_type_t;
+
+#define CKA_CLASS (0)
+#define CKA_TOKEN (1)
+#define CKA_PRIVATE (2)
+#define CKA_LABEL (3)
+#define CKA_APPLICATION (0x10)
+#define CKA_VALUE (0x11)
+#define CKA_OBJECT_ID (0x12)
+#define CKA_CERTIFICATE_TYPE (0x80)
+#define CKA_ISSUER (0x81)
+#define CKA_SERIAL_NUMBER (0x82)
+#define CKA_AC_ISSUER (0x83)
+#define CKA_OWNER (0x84)
+#define CKA_ATTR_TYPES (0x85)
+#define CKA_TRUSTED (0x86)
+#define CKA_CERTIFICATE_CATEGORY (0x87)
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88)
+#define CKA_URL (0x89)
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8a)
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8b)
+#define CKA_CHECK_VALUE (0x90)
+#define CKA_KEY_TYPE (0x100)
+#define CKA_SUBJECT (0x101)
+#define CKA_ID (0x102)
+#define CKA_SENSITIVE (0x103)
+#define CKA_ENCRYPT (0x104)
+#define CKA_DECRYPT (0x105)
+#define CKA_WRAP (0x106)
+#define CKA_UNWRAP (0x107)
+#define CKA_SIGN (0x108)
+#define CKA_SIGN_RECOVER (0x109)
+#define CKA_VERIFY (0x10a)
+#define CKA_VERIFY_RECOVER (0x10b)
+#define CKA_DERIVE (0x10c)
+#define CKA_START_DATE (0x110)
+#define CKA_END_DATE (0x111)
+#define CKA_MODULUS (0x120)
+#define CKA_MODULUS_BITS (0x121)
+#define CKA_PUBLIC_EXPONENT (0x122)
+#define CKA_PRIVATE_EXPONENT (0x123)
+#define CKA_PRIME_1 (0x124)
+#define CKA_PRIME_2 (0x125)
+#define CKA_EXPONENT_1 (0x126)
+#define CKA_EXPONENT_2 (0x127)
+#define CKA_COEFFICIENT (0x128)
+#define CKA_PRIME (0x130)
+#define CKA_SUBPRIME (0x131)
+#define CKA_BASE (0x132)
+#define CKA_PRIME_BITS (0x133)
+#define CKA_SUB_PRIME_BITS (0x134)
+#define CKA_VALUE_BITS (0x160)
+#define CKA_VALUE_LEN (0x161)
+#define CKA_EXTRACTABLE (0x162)
+#define CKA_LOCAL (0x163)
+#define CKA_NEVER_EXTRACTABLE (0x164)
+#define CKA_ALWAYS_SENSITIVE (0x165)
+#define CKA_KEY_GEN_MECHANISM (0x166)
+#define CKA_MODIFIABLE (0x170)
+#define CKA_ECDSA_PARAMS (0x180)
+#define CKA_EC_PARAMS (0x180)
+#define CKA_EC_POINT (0x181)
+#define CKA_SECONDARY_AUTH (0x200)
+#define CKA_AUTH_PIN_FLAGS (0x201)
+#define CKA_ALWAYS_AUTHENTICATE (0x202)
+#define CKA_WRAP_WITH_TRUSTED (0x210)
+#define CKA_HW_FEATURE_TYPE (0x300)
+#define CKA_RESET_ON_INIT (0x301)
+#define CKA_HAS_RESET (0x302)
+#define CKA_PIXEL_X (0x400)
+#define CKA_PIXEL_Y (0x401)
+#define CKA_RESOLUTION (0x402)
+#define CKA_CHAR_ROWS (0x403)
+#define CKA_CHAR_COLUMNS (0x404)
+#define CKA_COLOR (0x405)
+#define CKA_BITS_PER_PIXEL (0x406)
+#define CKA_CHAR_SETS (0x480)
+#define CKA_ENCODING_METHODS (0x481)
+#define CKA_MIME_TYPES (0x482)
+#define CKA_MECHANISM_TYPE (0x500)
+#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501)
+#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502)
+#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503)
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212)
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600)
+#define CKA_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+struct ck_attribute
+{
+ ck_attribute_type_t type;
+ void *value;
+ unsigned long value_len;
+};
+
+
+struct ck_date
+{
+ unsigned char year[4];
+ unsigned char month[2];
+ unsigned char day[2];
+};
+
+
+typedef unsigned long ck_mechanism_type_t;
+
+#define CKM_RSA_PKCS_KEY_PAIR_GEN (0)
+#define CKM_RSA_PKCS (1)
+#define CKM_RSA_9796 (2)
+#define CKM_RSA_X_509 (3)
+#define CKM_MD2_RSA_PKCS (4)
+#define CKM_MD5_RSA_PKCS (5)
+#define CKM_SHA1_RSA_PKCS (6)
+#define CKM_RIPEMD128_RSA_PKCS (7)
+#define CKM_RIPEMD160_RSA_PKCS (8)
+#define CKM_RSA_PKCS_OAEP (9)
+#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xa)
+#define CKM_RSA_X9_31 (0xb)
+#define CKM_SHA1_RSA_X9_31 (0xc)
+#define CKM_RSA_PKCS_PSS (0xd)
+#define CKM_SHA1_RSA_PKCS_PSS (0xe)
+#define CKM_DSA_KEY_PAIR_GEN (0x10)
+#define CKM_DSA (0x11)
+#define CKM_DSA_SHA1 (0x12)
+#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20)
+#define CKM_DH_PKCS_DERIVE (0x21)
+#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30)
+#define CKM_X9_42_DH_DERIVE (0x31)
+#define CKM_X9_42_DH_HYBRID_DERIVE (0x32)
+#define CKM_X9_42_MQV_DERIVE (0x33)
+#define CKM_SHA256_RSA_PKCS (0x40)
+#define CKM_SHA384_RSA_PKCS (0x41)
+#define CKM_SHA512_RSA_PKCS (0x42)
+#define CKM_SHA256_RSA_PKCS_PSS (0x43)
+#define CKM_SHA384_RSA_PKCS_PSS (0x44)
+#define CKM_SHA512_RSA_PKCS_PSS (0x45)
+#define CKM_RC2_KEY_GEN (0x100)
+#define CKM_RC2_ECB (0x101)
+#define CKM_RC2_CBC (0x102)
+#define CKM_RC2_MAC (0x103)
+#define CKM_RC2_MAC_GENERAL (0x104)
+#define CKM_RC2_CBC_PAD (0x105)
+#define CKM_RC4_KEY_GEN (0x110)
+#define CKM_RC4 (0x111)
+#define CKM_DES_KEY_GEN (0x120)
+#define CKM_DES_ECB (0x121)
+#define CKM_DES_CBC (0x122)
+#define CKM_DES_MAC (0x123)
+#define CKM_DES_MAC_GENERAL (0x124)
+#define CKM_DES_CBC_PAD (0x125)
+#define CKM_DES2_KEY_GEN (0x130)
+#define CKM_DES3_KEY_GEN (0x131)
+#define CKM_DES3_ECB (0x132)
+#define CKM_DES3_CBC (0x133)
+#define CKM_DES3_MAC (0x134)
+#define CKM_DES3_MAC_GENERAL (0x135)
+#define CKM_DES3_CBC_PAD (0x136)
+#define CKM_CDMF_KEY_GEN (0x140)
+#define CKM_CDMF_ECB (0x141)
+#define CKM_CDMF_CBC (0x142)
+#define CKM_CDMF_MAC (0x143)
+#define CKM_CDMF_MAC_GENERAL (0x144)
+#define CKM_CDMF_CBC_PAD (0x145)
+#define CKM_MD2 (0x200)
+#define CKM_MD2_HMAC (0x201)
+#define CKM_MD2_HMAC_GENERAL (0x202)
+#define CKM_MD5 (0x210)
+#define CKM_MD5_HMAC (0x211)
+#define CKM_MD5_HMAC_GENERAL (0x212)
+#define CKM_SHA_1 (0x220)
+#define CKM_SHA_1_HMAC (0x221)
+#define CKM_SHA_1_HMAC_GENERAL (0x222)
+#define CKM_RIPEMD128 (0x230)
+#define CKM_RIPEMD128_HMAC (0x231)
+#define CKM_RIPEMD128_HMAC_GENERAL (0x232)
+#define CKM_RIPEMD160 (0x240)
+#define CKM_RIPEMD160_HMAC (0x241)
+#define CKM_RIPEMD160_HMAC_GENERAL (0x242)
+#define CKM_SHA256 (0x250)
+#define CKM_SHA256_HMAC (0x251)
+#define CKM_SHA256_HMAC_GENERAL (0x252)
+#define CKM_SHA384 (0x260)
+#define CKM_SHA384_HMAC (0x261)
+#define CKM_SHA384_HMAC_GENERAL (0x262)
+#define CKM_SHA512 (0x270)
+#define CKM_SHA512_HMAC (0x271)
+#define CKM_SHA512_HMAC_GENERAL (0x272)
+#define CKM_CAST_KEY_GEN (0x300)
+#define CKM_CAST_ECB (0x301)
+#define CKM_CAST_CBC (0x302)
+#define CKM_CAST_MAC (0x303)
+#define CKM_CAST_MAC_GENERAL (0x304)
+#define CKM_CAST_CBC_PAD (0x305)
+#define CKM_CAST3_KEY_GEN (0x310)
+#define CKM_CAST3_ECB (0x311)
+#define CKM_CAST3_CBC (0x312)
+#define CKM_CAST3_MAC (0x313)
+#define CKM_CAST3_MAC_GENERAL (0x314)
+#define CKM_CAST3_CBC_PAD (0x315)
+#define CKM_CAST5_KEY_GEN (0x320)
+#define CKM_CAST128_KEY_GEN (0x320)
+#define CKM_CAST5_ECB (0x321)
+#define CKM_CAST128_ECB (0x321)
+#define CKM_CAST5_CBC (0x322)
+#define CKM_CAST128_CBC (0x322)
+#define CKM_CAST5_MAC (0x323)
+#define CKM_CAST128_MAC (0x323)
+#define CKM_CAST5_MAC_GENERAL (0x324)
+#define CKM_CAST128_MAC_GENERAL (0x324)
+#define CKM_CAST5_CBC_PAD (0x325)
+#define CKM_CAST128_CBC_PAD (0x325)
+#define CKM_RC5_KEY_GEN (0x330)
+#define CKM_RC5_ECB (0x331)
+#define CKM_RC5_CBC (0x332)
+#define CKM_RC5_MAC (0x333)
+#define CKM_RC5_MAC_GENERAL (0x334)
+#define CKM_RC5_CBC_PAD (0x335)
+#define CKM_IDEA_KEY_GEN (0x340)
+#define CKM_IDEA_ECB (0x341)
+#define CKM_IDEA_CBC (0x342)
+#define CKM_IDEA_MAC (0x343)
+#define CKM_IDEA_MAC_GENERAL (0x344)
+#define CKM_IDEA_CBC_PAD (0x345)
+#define CKM_GENERIC_SECRET_KEY_GEN (0x350)
+#define CKM_CONCATENATE_BASE_AND_KEY (0x360)
+#define CKM_CONCATENATE_BASE_AND_DATA (0x362)
+#define CKM_CONCATENATE_DATA_AND_BASE (0x363)
+#define CKM_XOR_BASE_AND_DATA (0x364)
+#define CKM_EXTRACT_KEY_FROM_KEY (0x365)
+#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370)
+#define CKM_SSL3_MASTER_KEY_DERIVE (0x371)
+#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372)
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373)
+#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374)
+#define CKM_TLS_MASTER_KEY_DERIVE (0x375)
+#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376)
+#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377)
+#define CKM_SSL3_MD5_MAC (0x380)
+#define CKM_SSL3_SHA1_MAC (0x381)
+#define CKM_MD5_KEY_DERIVATION (0x390)
+#define CKM_MD2_KEY_DERIVATION (0x391)
+#define CKM_SHA1_KEY_DERIVATION (0x392)
+#define CKM_PBE_MD2_DES_CBC (0x3a0)
+#define CKM_PBE_MD5_DES_CBC (0x3a1)
+#define CKM_PBE_MD5_CAST_CBC (0x3a2)
+#define CKM_PBE_MD5_CAST3_CBC (0x3a3)
+#define CKM_PBE_MD5_CAST5_CBC (0x3a4)
+#define CKM_PBE_MD5_CAST128_CBC (0x3a4)
+#define CKM_PBE_SHA1_CAST5_CBC (0x3a5)
+#define CKM_PBE_SHA1_CAST128_CBC (0x3a5)
+#define CKM_PBE_SHA1_RC4_128 (0x3a6)
+#define CKM_PBE_SHA1_RC4_40 (0x3a7)
+#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8)
+#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9)
+#define CKM_PBE_SHA1_RC2_128_CBC (0x3aa)
+#define CKM_PBE_SHA1_RC2_40_CBC (0x3ab)
+#define CKM_PKCS5_PBKD2 (0x3b0)
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0)
+#define CKM_KEY_WRAP_LYNKS (0x400)
+#define CKM_KEY_WRAP_SET_OAEP (0x401)
+#define CKM_SKIPJACK_KEY_GEN (0x1000)
+#define CKM_SKIPJACK_ECB64 (0x1001)
+#define CKM_SKIPJACK_CBC64 (0x1002)
+#define CKM_SKIPJACK_OFB64 (0x1003)
+#define CKM_SKIPJACK_CFB64 (0x1004)
+#define CKM_SKIPJACK_CFB32 (0x1005)
+#define CKM_SKIPJACK_CFB16 (0x1006)
+#define CKM_SKIPJACK_CFB8 (0x1007)
+#define CKM_SKIPJACK_WRAP (0x1008)
+#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009)
+#define CKM_SKIPJACK_RELAYX (0x100a)
+#define CKM_KEA_KEY_PAIR_GEN (0x1010)
+#define CKM_KEA_KEY_DERIVE (0x1011)
+#define CKM_FORTEZZA_TIMESTAMP (0x1020)
+#define CKM_BATON_KEY_GEN (0x1030)
+#define CKM_BATON_ECB128 (0x1031)
+#define CKM_BATON_ECB96 (0x1032)
+#define CKM_BATON_CBC128 (0x1033)
+#define CKM_BATON_COUNTER (0x1034)
+#define CKM_BATON_SHUFFLE (0x1035)
+#define CKM_BATON_WRAP (0x1036)
+#define CKM_ECDSA_KEY_PAIR_GEN (0x1040)
+#define CKM_EC_KEY_PAIR_GEN (0x1040)
+#define CKM_ECDSA (0x1041)
+#define CKM_ECDSA_SHA1 (0x1042)
+#define CKM_ECDH1_DERIVE (0x1050)
+#define CKM_ECDH1_COFACTOR_DERIVE (0x1051)
+#define CKM_ECMQV_DERIVE (0x1052)
+#define CKM_JUNIPER_KEY_GEN (0x1060)
+#define CKM_JUNIPER_ECB128 (0x1061)
+#define CKM_JUNIPER_CBC128 (0x1062)
+#define CKM_JUNIPER_COUNTER (0x1063)
+#define CKM_JUNIPER_SHUFFLE (0x1064)
+#define CKM_JUNIPER_WRAP (0x1065)
+#define CKM_FASTHASH (0x1070)
+#define CKM_AES_KEY_GEN (0x1080)
+#define CKM_AES_ECB (0x1081)
+#define CKM_AES_CBC (0x1082)
+#define CKM_AES_MAC (0x1083)
+#define CKM_AES_MAC_GENERAL (0x1084)
+#define CKM_AES_CBC_PAD (0x1085)
+#define CKM_DSA_PARAMETER_GEN (0x2000)
+#define CKM_DH_PKCS_PARAMETER_GEN (0x2001)
+#define CKM_X9_42_DH_PARAMETER_GEN (0x2002)
+#define CKM_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+struct ck_mechanism
+{
+ ck_mechanism_type_t mechanism;
+ void *parameter;
+ unsigned long parameter_len;
+};
+
+
+struct ck_mechanism_info
+{
+ unsigned long min_key_size;
+ unsigned long max_key_size;
+ ck_flags_t flags;
+};
+
+#define CKF_HW (1 << 0)
+#define CKF_ENCRYPT (1 << 8)
+#define CKF_DECRYPT (1 << 9)
+#define CKF_DIGEST (1 << 10)
+#define CKF_SIGN (1 << 11)
+#define CKF_SIGN_RECOVER (1 << 12)
+#define CKF_VERIFY (1 << 13)
+#define CKF_VERIFY_RECOVER (1 << 14)
+#define CKF_GENERATE (1 << 15)
+#define CKF_GENERATE_KEY_PAIR (1 << 16)
+#define CKF_WRAP (1 << 17)
+#define CKF_UNWRAP (1 << 18)
+#define CKF_DERIVE (1 << 19)
+#define CKF_EXTENSION ((unsigned long) (1 << 31))
+
+
+/* Flags for C_WaitForSlotEvent. */
+#define CKF_DONT_BLOCK (1)
+
+
+typedef unsigned long ck_rv_t;
+
+
+typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session,
+ ck_notification_t event, void *application);
+
+/* Forward reference. */
+struct ck_function_list;
+
+#define _CK_DECLARE_FUNCTION(name, args) \
+typedef ck_rv_t (*CK_ ## name) args; \
+ck_rv_t CK_SPEC name args
+
+_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args));
+_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved));
+_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info));
+_CK_DECLARE_FUNCTION (C_GetFunctionList,
+ (struct ck_function_list **function_list));
+
+_CK_DECLARE_FUNCTION (C_GetSlotList,
+ (unsigned char token_present, ck_slot_id_t *slot_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetSlotInfo,
+ (ck_slot_id_t slot_id, struct ck_slot_info *info));
+_CK_DECLARE_FUNCTION (C_GetTokenInfo,
+ (ck_slot_id_t slot_id, struct ck_token_info *info));
+_CK_DECLARE_FUNCTION (C_WaitForSlotEvent,
+ (ck_flags_t flags, ck_slot_id_t *slot, void *reserved));
+_CK_DECLARE_FUNCTION (C_GetMechanismList,
+ (ck_slot_id_t slot_id,
+ ck_mechanism_type_t *mechanism_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetMechanismInfo,
+ (ck_slot_id_t slot_id, ck_mechanism_type_t type,
+ struct ck_mechanism_info *info));
+_CK_DECLARE_FUNCTION (C_InitToken,
+ (ck_slot_id_t slot_id, unsigned char *pin,
+ unsigned long pin_len, unsigned char *label));
+_CK_DECLARE_FUNCTION (C_InitPIN,
+ (ck_session_handle_t session, unsigned char *pin,
+ unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_SetPIN,
+ (ck_session_handle_t session, unsigned char *old_pin,
+ unsigned long old_len, unsigned char *new_pin,
+ unsigned long new_len));
+
+_CK_DECLARE_FUNCTION (C_OpenSession,
+ (ck_slot_id_t slot_id, ck_flags_t flags,
+ void *application, ck_notify_t notify,
+ ck_session_handle_t *session));
+_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id));
+_CK_DECLARE_FUNCTION (C_GetSessionInfo,
+ (ck_session_handle_t session,
+ struct ck_session_info *info));
+_CK_DECLARE_FUNCTION (C_GetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long *operation_state_len));
+_CK_DECLARE_FUNCTION (C_SetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long operation_state_len,
+ ck_object_handle_t encryption_key,
+ ck_object_handle_t authentiation_key));
+_CK_DECLARE_FUNCTION (C_Login,
+ (ck_session_handle_t session, ck_user_type_t user_type,
+ unsigned char *pin, unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_CreateObject,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count, ck_object_handle_t *object));
+_CK_DECLARE_FUNCTION (C_CopyObject,
+ (ck_session_handle_t session, ck_object_handle_t object,
+ struct ck_attribute *templ, unsigned long count,
+ ck_object_handle_t *new_object));
+_CK_DECLARE_FUNCTION (C_DestroyObject,
+ (ck_session_handle_t session,
+ ck_object_handle_t object));
+_CK_DECLARE_FUNCTION (C_GetObjectSize,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ unsigned long *size));
+_CK_DECLARE_FUNCTION (C_GetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_SetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjectsInit,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjects,
+ (ck_session_handle_t session,
+ ck_object_handle_t *object,
+ unsigned long max_object_count,
+ unsigned long *object_count));
+_CK_DECLARE_FUNCTION (C_FindObjectsFinal,
+ (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_EncryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Encrypt,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *encrypted_data,
+ unsigned long *encrypted_data_len));
+_CK_DECLARE_FUNCTION (C_EncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_EncryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_encrypted_part,
+ unsigned long *last_encrypted_part_len));
+
+_CK_DECLARE_FUNCTION (C_DecryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Decrypt,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_data,
+ unsigned long encrypted_data_len,
+ unsigned char *data, unsigned long *data_len));
+_CK_DECLARE_FUNCTION (C_DecryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part, unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_DecryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_part,
+ unsigned long *last_part_len));
+
+_CK_DECLARE_FUNCTION (C_DigestInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism));
+_CK_DECLARE_FUNCTION (C_Digest,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *digest,
+ unsigned long *digest_len));
+_CK_DECLARE_FUNCTION (C_DigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_DigestKey,
+ (ck_session_handle_t session, ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_DigestFinal,
+ (ck_session_handle_t session,
+ unsigned char *digest,
+ unsigned long *digest_len));
+
+_CK_DECLARE_FUNCTION (C_SignInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Sign,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_SignFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_SignRecover,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+
+_CK_DECLARE_FUNCTION (C_VerifyInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Verify,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_VerifyFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_VerifyRecover,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len,
+ unsigned char *data,
+ unsigned long *data_len));
+
+_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_SignEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+
+_CK_DECLARE_FUNCTION (C_GenerateKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *templ,
+ unsigned long count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_GenerateKeyPair,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *public_key_template,
+ unsigned long public_key_attribute_count,
+ struct ck_attribute *private_key_template,
+ unsigned long private_key_attribute_count,
+ ck_object_handle_t *public_key,
+ ck_object_handle_t *private_key));
+_CK_DECLARE_FUNCTION (C_WrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t wrapping_key,
+ ck_object_handle_t key,
+ unsigned char *wrapped_key,
+ unsigned long *wrapped_key_len));
+_CK_DECLARE_FUNCTION (C_UnwrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t unwrapping_key,
+ unsigned char *wrapped_key,
+ unsigned long wrapped_key_len,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_DeriveKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t base_key,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+
+_CK_DECLARE_FUNCTION (C_SeedRandom,
+ (ck_session_handle_t session, unsigned char *seed,
+ unsigned long seed_len));
+_CK_DECLARE_FUNCTION (C_GenerateRandom,
+ (ck_session_handle_t session,
+ unsigned char *random_data,
+ unsigned long random_len));
+
+_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session));
+
+
+struct ck_function_list
+{
+ struct ck_version version;
+ CK_C_Initialize C_Initialize;
+ CK_C_Finalize C_Finalize;
+ CK_C_GetInfo C_GetInfo;
+ CK_C_GetFunctionList C_GetFunctionList;
+ CK_C_GetSlotList C_GetSlotList;
+ CK_C_GetSlotInfo C_GetSlotInfo;
+ CK_C_GetTokenInfo C_GetTokenInfo;
+ CK_C_GetMechanismList C_GetMechanismList;
+ CK_C_GetMechanismInfo C_GetMechanismInfo;
+ CK_C_InitToken C_InitToken;
+ CK_C_InitPIN C_InitPIN;
+ CK_C_SetPIN C_SetPIN;
+ CK_C_OpenSession C_OpenSession;
+ CK_C_CloseSession C_CloseSession;
+ CK_C_CloseAllSessions C_CloseAllSessions;
+ CK_C_GetSessionInfo C_GetSessionInfo;
+ CK_C_GetOperationState C_GetOperationState;
+ CK_C_SetOperationState C_SetOperationState;
+ CK_C_Login C_Login;
+ CK_C_Logout C_Logout;
+ CK_C_CreateObject C_CreateObject;
+ CK_C_CopyObject C_CopyObject;
+ CK_C_DestroyObject C_DestroyObject;
+ CK_C_GetObjectSize C_GetObjectSize;
+ CK_C_GetAttributeValue C_GetAttributeValue;
+ CK_C_SetAttributeValue C_SetAttributeValue;
+ CK_C_FindObjectsInit C_FindObjectsInit;
+ CK_C_FindObjects C_FindObjects;
+ CK_C_FindObjectsFinal C_FindObjectsFinal;
+ CK_C_EncryptInit C_EncryptInit;
+ CK_C_Encrypt C_Encrypt;
+ CK_C_EncryptUpdate C_EncryptUpdate;
+ CK_C_EncryptFinal C_EncryptFinal;
+ CK_C_DecryptInit C_DecryptInit;
+ CK_C_Decrypt C_Decrypt;
+ CK_C_DecryptUpdate C_DecryptUpdate;
+ CK_C_DecryptFinal C_DecryptFinal;
+ CK_C_DigestInit C_DigestInit;
+ CK_C_Digest C_Digest;
+ CK_C_DigestUpdate C_DigestUpdate;
+ CK_C_DigestKey C_DigestKey;
+ CK_C_DigestFinal C_DigestFinal;
+ CK_C_SignInit C_SignInit;
+ CK_C_Sign C_Sign;
+ CK_C_SignUpdate C_SignUpdate;
+ CK_C_SignFinal C_SignFinal;
+ CK_C_SignRecoverInit C_SignRecoverInit;
+ CK_C_SignRecover C_SignRecover;
+ CK_C_VerifyInit C_VerifyInit;
+ CK_C_Verify C_Verify;
+ CK_C_VerifyUpdate C_VerifyUpdate;
+ CK_C_VerifyFinal C_VerifyFinal;
+ CK_C_VerifyRecoverInit C_VerifyRecoverInit;
+ CK_C_VerifyRecover C_VerifyRecover;
+ CK_C_DigestEncryptUpdate C_DigestEncryptUpdate;
+ CK_C_DecryptDigestUpdate C_DecryptDigestUpdate;
+ CK_C_SignEncryptUpdate C_SignEncryptUpdate;
+ CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate;
+ CK_C_GenerateKey C_GenerateKey;
+ CK_C_GenerateKeyPair C_GenerateKeyPair;
+ CK_C_WrapKey C_WrapKey;
+ CK_C_UnwrapKey C_UnwrapKey;
+ CK_C_DeriveKey C_DeriveKey;
+ CK_C_SeedRandom C_SeedRandom;
+ CK_C_GenerateRandom C_GenerateRandom;
+ CK_C_GetFunctionStatus C_GetFunctionStatus;
+ CK_C_CancelFunction C_CancelFunction;
+ CK_C_WaitForSlotEvent C_WaitForSlotEvent;
+};
+
+
+typedef ck_rv_t (*ck_createmutex_t) (void **mutex);
+typedef ck_rv_t (*ck_destroymutex_t) (void *mutex);
+typedef ck_rv_t (*ck_lockmutex_t) (void *mutex);
+typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex);
+
+
+struct ck_c_initialize_args
+{
+ ck_createmutex_t create_mutex;
+ ck_destroymutex_t destroy_mutex;
+ ck_lockmutex_t lock_mutex;
+ ck_unlockmutex_t unlock_mutex;
+ ck_flags_t flags;
+ void *reserved;
+};
+
+
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1 << 0)
+#define CKF_OS_LOCKING_OK (1 << 1)
+
+#define CKR_OK (0)
+#define CKR_CANCEL (1)
+#define CKR_HOST_MEMORY (2)
+#define CKR_SLOT_ID_INVALID (3)
+#define CKR_GENERAL_ERROR (5)
+#define CKR_FUNCTION_FAILED (6)
+#define CKR_ARGUMENTS_BAD (7)
+#define CKR_NO_EVENT (8)
+#define CKR_NEED_TO_CREATE_THREADS (9)
+#define CKR_CANT_LOCK (0xa)
+#define CKR_ATTRIBUTE_READ_ONLY (0x10)
+#define CKR_ATTRIBUTE_SENSITIVE (0x11)
+#define CKR_ATTRIBUTE_TYPE_INVALID (0x12)
+#define CKR_ATTRIBUTE_VALUE_INVALID (0x13)
+#define CKR_DATA_INVALID (0x20)
+#define CKR_DATA_LEN_RANGE (0x21)
+#define CKR_DEVICE_ERROR (0x30)
+#define CKR_DEVICE_MEMORY (0x31)
+#define CKR_DEVICE_REMOVED (0x32)
+#define CKR_ENCRYPTED_DATA_INVALID (0x40)
+#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41)
+#define CKR_FUNCTION_CANCELED (0x50)
+#define CKR_FUNCTION_NOT_PARALLEL (0x51)
+#define CKR_FUNCTION_NOT_SUPPORTED (0x54)
+#define CKR_KEY_HANDLE_INVALID (0x60)
+#define CKR_KEY_SIZE_RANGE (0x62)
+#define CKR_KEY_TYPE_INCONSISTENT (0x63)
+#define CKR_KEY_NOT_NEEDED (0x64)
+#define CKR_KEY_CHANGED (0x65)
+#define CKR_KEY_NEEDED (0x66)
+#define CKR_KEY_INDIGESTIBLE (0x67)
+#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68)
+#define CKR_KEY_NOT_WRAPPABLE (0x69)
+#define CKR_KEY_UNEXTRACTABLE (0x6a)
+#define CKR_MECHANISM_INVALID (0x70)
+#define CKR_MECHANISM_PARAM_INVALID (0x71)
+#define CKR_OBJECT_HANDLE_INVALID (0x82)
+#define CKR_OPERATION_ACTIVE (0x90)
+#define CKR_OPERATION_NOT_INITIALIZED (0x91)
+#define CKR_PIN_INCORRECT (0xa0)
+#define CKR_PIN_INVALID (0xa1)
+#define CKR_PIN_LEN_RANGE (0xa2)
+#define CKR_PIN_EXPIRED (0xa3)
+#define CKR_PIN_LOCKED (0xa4)
+#define CKR_SESSION_CLOSED (0xb0)
+#define CKR_SESSION_COUNT (0xb1)
+#define CKR_SESSION_HANDLE_INVALID (0xb3)
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4)
+#define CKR_SESSION_READ_ONLY (0xb5)
+#define CKR_SESSION_EXISTS (0xb6)
+#define CKR_SESSION_READ_ONLY_EXISTS (0xb7)
+#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8)
+#define CKR_SIGNATURE_INVALID (0xc0)
+#define CKR_SIGNATURE_LEN_RANGE (0xc1)
+#define CKR_TEMPLATE_INCOMPLETE (0xd0)
+#define CKR_TEMPLATE_INCONSISTENT (0xd1)
+#define CKR_TOKEN_NOT_PRESENT (0xe0)
+#define CKR_TOKEN_NOT_RECOGNIZED (0xe1)
+#define CKR_TOKEN_WRITE_PROTECTED (0xe2)
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0)
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1)
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2)
+#define CKR_USER_ALREADY_LOGGED_IN (0x100)
+#define CKR_USER_NOT_LOGGED_IN (0x101)
+#define CKR_USER_PIN_NOT_INITIALIZED (0x102)
+#define CKR_USER_TYPE_INVALID (0x103)
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104)
+#define CKR_USER_TOO_MANY_TYPES (0x105)
+#define CKR_WRAPPED_KEY_INVALID (0x110)
+#define CKR_WRAPPED_KEY_LEN_RANGE (0x112)
+#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113)
+#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114)
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115)
+#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120)
+#define CKR_RANDOM_NO_RNG (0x121)
+#define CKR_DOMAIN_PARAMS_INVALID (0x130)
+#define CKR_BUFFER_TOO_SMALL (0x150)
+#define CKR_SAVED_STATE_INVALID (0x160)
+#define CKR_INFORMATION_SENSITIVE (0x170)
+#define CKR_STATE_UNSAVEABLE (0x180)
+#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190)
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191)
+#define CKR_MUTEX_BAD (0x1a0)
+#define CKR_MUTEX_NOT_LOCKED (0x1a1)
+#define CKR_FUNCTION_REJECTED (0x200)
+#define CKR_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+
+/* Compatibility layer. */
+
+#ifdef CRYPTOKI_COMPAT
+
+#undef CK_DEFINE_FUNCTION
+#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name
+
+/* For NULL. */
+#include <stddef.h>
+
+typedef unsigned char CK_BYTE;
+typedef unsigned char CK_CHAR;
+typedef unsigned char CK_UTF8CHAR;
+typedef unsigned char CK_BBOOL;
+typedef unsigned long int CK_ULONG;
+typedef long int CK_LONG;
+typedef CK_BYTE *CK_BYTE_PTR;
+typedef CK_CHAR *CK_CHAR_PTR;
+typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR;
+typedef CK_ULONG *CK_ULONG_PTR;
+typedef void *CK_VOID_PTR;
+typedef void **CK_VOID_PTR_PTR;
+#define CK_FALSE 0
+#define CK_TRUE 1
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#endif
+
+typedef struct ck_version CK_VERSION;
+typedef struct ck_version *CK_VERSION_PTR;
+
+typedef struct ck_info CK_INFO;
+typedef struct ck_info *CK_INFO_PTR;
+
+typedef ck_slot_id_t *CK_SLOT_ID_PTR;
+
+typedef struct ck_slot_info CK_SLOT_INFO;
+typedef struct ck_slot_info *CK_SLOT_INFO_PTR;
+
+typedef struct ck_token_info CK_TOKEN_INFO;
+typedef struct ck_token_info *CK_TOKEN_INFO_PTR;
+
+typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR;
+
+typedef struct ck_session_info CK_SESSION_INFO;
+typedef struct ck_session_info *CK_SESSION_INFO_PTR;
+
+typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR;
+
+typedef ck_object_class_t *CK_OBJECT_CLASS_PTR;
+
+typedef struct ck_attribute CK_ATTRIBUTE;
+typedef struct ck_attribute *CK_ATTRIBUTE_PTR;
+
+typedef struct ck_date CK_DATE;
+typedef struct ck_date *CK_DATE_PTR;
+
+typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR;
+
+typedef struct ck_mechanism CK_MECHANISM;
+typedef struct ck_mechanism *CK_MECHANISM_PTR;
+
+typedef struct ck_mechanism_info CK_MECHANISM_INFO;
+typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR;
+
+typedef struct ck_function_list CK_FUNCTION_LIST;
+typedef struct ck_function_list *CK_FUNCTION_LIST_PTR;
+typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
+
+typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS;
+typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
+
+#define NULL_PTR NULL
+
+/* Delete the helper macros defined at the top of the file. */
+#undef ck_flags_t
+#undef ck_version
+
+#undef ck_info
+#undef cryptoki_version
+#undef manufacturer_id
+#undef library_description
+#undef library_version
+
+#undef ck_notification_t
+#undef ck_slot_id_t
+
+#undef ck_slot_info
+#undef slot_description
+#undef hardware_version
+#undef firmware_version
+
+#undef ck_token_info
+#undef serial_number
+#undef max_session_count
+#undef session_count
+#undef max_rw_session_count
+#undef rw_session_count
+#undef max_pin_len
+#undef min_pin_len
+#undef total_public_memory
+#undef free_public_memory
+#undef total_private_memory
+#undef free_private_memory
+#undef utc_time
+
+#undef ck_session_handle_t
+#undef ck_user_type_t
+#undef ck_state_t
+
+#undef ck_session_info
+#undef slot_id
+#undef device_error
+
+#undef ck_object_handle_t
+#undef ck_object_class_t
+#undef ck_hw_feature_type_t
+#undef ck_key_type_t
+#undef ck_certificate_type_t
+#undef ck_attribute_type_t
+
+#undef ck_attribute
+#undef value
+#undef value_len
+
+#undef ck_date
+
+#undef ck_mechanism_type_t
+
+#undef ck_mechanism
+#undef parameter
+#undef parameter_len
+
+#undef ck_mechanism_info
+#undef min_key_size
+#undef max_key_size
+
+#undef ck_rv_t
+#undef ck_notify_t
+
+#undef ck_function_list
+
+#undef ck_createmutex_t
+#undef ck_destroymutex_t
+#undef ck_lockmutex_t
+#undef ck_unlockmutex_t
+
+#undef ck_c_initialize_args
+#undef create_mutex
+#undef destroy_mutex
+#undef lock_mutex
+#undef unlock_mutex
+#undef reserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+/* System dependencies. */
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+#pragma pack(pop, cryptoki)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PKCS11_H */
diff --git a/src/plugins/preauth/pkinit/pkinit.exports b/src/plugins/preauth/pkinit/pkinit.exports
new file mode 100644
index 0000000..98e96c3
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit.exports
@@ -0,0 +1,2 @@
+preauthentication_client_1
+preauthentication_server_1
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
new file mode 100644
index 0000000..e18962e
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -0,0 +1,354 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#ifndef _PKINIT_H
+#define _PKINIT_H
+
+#include <krb5/krb5.h>
+#include <krb5/preauth_plugin.h>
+#include <k5-int-pkinit.h>
+#include <profile.h>
+#include "pkinit_accessor.h"
+
+/*
+ * It is anticipated that all the special checks currently
+ * required when talking to a Longhorn server will go away
+ * by the time it is officially released and all references
+ * to the longhorn global can be removed and any code
+ * #ifdef'd with LONGHORN_BETA_COMPAT can be removed.
+ * And this #define!
+ */
+#define LONGHORN_BETA_COMPAT 1
+#ifdef LONGHORN_BETA_COMPAT
+extern int longhorn; /* XXX Talking to a Longhorn server? */
+#endif
+
+
+#ifndef WITHOUT_PKCS11
+#include "pkcs11.h"
+
+#define PKCS11_MODNAME "opensc-pkcs11.so"
+#define PK_SIGLEN_GUESS 1000
+#define PK_NOSLOT 999999
+#endif
+
+#define DH_PROTOCOL 1
+#define RSA_PROTOCOL 2
+
+#define TD_TRUSTED_CERTIFIERS 104
+#define TD_INVALID_CERTIFICATES 105
+#define TD_DH_PARAMETERS 109
+
+#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 printf
+#else
+/* 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__
+ * hack for now. Fix all the uses eventually. */
+#define __FUNCTION__ __func__
+
+/* Macros to deal with converting between various data types... */
+#define PADATA_TO_KRB5DATA(pad, k5d) \
+ (k5d)->length = (pad)->length; (k5d)->data = (char *)(pad)->contents;
+#define OCTETDATA_TO_KRB5DATA(octd, k5d) \
+ (k5d)->length = (octd)->length; (k5d)->data = (char *)(octd)->data;
+
+extern const krb5_octet_data dh_oid;
+
+/*
+ * notes about crypto contexts:
+ *
+ * the basic idea is that there are crypto contexts that live at
+ * both the plugin level and request level. the identity context (that
+ * keeps info about your own certs and such) is separate because
+ * it is needed at different levels for the kdc and and the client.
+ * (the kdc's identity is at the plugin level, the client's identity
+ * information could change per-request.)
+ * the identity context is meant to have the entity's cert,
+ * a list of trusted and intermediate cas, a list of crls, and any
+ * pkcs11 information. the req context is meant to have the
+ * received certificate and the DH related information. the plugin
+ * context is meant to have global crypto information, i.e., OIDs
+ * and constant DH parameter information.
+ */
+
+/*
+ * plugin crypto context should keep plugin common information,
+ * eg., OIDs, known DHparams
+ */
+typedef struct _pkinit_plg_crypto_context *pkinit_plg_crypto_context;
+
+/*
+ * request crypto context should keep reqyest common information,
+ * eg., received credentials, DH parameters of this request
+ */
+typedef struct _pkinit_req_crypto_context *pkinit_req_crypto_context;
+
+/*
+ * identity context should keep information about credentials
+ * for the request, eg., my credentials, trusted ca certs,
+ * intermediate ca certs, crls, pkcs11 info
+ */
+typedef struct _pkinit_identity_crypto_context *pkinit_identity_crypto_context;
+
+/*
+ * this structure keeps information about the config options
+ */
+typedef struct _pkinit_plg_opts {
+ int require_eku; /* require EKU checking (default is true) */
+ int accept_secondary_eku;/* accept secondary EKU (default is false) */
+ int allow_upn; /* allow UPN-SAN instead of pkinit-SAN */
+ int dh_or_rsa; /* selects DH or RSA based pkinit */
+ int require_crl_checking; /* require CRL for a CA (default is false) */
+ int dh_min_bits; /* minimum DH modulus size allowed */
+} pkinit_plg_opts;
+
+/*
+ * this structure keeps options used for a given request
+ */
+typedef struct _pkinit_req_opts {
+ int require_eku;
+ int accept_secondary_eku;
+ int allow_upn;
+ int dh_or_rsa;
+ int require_crl_checking;
+ int dh_size; /* initial request DH modulus size (default=1024) */
+ int require_hostname_match;
+ int win2k_target;
+ int win2k_require_cksum;
+} pkinit_req_opts;
+
+/*
+ * information about identity from config file or command line
+ */
+
+#define PKINIT_ID_OPT_USER_IDENTITY 1
+#define PKINIT_ID_OPT_ANCHOR_CAS 2
+#define PKINIT_ID_OPT_INTERMEDIATE_CAS 3
+#define PKINIT_ID_OPT_CRLS 4
+#define PKINIT_ID_OPT_OCSP 5
+#define PKINIT_ID_OPT_DN_MAPPING 6 /* XXX ? */
+
+typedef struct _pkinit_identity_opts {
+ char *identity;
+ char **identity_alt;
+ char **anchors;
+ char **intermediates;
+ char **crls;
+ char *ocsp;
+ char *dn_mapping_file;
+ int idtype;
+ char *cert_filename;
+ char *key_filename;
+#ifndef WITHOUT_PKCS11
+ char *p11_module_name;
+ CK_SLOT_ID slotid;
+ char *token_label;
+ char *cert_id_string;
+ char *cert_label;
+#endif
+} pkinit_identity_opts;
+
+
+/*
+ * Client's plugin context
+ */
+struct _pkinit_context {
+ int magic;
+ pkinit_plg_crypto_context cryptoctx;
+ pkinit_plg_opts *opts;
+ pkinit_identity_opts *idopts;
+};
+typedef struct _pkinit_context *pkinit_context;
+
+/*
+ * Client's per-request context
+ */
+struct _pkinit_req_context {
+ int magic;
+ pkinit_req_crypto_context cryptoctx;
+ pkinit_req_opts *opts;
+ pkinit_identity_crypto_context idctx;
+ pkinit_identity_opts *idopts;
+ krb5_preauthtype pa_type;
+};
+typedef struct _pkinit_kdc_context *pkinit_kdc_context;
+
+/*
+ * KDC's (per-realm) plugin context
+ */
+struct _pkinit_kdc_context {
+ int magic;
+ pkinit_plg_crypto_context cryptoctx;
+ pkinit_plg_opts *opts;
+ pkinit_identity_crypto_context idctx;
+ pkinit_identity_opts *idopts;
+ char *realmname;
+ unsigned int realmname_len;
+};
+typedef struct _pkinit_req_context *pkinit_req_context;
+
+/*
+ * KDC's per-request context
+ */
+struct _pkinit_kdc_req_context {
+ int magic;
+ pkinit_req_crypto_context cryptoctx;
+ krb5_auth_pack *rcv_auth_pack;
+ krb5_auth_pack_draft9 *rcv_auth_pack9;
+ krb5_preauthtype pa_type;
+};
+typedef struct _pkinit_kdc_req_context *pkinit_kdc_req_context;
+
+/*
+ * Functions in pkinit_lib.c
+ */
+
+krb5_error_code pkinit_init_req_opts(pkinit_req_opts **);
+void pkinit_fini_req_opts(pkinit_req_opts *);
+
+krb5_error_code pkinit_init_plg_opts(pkinit_plg_opts **);
+void pkinit_fini_plg_opts(pkinit_plg_opts *);
+
+krb5_error_code pkinit_init_identity_opts(pkinit_identity_opts **idopts);
+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 */
+ pkinit_req_crypto_context req_cryptoctx, /* IN */
+ pkinit_identity_opts *idopts, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */
+ int do_matching, /* IN */
+ krb5_principal princ); /* IN (optional) */
+
+krb5_error_code pkinit_cert_matching
+ (krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ);
+
+/*
+ * initialization and free functions
+ */
+void init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in);
+void init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in);
+void init_krb5_reply_key_pack(krb5_reply_key_pack **in);
+void init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in);
+
+void init_krb5_auth_pack(krb5_auth_pack **in);
+void init_krb5_auth_pack_draft9(krb5_auth_pack_draft9 **in);
+void init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in);
+void init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in);
+void init_krb5_typed_data(krb5_typed_data **in);
+void init_krb5_subject_pk_info(krb5_subject_pk_info **in);
+
+void free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in);
+void free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in);
+void free_krb5_reply_key_pack(krb5_reply_key_pack **in);
+void free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in);
+void free_krb5_auth_pack(krb5_auth_pack **in);
+void free_krb5_auth_pack_draft9(krb5_context, krb5_auth_pack_draft9 **in);
+void free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in);
+void free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in);
+void free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in);
+void free_krb5_trusted_ca(krb5_trusted_ca ***in);
+void free_krb5_typed_data(krb5_typed_data ***in);
+void free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in);
+void free_krb5_algorithm_identifier(krb5_algorithm_identifier *in);
+void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in);
+void free_krb5_subject_pk_info(krb5_subject_pk_info **in);
+krb5_error_code pkinit_copy_krb5_octet_data(krb5_octet_data *dst, const krb5_octet_data *src);
+
+
+/*
+ * Functions in pkinit_profile.c
+ */
+krb5_error_code pkinit_kdcdefault_strings
+ (krb5_context context, const char *realmname, const char *option,
+ char ***ret_value);
+krb5_error_code pkinit_kdcdefault_string
+ (krb5_context context, const char *realmname, const char *option,
+ char **ret_value);
+krb5_error_code pkinit_kdcdefault_boolean
+ (krb5_context context, const char *realmname, const char *option,
+ 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);
+
+
+krb5_error_code pkinit_libdefault_strings
+ (krb5_context context, const krb5_data *realm,
+ 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);
+krb5_error_code pkinit_libdefault_boolean
+ (krb5_context context, const krb5_data *realm, const char *option,
+ int default_value, int *ret_value);
+krb5_error_code pkinit_libdefault_integer
+ (krb5_context context, const krb5_data *realm, const char *option,
+ int default_value, int *ret_value);
+
+/*
+ * 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_accessor.c b/src/plugins/preauth/pkinit/pkinit_accessor.c
new file mode 100644
index 0000000..e954ca3
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_accessor.c
@@ -0,0 +1,118 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#include <k5-int.h>
+#include "pkinit_accessor.h"
+
+#define DEF_FUNC_PTRS(type) \
+krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \
+krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **)
+
+#define DEF_FUNC_PTRS_ARRAY(type) \
+krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \
+krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***)
+
+DEF_FUNC_PTRS(krb5_auth_pack);
+DEF_FUNC_PTRS(krb5_auth_pack_draft9);
+DEF_FUNC_PTRS(krb5_kdc_dh_key_info);
+DEF_FUNC_PTRS(krb5_pa_pk_as_rep);
+DEF_FUNC_PTRS(krb5_pa_pk_as_rep_draft9);
+DEF_FUNC_PTRS(krb5_pa_pk_as_req);
+DEF_FUNC_PTRS(krb5_pa_pk_as_req_draft9);
+DEF_FUNC_PTRS(krb5_reply_key_pack);
+DEF_FUNC_PTRS(krb5_reply_key_pack_draft9);
+DEF_FUNC_PTRS_ARRAY(krb5_typed_data);
+
+/* special cases... */
+krb5_error_code (*k5int_decode_krb5_principal_name)
+ (const krb5_data *, krb5_principal_data **);
+
+krb5_error_code (*k5int_encode_krb5_td_dh_parameters)
+ (const krb5_algorithm_identifier **, krb5_data **code);
+krb5_error_code (*k5int_decode_krb5_td_dh_parameters)
+ (const krb5_data *, krb5_algorithm_identifier ***);
+
+krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers)
+ (const krb5_external_principal_identifier **, krb5_data **code);
+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 *);
+krb5_error_code (*k5int_encode_krb5_authdata_elt)
+ (const krb5_authdata *rep, krb5_data **code);
+
+
+
+/*
+ * Grab internal function pointers from the krb5int_accessor
+ * structure and make them available
+ */
+krb5_error_code
+pkinit_accessor_init(void)
+{
+ krb5_error_code retval;
+ krb5int_access k5int;
+
+ retval = krb5int_accessor(&k5int, KRB5INT_ACCESS_VERSION);
+ if (retval)
+ return retval;
+#define SET_PTRS(type) \
+k5int_encode_##type = k5int.encode_##type; \
+k5int_decode_##type = k5int.decode_##type;
+
+ SET_PTRS(krb5_auth_pack);
+ SET_PTRS(krb5_auth_pack_draft9);
+ SET_PTRS(krb5_kdc_dh_key_info);
+ SET_PTRS(krb5_pa_pk_as_rep);
+ SET_PTRS(krb5_pa_pk_as_rep_draft9);
+ SET_PTRS(krb5_pa_pk_as_req);
+ SET_PTRS(krb5_pa_pk_as_req_draft9);
+ SET_PTRS(krb5_reply_key_pack);
+ SET_PTRS(krb5_reply_key_pack_draft9);
+ SET_PTRS(krb5_td_dh_parameters);
+ SET_PTRS(krb5_td_trusted_certifiers);
+ SET_PTRS(krb5_typed_data);
+
+ /* 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;
+ k5int_encode_krb5_authdata_elt = k5int.encode_krb5_authdata_elt;
+ return 0;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.h b/src/plugins/preauth/pkinit/pkinit_accessor.h
new file mode 100644
index 0000000..ba82533
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_accessor.h
@@ -0,0 +1,83 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#ifndef _PKINIT_ACCESSOR_H
+#define _PKINIT_ACCESSOR_H
+
+/*
+ * Function prototypes
+ */
+krb5_error_code pkinit_accessor_init(void);
+
+#define DEF_EXT_FUNC_PTRS(type) \
+extern krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \
+extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **)
+
+#define DEF_EXT_FUNC_PTRS_ARRAY(type) \
+extern krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \
+extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***)
+
+DEF_EXT_FUNC_PTRS(krb5_auth_pack);
+DEF_EXT_FUNC_PTRS(krb5_auth_pack_draft9);
+DEF_EXT_FUNC_PTRS(krb5_kdc_dh_key_info);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep_draft9);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req_draft9);
+DEF_EXT_FUNC_PTRS(krb5_reply_key_pack);
+DEF_EXT_FUNC_PTRS(krb5_reply_key_pack_draft9);
+DEF_EXT_FUNC_PTRS_ARRAY(krb5_typed_data);
+
+/* special cases... */
+extern krb5_error_code (*k5int_decode_krb5_principal_name)
+ (const krb5_data *, krb5_principal_data **);
+
+extern krb5_error_code (*k5int_encode_krb5_td_dh_parameters)
+ (const krb5_algorithm_identifier **, krb5_data **code);
+extern krb5_error_code (*k5int_decode_krb5_td_dh_parameters)
+ (const krb5_data *, krb5_algorithm_identifier ***);
+
+extern krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers)
+ (const krb5_external_principal_identifier **, krb5_data **code);
+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 *);
+extern krb5_error_code (*k5int_encode_krb5_authdata_elt)
+ (const krb5_authdata *rep, krb5_data **code);
+
+#endif /* _PKINIT_ACCESSOR_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
new file mode 100644
index 0000000..0d6da4d
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -0,0 +1,1492 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <sys/stat.h>
+
+#include "pkinit.h"
+
+#ifdef LONGHORN_BETA_COMPAT
+/*
+ * It is anticipated that all the special checks currently
+ * required when talking to a Longhorn server will go away
+ * by the time it is officially released and all references
+ * to the longhorn global can be removed and any code
+ * #ifdef'd with LONGHORN_BETA_COMPAT can be removed.
+ *
+ * Current testing (20070620) is against a patched Beta 3
+ * version of Longhorn. Most, if not all, problems should
+ * be fixed in SP1 of Longhorn.
+ */
+int longhorn = 0; /* Talking to a Longhorn server? */
+#endif
+
+krb5_error_code pkinit_client_process
+ (krb5_context context, void *plugin_context, void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req * request, krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request, krb5_pa_data *in_padata,
+ krb5_prompter_fct prompter, void *prompter_data,
+ preauth_get_as_key_proc gak_fct, void *gak_data,
+ krb5_data * salt, krb5_data * s2kparams,
+ krb5_keyblock * as_key, krb5_pa_data *** out_padata);
+
+krb5_error_code pkinit_client_tryagain
+ (krb5_context context, void *plugin_context, void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req * request, krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_padata, krb5_error *err_reply,
+ krb5_prompter_fct prompter, void *prompter_data,
+ preauth_get_as_key_proc gak_fct, void *gak_data,
+ krb5_data * salt, krb5_data * s2kparams,
+ krb5_keyblock * as_key, krb5_pa_data *** out_padata);
+
+void pkinit_client_req_init
+ (krb5_context contex, void *plugin_context, void **request_context);
+
+void pkinit_client_req_fini
+ (krb5_context context, void *plugin_context, void *request_context);
+
+krb5_error_code pa_pkinit_gen_req
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqctx, krb5_kdc_req * request,
+ krb5_pa_data * in_padata, krb5_pa_data *** out_padata,
+ krb5_prompter_fct prompter, void *prompter_data,
+ krb5_get_init_creds_opt *gic_opt);
+
+krb5_error_code pkinit_as_req_create
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqctx, krb5_timestamp ctsec,
+ krb5_int32 cusec, krb5_ui_4 nonce,
+ const krb5_checksum * cksum, krb5_principal server,
+ krb5_data ** as_req);
+
+krb5_error_code pkinit_as_rep_parse
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqctx, krb5_preauthtype pa_type,
+ krb5_kdc_req * request, const krb5_data * as_rep,
+ krb5_keyblock * key_block, krb5_enctype etype, krb5_data *);
+
+krb5_error_code pa_pkinit_parse_rep
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqcxt, krb5_kdc_req * request,
+ krb5_pa_data * in_padata, krb5_enctype etype,
+ krb5_keyblock * as_key, krb5_data *);
+
+static int pkinit_client_plugin_init(krb5_context context, void **blob);
+static void pkinit_client_plugin_fini(krb5_context context, void *blob);
+
+krb5_error_code
+pa_pkinit_gen_req(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_kdc_req * request,
+ krb5_pa_data * in_padata,
+ krb5_pa_data *** out_padata,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_get_init_creds_opt *gic_opt)
+{
+
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_data *out_data = NULL;
+ krb5_timestamp ctsec = 0;
+ krb5_int32 cusec = 0;
+ krb5_ui_4 nonce = 0;
+ krb5_checksum cksum;
+ krb5_data *der_req = NULL;
+ krb5_pa_data **return_pa_data = NULL;
+
+ cksum.contents = NULL;
+ reqctx->pa_type = in_padata->pa_type;
+
+ 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");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx,
+ reqctx->idctx, request->server);
+ if (retval) {
+ pkiDebug("pkinit_get_kdc_cert returned %d\n", retval);
+ goto cleanup;
+ }
+
+ /* checksum of the encoded KDC-REQ-BODY */
+ 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;
+ }
+
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
+ der_req, &cksum);
+ if (retval)
+ goto cleanup;
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size (%d)\n", der_req->length);
+ print_buffer(der_req->data, der_req->length);
+#endif
+
+ retval = krb5_us_timeofday(context, &ctsec, &cusec);
+ if (retval)
+ goto cleanup;
+
+ /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the
+ * same as in the AS_REQ. However, if we pick a different nonce, then we
+ * need to remember that info when AS_REP is returned. I'm choosing to
+ * reuse the AS_REQ nonce.
+ */
+ nonce = request->nonce;
+
+ retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec,
+ nonce, &cksum, request->server, &out_data);
+ if (retval || !out_data->length) {
+ pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n",
+ (int) retval);
+ goto cleanup;
+ }
+ retval = ENOMEM;
+ /*
+ * The most we'll return is two pa_data, normally just one.
+ * We need to make room for the NULL terminator.
+ */
+ return_pa_data = (krb5_pa_data **) malloc(3 * sizeof(krb5_pa_data *));
+ if (return_pa_data == NULL)
+ goto cleanup;
+
+ return_pa_data[1] = NULL; /* in case of an early trip to cleanup */
+ return_pa_data[2] = NULL; /* Terminate the list */
+
+ return_pa_data[0] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
+ if (return_pa_data[0] == NULL)
+ goto cleanup;
+
+ return_pa_data[1] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
+ if (return_pa_data[1] == NULL)
+ goto cleanup;
+
+ return_pa_data[0]->magic = KV5M_PA_DATA;
+
+ if (in_padata->pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
+ return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ else
+ return_pa_data[0]->pa_type = in_padata->pa_type;
+ return_pa_data[0]->length = out_data->length;
+ return_pa_data[0]->contents = (krb5_octet *) out_data->data;
+
+#ifdef LONGHORN_BETA_COMPAT
+ /*
+ * LH Beta 3 requires the extra pa-data, even for RFC requests,
+ * in order to get the Checksum rather than a Nonce in the reply.
+ * This can be removed when LH SP1 is released.
+ */
+ if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
+ && reqctx->opts->win2k_require_cksum) || (longhorn == 1)) {
+#else
+ if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
+ && reqctx->opts->win2k_require_cksum)) {
+#endif
+ return_pa_data[1]->pa_type = 132;
+ return_pa_data[1]->length = 0;
+ return_pa_data[1]->contents = NULL;
+ } else {
+ free(return_pa_data[1]);
+ return_pa_data[1] = NULL; /* Move the list terminator */
+ }
+ *out_padata = return_pa_data;
+ retval = 0;
+
+ cleanup:
+ if (der_req != NULL)
+ krb5_free_data(context, der_req);
+
+ if (out_data != NULL)
+ free(out_data);
+
+ if (retval) {
+ if (return_pa_data) {
+ if (return_pa_data[0] != NULL)
+ free(return_pa_data[0]);
+ if (return_pa_data[1] != NULL)
+ free(return_pa_data[1]);
+ free(return_pa_data);
+ }
+ if (out_data) {
+ free(out_data->data);
+ free(out_data);
+ }
+ }
+ return retval;
+}
+
+krb5_error_code
+pkinit_as_req_create(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_timestamp ctsec,
+ krb5_int32 cusec,
+ krb5_ui_4 nonce,
+ const krb5_checksum * cksum,
+ krb5_principal server,
+ krb5_data ** as_req)
+{
+ krb5_error_code retval = ENOMEM;
+ krb5_subject_pk_info *info = NULL;
+ krb5_data *coded_auth_pack = NULL;
+ krb5_auth_pack *auth_pack = NULL;
+ krb5_pa_pk_as_req *req = NULL;
+ krb5_auth_pack_draft9 *auth_pack9 = NULL;
+ krb5_pa_pk_as_req_draft9 *req9 = NULL;
+ int protocol = reqctx->opts->dh_or_rsa;
+
+ pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type);
+
+ /* Create the authpack */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ protocol = RSA_PROTOCOL;
+ init_krb5_auth_pack_draft9(&auth_pack9);
+ if (auth_pack9 == NULL)
+ goto cleanup;
+ auth_pack9->pkAuthenticator.ctime = ctsec;
+ auth_pack9->pkAuthenticator.cusec = cusec;
+ auth_pack9->pkAuthenticator.nonce = nonce;
+ auth_pack9->pkAuthenticator.kdcName = server;
+ auth_pack9->pkAuthenticator.kdcRealm.magic = 0;
+ auth_pack9->pkAuthenticator.kdcRealm.data =
+ (unsigned char *)server->realm.data;
+ auth_pack9->pkAuthenticator.kdcRealm.length = server->realm.length;
+ free(cksum->contents);
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ init_krb5_subject_pk_info(&info);
+ if (info == NULL)
+ goto cleanup;
+ init_krb5_auth_pack(&auth_pack);
+ if (auth_pack == NULL)
+ goto cleanup;
+ auth_pack->pkAuthenticator.ctime = ctsec;
+ auth_pack->pkAuthenticator.cusec = cusec;
+ auth_pack->pkAuthenticator.nonce = nonce;
+ auth_pack->pkAuthenticator.paChecksum = *cksum;
+ auth_pack->clientDHNonce.length = 0;
+ auth_pack->clientPublicValue = info;
+
+ /* add List of CMS algorithms */
+ retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ &auth_pack->supportedCMSTypes);
+ if (retval)
+ goto cleanup;
+ break;
+ default:
+ pkiDebug("as_req: unrecognized pa_type = %d\n",
+ (int)reqctx->pa_type);
+ retval = -1;
+ goto cleanup;
+ }
+
+ switch(protocol) {
+ case DH_PROTOCOL:
+ pkiDebug("as_req: DH key transport algorithm\n");
+ retval = pkinit_copy_krb5_octet_data(&info->algorithm.algorithm, &dh_oid);
+ if (retval) {
+ pkiDebug("failed to copy dh_oid\n");
+ goto cleanup;
+ }
+
+ /* create client-side DH keys */
+ if ((retval = client_create_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size,
+ &info->algorithm.parameters.data,
+ &info->algorithm.parameters.length,
+ &info->subjectPublicKey.data,
+ &info->subjectPublicKey.length)) != 0) {
+ pkiDebug("failed to create dh parameters\n");
+ goto cleanup;
+ }
+ break;
+ case RSA_PROTOCOL:
+ pkiDebug("as_req: RSA key transport algorithm\n");
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ auth_pack9->clientPublicValue = NULL;
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_subject_pk_info(&info);
+ auth_pack->clientPublicValue = NULL;
+ break;
+ }
+ break;
+ default:
+ pkiDebug("as_req: unknown key transport protocol %d\n",
+ protocol);
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* Encode the authpack */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = k5int_encode_krb5_auth_pack(auth_pack, &coded_auth_pack);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9,
+ &coded_auth_pack);
+ break;
+ }
+ if (retval) {
+ pkiDebug("failed to encode the AuthPack %d\n", retval);
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)coded_auth_pack->data,
+ coded_auth_pack->length,
+ "/tmp/client_auth_pack");
+#endif
+
+ /* create PKCS7 object from authpack */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ init_krb5_pa_pk_as_req(&req);
+ if (req == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1,
+ (unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
+ &req->signedAuthPack.data, &req->signedAuthPack.length);
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)req->signedAuthPack.data,
+ req->signedAuthPack.length,
+ "/tmp/client_signed_data");
+#endif
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ init_krb5_pa_pk_as_req_draft9(&req9);
+ if (req9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1,
+ (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) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ /* create a list of trusted CAs */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers);
+ if (retval)
+ goto cleanup;
+ retval = create_issuerAndSerial(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, &req->kdcPkId.data,
+ &req->kdcPkId.length);
+ if (retval)
+ goto cleanup;
+
+ /* Encode the as-req */
+ retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+#if 0
+ /* W2K3 KDC doesn't like this */
+ retval = create_krb5_trustedCas(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, 1, &req9->trustedCertifiers);
+ if (retval)
+ goto cleanup;
+
+#endif
+ retval = create_issuerAndSerial(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, &req9->kdcCert.data,
+ &req9->kdcCert.length);
+ if (retval)
+ goto cleanup;
+ /* Encode the as-req */
+ retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req);
+ break;
+ }
+#ifdef DEBUG_ASN1
+ if (!retval)
+ print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length,
+ "/tmp/client_as_req");
+#endif
+
+cleanup:
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_auth_pack(&auth_pack);
+ free_krb5_pa_pk_as_req(&req);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ free_krb5_pa_pk_as_req_draft9(&req9);
+ free(auth_pack9);
+ break;
+ }
+
+
+ pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval);
+
+ return retval;
+}
+
+krb5_error_code
+pa_pkinit_parse_rep(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_kdc_req * request,
+ krb5_pa_data * in_padata,
+ krb5_enctype etype,
+ krb5_keyblock * as_key,
+ krb5_data *encoded_request)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_data asRep = { 0, 0, NULL};
+
+ /*
+ * One way or the other - success or failure - no other PA systems can
+ * work if the server sent us a PKINIT reply, since only we know how to
+ * decrypt the key.
+ */
+ if ((in_padata == NULL) || (in_padata->length == 0)) {
+ pkiDebug("pa_pkinit_parse_rep: no in_padata\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ asRep.data = (char *) in_padata->contents;
+ asRep.length = in_padata->length;
+
+ retval =
+ pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type,
+ request, &asRep, as_key, etype, encoded_request);
+ if (retval) {
+ pkiDebug("pkinit_as_rep_parse returned %d (%s)\n",
+ retval, error_message(retval));
+ goto cleanup;
+ }
+
+ retval = 0;
+
+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.
+ */
+krb5_error_code
+pkinit_as_rep_parse(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_preauthtype pa_type,
+ krb5_kdc_req *request,
+ const krb5_data *as_rep,
+ krb5_keyblock *key_block,
+ krb5_enctype etype,
+ krb5_data *encoded_request)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_pa_pk_as_rep *kdc_reply = NULL;
+ krb5_kdc_dh_key_info *kdc_dh = NULL;
+ krb5_reply_key_pack *key_pack = NULL;
+ krb5_reply_key_pack_draft9 *key_pack9 = NULL;
+ krb5_octet_data dh_data = { 0, 0, NULL };
+ unsigned char *client_key = NULL, *kdc_hostname = NULL;
+ unsigned int client_key_len = 0;
+ krb5_checksum cksum = {0, 0, 0, NULL};
+ krb5_data k5data;
+ int valid_san = 0;
+ int valid_eku = 0;
+ int need_eku_checking = 1;
+
+ assert((as_rep != NULL) && (key_block != NULL));
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)as_rep->data, as_rep->length,
+ "/tmp/client_as_rep");
+#endif
+
+ if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) {
+ pkiDebug("decode_krb5_as_rep failed %d\n", retval);
+ return retval;
+ }
+
+ switch(kdc_reply->choice) {
+ case choice_pa_pk_as_rep_dhInfo:
+ pkiDebug("as_rep: DH key transport algorithm\n");
+#ifdef DEBUG_ASN1
+ print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
+ kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata");
+#endif
+ if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER,
+ reqctx->opts->require_crl_checking,
+ kdc_reply->u.dh_Info.dhSignedData.data,
+ kdc_reply->u.dh_Info.dhSignedData.length,
+ &dh_data.data, &dh_data.length, NULL, NULL)) != 0) {
+ pkiDebug("failed to verify pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ pkiDebug("as_rep: RSA key transport algorithm\n");
+ if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, pa_type,
+ reqctx->opts->require_crl_checking,
+ kdc_reply->u.encKeyPack.data,
+ kdc_reply->u.encKeyPack.length,
+ &dh_data.data, &dh_data.length)) != 0) {
+ pkiDebug("failed to verify pkcs7 enveloped data\n");
+ goto cleanup;
+ }
+ break;
+ default:
+ pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
+ retval = -1;
+ goto cleanup;
+ }
+
+ 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_eku) {
+ pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
+ goto cleanup;
+ }
+ } else
+ pkiDebug("%s: skipping EKU check\n", __FUNCTION__);
+
+ OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);
+
+ switch(kdc_reply->choice) {
+ case choice_pa_pk_as_rep_dhInfo:
+#ifdef DEBUG_ASN1
+ print_buffer_bin(dh_data.data, dh_data.length,
+ "/tmp/client_dh_key");
+#endif
+ if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data,
+ &kdc_dh)) != 0) {
+ pkiDebug("failed to decode kdc_dh_key_info\n");
+ goto cleanup;
+ }
+
+ /* client after KDC reply */
+ if ((retval = client_process_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ kdc_dh->subjectPublicKey.data,
+ kdc_dh->subjectPublicKey.length,
+ &client_key, &client_key_len)) != 0) {
+ pkiDebug("failed to process dh params\n");
+ goto cleanup;
+ }
+
+ retval = pkinit_octetstring2key(context, etype, client_key,
+ client_key_len, key_block);
+ if (retval) {
+ pkiDebug("failed to create key pkinit_octetstring2key %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+#ifdef DEBUG_ASN1
+ print_buffer_bin(dh_data.data, dh_data.length,
+ "/tmp/client_key_pack");
+#endif
+ if ((retval = k5int_decode_krb5_reply_key_pack(&k5data,
+ &key_pack)) != 0) {
+ pkiDebug("failed to decode reply_key_pack\n");
+#ifdef LONGHORN_BETA_COMPAT
+ /*
+ * LH Beta 3 requires the extra pa-data, even for RFC requests,
+ * in order to get the Checksum rather than a Nonce in the reply.
+ * This can be removed when LH SP1 is released.
+ */
+ if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0)
+#else
+ if (pa_type == KRB5_PADATA_PK_AS_REP)
+#endif
+ goto cleanup;
+ else {
+ if ((retval =
+ k5int_decode_krb5_reply_key_pack_draft9(&k5data,
+ &key_pack9)) != 0) {
+ pkiDebug("failed to decode reply_key_pack_draft9\n");
+ goto cleanup;
+ }
+ pkiDebug("decode reply_key_pack_draft9\n");
+ if (key_pack9->nonce != request->nonce) {
+ pkiDebug("nonce in AS_REP=%d doesn't match AS_REQ=%d\n", key_pack9->nonce, request->nonce);
+ retval = -1;
+ goto cleanup;
+ }
+ krb5_copy_keyblock_contents(context, &key_pack9->replyKey,
+ key_block);
+ break;
+ }
+ }
+ /*
+ * This is hack but Windows sends back SHA1 checksum
+ * with checksum type of 14. There is currently no
+ * checksum type of 14 defined.
+ */
+ if (key_pack->asChecksum.checksum_type == 14)
+ key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA;
+ retval = krb5_c_make_checksum(context,
+ key_pack->asChecksum.checksum_type,
+ &key_pack->replyKey,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ encoded_request, &cksum);
+ if (retval) {
+ pkiDebug("failed to make a checksum\n");
+ goto cleanup;
+ }
+
+ if ((cksum.length != key_pack->asChecksum.length) ||
+ memcmp(cksum.contents, key_pack->asChecksum.contents,
+ cksum.length)) {
+ pkiDebug("failed to match the checksums\n");
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size (%d)\n",
+ encoded_request->length);
+ print_buffer(encoded_request->data, encoded_request->length);
+ pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length);
+ print_buffer(key_pack->replyKey.contents,
+ key_pack->replyKey.length);
+ pkiDebug("received checksum type=%d size=%d ",
+ key_pack->asChecksum.checksum_type,
+ key_pack->asChecksum.length);
+ print_buffer(key_pack->asChecksum.contents,
+ key_pack->asChecksum.length);
+ pkiDebug("expected checksum type=%d size=%d ",
+ cksum.checksum_type, cksum.length);
+ print_buffer(cksum.contents, cksum.length);
+#endif
+ goto cleanup;
+ } else
+ pkiDebug("checksums match\n");
+
+ krb5_copy_keyblock_contents(context, &key_pack->replyKey,
+ key_block);
+
+ break;
+ default:
+ pkiDebug("unknow as_rep type %d\n", kdc_reply->choice);
+ goto cleanup;
+ }
+
+ retval = 0;
+
+cleanup:
+ if (dh_data.data != NULL)
+ free(dh_data.data);
+ if (client_key != NULL)
+ free(client_key);
+ free_krb5_kdc_dh_key_info(&kdc_dh);
+ free_krb5_pa_pk_as_rep(&kdc_reply);
+
+ if (key_pack != NULL) {
+ free_krb5_reply_key_pack(&key_pack);
+ if (cksum.contents != NULL)
+ free(cksum.contents);
+ }
+ if (key_pack9 != NULL)
+ free_krb5_reply_key_pack_draft9(&key_pack9);
+
+ if (kdc_hostname != NULL)
+ free(kdc_hostname);
+
+ pkiDebug("pkinit_as_rep_parse returning %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+}
+
+static void
+pkinit_client_profile(krb5_context context,
+ pkinit_context plgctx,
+ 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);
+
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "pkinit_win2k",
+ reqctx->opts->win2k_target,
+ &reqctx->opts->win2k_target);
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "pkinit_win2k_require_binding",
+ reqctx->opts->win2k_require_cksum,
+ &reqctx->opts->win2k_require_cksum);
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "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);
+ 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);
+ }
+#ifdef LONGHORN_BETA_COMPAT
+ /* Temporarily just set global flag from config file */
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "pkinit_longhorn",
+ 0,
+ &longhorn);
+#endif
+
+ /* Only process anchors here if they were not specified on command line */
+ if (reqctx->idopts->anchors == NULL)
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_anchors",
+ &reqctx->idopts->anchors);
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_pool",
+ &reqctx->idopts->intermediates);
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_revoke",
+ &reqctx->idopts->crls);
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_identities",
+ &reqctx->idopts->identity_alt);
+}
+
+krb5_error_code
+pkinit_client_process(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_padata,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ void *gak_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ krb5_pa_data ***out_padata)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_enctype enctype = -1;
+ krb5_data *cdata = NULL;
+ int processing_request = 0;
+ pkinit_context plgctx = (pkinit_context)plugin_context;
+ pkinit_req_context reqctx = (pkinit_req_context)request_context;
+
+ pkiDebug("pkinit_client_process %p %p %p %p\n",
+ context, plgctx, reqctx, request);
+
+ if (plgctx == NULL || reqctx == NULL)
+ return EINVAL;
+
+ switch ((int) in_padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
+ processing_request = 1;
+ break;
+
+ case KRB5_PADATA_PK_AS_REP:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ if (in_padata->length == 0) {
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
+ in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
+ processing_request = 1;
+ } else {
+ pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n");
+ in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ }
+ break;
+ default:
+ pkiDebug("unrecognized patype = %d for PKINIT\n",
+ in_padata->pa_type);
+ return EINVAL;
+ }
+
+ if (processing_request) {
+ pkinit_client_profile(context, plgctx, reqctx, request);
+ pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data);
+ retval = pkinit_identity_initialize(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idopts,
+ reqctx->idctx, 1, request->client);
+ if (retval) {
+ pkiDebug("pkinit_identity_initialize 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, gic_opt);
+ } else {
+ /*
+ * Get the enctype of the reply.
+ */
+ retval = (*get_data_proc)(context, rock,
+ krb5plugin_preauth_client_get_etype, &cdata);
+ 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);
+ retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
+ in_padata, enctype, as_key,
+ encoded_previous_request);
+ }
+
+ pkiDebug("pkinit_client_process: returning %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+}
+
+krb5_error_code
+pkinit_client_tryagain(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_padata,
+ krb5_error *err_reply,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ void *gak_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ krb5_pa_data ***out_padata)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ pkinit_context plgctx = (pkinit_context)plugin_context;
+ pkinit_req_context reqctx = (pkinit_req_context)request_context;
+ krb5_typed_data **typed_data = NULL;
+ krb5_data scratch;
+ krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
+ krb5_algorithm_identifier **algId = NULL;
+ int do_again = 0;
+
+ pkiDebug("pkinit_client_tryagain %p %p %p %p\n",
+ context, plgctx, reqctx, request);
+
+ if (reqctx->pa_type != in_padata->pa_type)
+ return retval;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)err_reply->e_data.data,
+ err_reply->e_data.length, "/tmp/client_edata");
+#endif
+ retval = k5int_decode_krb5_typed_data(&err_reply->e_data, &typed_data);
+ if (retval) {
+ pkiDebug("decode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(typed_data[0]->data, typed_data[0]->length,
+ "/tmp/client_typed_data");
+#endif
+ OCTETDATA_TO_KRB5DATA(typed_data[0], &scratch);
+
+ switch(typed_data[0]->type) {
+ case TD_TRUSTED_CERTIFIERS:
+ case TD_INVALID_CERTIFICATES:
+ retval = k5int_decode_krb5_td_trusted_certifiers(&scratch,
+ &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("failed to decode sequence of trusted certifiers\n");
+ goto cleanup;
+ }
+ retval = pkinit_process_td_trusted_certifiers(context,
+ plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx,
+ krb5_trusted_certifiers, typed_data[0]->type);
+ if (!retval)
+ do_again = 1;
+ break;
+ case TD_DH_PARAMETERS:
+ retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
+ if (retval) {
+ pkiDebug("failed to decode td_dh_parameters\n");
+ goto cleanup;
+ }
+ retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, algId,
+ &reqctx->opts->dh_size);
+ if (!retval)
+ do_again = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (do_again) {
+ retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata,
+ out_padata, prompter, prompter_data, gic_opt);
+ if (retval)
+ goto cleanup;
+ }
+
+ retval = 0;
+cleanup:
+ if (krb5_trusted_certifiers != NULL)
+ free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+
+ if (algId != NULL)
+ free_krb5_algorithm_identifiers(&algId);
+
+ pkiDebug("pkinit_client_tryagain: returning %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+}
+
+static int
+pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
+{
+ return PA_REAL;
+}
+
+static krb5_preauthtype supported_client_pa_types[] = {
+ KRB5_PADATA_PK_AS_REP,
+ KRB5_PADATA_PK_AS_REQ,
+ KRB5_PADATA_PK_AS_REP_OLD,
+ KRB5_PADATA_PK_AS_REQ_OLD,
+ 0
+};
+
+void
+pkinit_client_req_init(krb5_context context,
+ void *plugin_context,
+ void **request_context)
+{
+ krb5_error_code retval = ENOMEM;
+ struct _pkinit_req_context *reqctx = NULL;
+ struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context;
+
+ *request_context = NULL;
+
+ reqctx = (struct _pkinit_req_context *) malloc(sizeof(*reqctx));
+ if (reqctx == NULL)
+ return;
+ memset(reqctx, 0, sizeof(*reqctx));
+
+ reqctx->magic = PKINIT_REQ_CTX_MAGIC;
+ reqctx->cryptoctx = NULL;
+ reqctx->opts = NULL;
+ reqctx->idctx = NULL;
+ reqctx->idopts = NULL;
+
+ retval = pkinit_init_req_opts(&reqctx->opts);
+ if (retval)
+ goto cleanup;
+
+ reqctx->opts->require_eku = plgctx->opts->require_eku;
+ reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku;
+ reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa;
+ reqctx->opts->allow_upn = plgctx->opts->allow_upn;
+ reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking;
+
+ retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
+ if (retval)
+ goto cleanup;
+
+ retval = pkinit_init_identity_crypto(&reqctx->idctx);
+ if (retval)
+ goto cleanup;
+
+ retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts);
+ if (retval)
+ goto cleanup;
+
+ *request_context = (void *) reqctx;
+ pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
+
+cleanup:
+ if (retval) {
+ if (reqctx->idctx != NULL)
+ pkinit_fini_identity_crypto(reqctx->idctx);
+ if (reqctx->cryptoctx != NULL)
+ pkinit_fini_req_crypto(reqctx->cryptoctx);
+ if (reqctx->opts != NULL)
+ pkinit_fini_req_opts(reqctx->opts);
+ if (reqctx->idopts != NULL)
+ pkinit_fini_identity_opts(reqctx->idopts);
+ free(reqctx);
+ }
+
+ return;
+}
+
+void
+pkinit_client_req_fini(krb5_context context,
+ void *plugin_context,
+ void *request_context)
+{
+ struct _pkinit_req_context *reqctx =
+ (struct _pkinit_req_context *)request_context;
+
+ pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
+ if (reqctx == NULL)
+ return;
+ if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) {
+ pkiDebug("%s: Bad magic value (%x) in req ctx\n",
+ __FUNCTION__, reqctx->magic);
+ return;
+ }
+ if (reqctx->opts != NULL)
+ pkinit_fini_req_opts(reqctx->opts);
+
+ if (reqctx->cryptoctx != NULL)
+ pkinit_fini_req_crypto(reqctx->cryptoctx);
+
+ if (reqctx->idctx != NULL)
+ pkinit_fini_identity_crypto(reqctx->idctx);
+
+ if (reqctx->idopts != NULL)
+ pkinit_fini_identity_opts(reqctx->idopts);
+
+ free(reqctx);
+ return;
+}
+
+static void
+pkinit_fini_client_profile(krb5_context context, pkinit_context plgctx)
+{
+ /* This should clean up anything allocated in pkinit_init_client_profile */
+}
+
+static krb5_error_code
+pkinit_init_client_profile(krb5_context context, pkinit_context plgctx)
+{
+ return 0;
+}
+
+static int
+pkinit_client_plugin_init(krb5_context context, void **blob)
+{
+ krb5_error_code retval = ENOMEM;
+ struct _pkinit_context *ctx = NULL;
+
+ ctx = (struct _pkinit_context *)calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return ENOMEM;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->magic = PKINIT_CTX_MAGIC;
+ ctx->opts = NULL;
+ ctx->cryptoctx = NULL;
+ ctx->idopts = NULL;
+
+ retval = pkinit_accessor_init();
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_plg_opts(&ctx->opts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_plg_crypto(&ctx->cryptoctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_identity_opts(&ctx->idopts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_client_profile(context, ctx);
+ if (retval)
+ goto errout;
+
+ *blob = ctx;
+
+ pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx);
+
+errout:
+ if (retval)
+ pkinit_client_plugin_fini(context, ctx);
+
+ return retval;
+}
+
+static void
+pkinit_client_plugin_fini(krb5_context context, void *blob)
+{
+ struct _pkinit_context *ctx = (struct _pkinit_context *)blob;
+
+ if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) {
+ pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx);
+ return;
+ }
+ pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx);
+
+ pkinit_fini_client_profile(context, ctx);
+ pkinit_fini_identity_opts(ctx->idopts);
+ pkinit_fini_plg_crypto(ctx->cryptoctx);
+ pkinit_fini_plg_opts(ctx->opts);
+ free(ctx);
+
+}
+
+static krb5_error_code
+add_string_to_array(krb5_context context, char ***array, const char *addition)
+{
+ char **out = NULL;
+
+ if (*array == NULL) {
+ out = malloc(2 * sizeof(char *));
+ if (out == NULL)
+ return ENOMEM;
+ out[1] = NULL;
+ out[0] = strdup(addition);
+ if (out[0] == NULL) {
+ free(out);
+ return ENOMEM;
+ }
+ } else {
+ int i;
+ char **a = *array;
+ for (i = 0; a[i] != NULL; i++);
+ out = malloc( (i + 2) * sizeof(char *));
+ if (out == NULL)
+ return ENOMEM;
+ for (i = 0; a[i] != NULL; i++) {
+ out[i] = a[i];
+ }
+ out[i++] = strdup(addition);
+ if (out == NULL) {
+ free(out);
+ return ENOMEM;
+ }
+ out[i] = NULL;
+ free(*array);
+ }
+ *array = out;
+
+ return 0;
+}
+static krb5_error_code
+handle_gic_opt(krb5_context context,
+ struct _pkinit_context *plgctx,
+ const char *attr,
+ const char *value)
+{
+ krb5_error_code retval;
+
+ if (strcmp(attr, "X509_user_identity") == 0) {
+ if (plgctx->idopts->identity != NULL) {
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ "X509_user_identity can not be given twice\n");
+ return KRB5_PREAUTH_FAILED;
+ }
+ plgctx->idopts->identity = strdup(value);
+ if (plgctx->idopts->identity == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ "Could not duplicate X509_user_identity value\n");
+ return ENOMEM;
+ }
+ } else if (strcmp(attr, "X509_anchors") == 0) {
+ retval = add_string_to_array(context, &plgctx->idopts->anchors, value);
+ if (retval)
+ return retval;
+ } else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) {
+ if (strcmp(value, "yes") == 0) {
+ pkiDebug("Setting flag to use RSA_PROTOCOL\n");
+ plgctx->opts->dh_or_rsa = RSA_PROTOCOL;
+ }
+ }
+ return 0;
+}
+
+static krb5_error_code
+pkinit_client_gic_opt(krb5_context context,
+ void *plugin_context,
+ krb5_get_init_creds_opt *gic_opt,
+ const char *attr,
+ const char *value)
+{
+ krb5_error_code retval;
+ struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context;
+
+ pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value);
+ retval = handle_gic_opt(context, plgctx, attr, value);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
+ "pkinit", /* name */
+ supported_client_pa_types, /* pa_type_list */
+ NULL, /* enctype_list */
+ pkinit_client_plugin_init, /* (*init) */
+ pkinit_client_plugin_fini, /* (*fini) */
+ pkinit_client_get_flags, /* (*flags) */
+ pkinit_client_req_init, /* (*client_req_init) */
+ pkinit_client_req_fini, /* (*client_req_fini) */
+ pkinit_client_process, /* (*process) */
+ pkinit_client_tryagain, /* (*tryagain) */
+ pkinit_client_gic_opt /* (*gic_opt) */
+};
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
new file mode 100644
index 0000000..779c08c
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -0,0 +1,623 @@
+/*
+ * 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
+};
+
+/*
+ * storage types for identity information
+ */
+#define IDTYPE_FILE 1
+#define IDTYPE_DIR 2
+#define IDTYPE_PKCS11 3
+#define IDTYPE_ENVVAR 4
+#define IDTYPE_PKCS12 5
+
+/*
+ * ca/crl types
+ */
+#define CATYPE_ANCHORS 1
+#define CATYPE_INTERMEDIATES 2
+#define CATYPE_CRLS 3
+
+/*
+ * The following represent Key Usage values that we
+ * may care about in a certificate
+ */
+#define PKINIT_KU_DIGITALSIGNATURE 0x80000000
+#define PKINIT_KU_KEYENCIPHERMENT 0x40000000
+
+/*
+ * The following represent Extended Key Usage oid values
+ * that we may care about in a certificate
+ */
+#define PKINIT_EKU_PKINIT 0x80000000
+#define PKINIT_EKU_MSSCLOGIN 0x40000000
+#define PKINIT_EKU_CLIENTAUTH 0x20000000
+#define PKINIT_EKU_EMAILPROTECTION 0x10000000
+
+
+/* Handle to cert, opaque above crypto interface */
+typedef struct _pkinit_cert_info *pkinit_cert_handle;
+
+/* Handle to cert iteration information, opaque above crypto interface */
+typedef struct _pkinit_cert_iter_info *pkinit_cert_iter_handle;
+
+#define PKINIT_ITER_NO_MORE 0x11111111 /* XXX */
+
+typedef struct _pkinit_cert_matching_data {
+ pkinit_cert_handle ch; /* cert handle for this certificate */
+ char *subject_dn; /* rfc2253-style subject name string */
+ char *issuer_dn; /* rfc2253-style issuer name string */
+ unsigned int ku_bits; /* key usage information */
+ unsigned int eku_bits; /* extended key usage information */
+ krb5_principal *sans; /* Null-terminated array of subject alternative
+ name info (pkinit and ms-upn) */
+} pkinit_cert_matching_data;
+
+/*
+ * 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
+ * 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
+ */
+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 the values from idopts and obtain the cert(s)
+ * specified by those options, populating the id_cryptoctx.
+ */
+krb5_error_code crypto_load_certs
+ (krb5_context context, /* IN */
+ pkinit_plg_crypto_context plg_cryptoctx, /* IN */
+ pkinit_req_crypto_context req_cryptoctx, /* IN */
+ pkinit_identity_opts *idopts, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */
+ krb5_principal princ); /* IN */
+
+/*
+ * Free up information held from crypto_load_certs()
+ */
+krb5_error_code crypto_free_cert_info
+ (krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx);
+
+
+/*
+ * Get number of certificates available after crypto_load_certs()
+ */
+krb5_error_code crypto_cert_get_count
+ (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 *cert_count); /* OUT */
+
+/*
+ * Begin iteration over the certs loaded in crypto_load_certs()
+ */
+krb5_error_code crypto_cert_iteration_begin
+ (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_cert_iter_handle *iter_handle); /* OUT */
+
+/*
+ * End iteration over the certs loaded in crypto_load_certs()
+ */
+krb5_error_code crypto_cert_iteration_end
+ (krb5_context context, /* IN */
+ pkinit_cert_iter_handle iter_handle); /* IN */
+
+/*
+ * Get next certificate handle
+ */
+krb5_error_code crypto_cert_iteration_next
+ (krb5_context context, /* IN */
+ pkinit_cert_iter_handle iter_handle, /* IN */
+ pkinit_cert_handle *cert_handle); /* OUT */
+
+/*
+ * Release cert handle
+ */
+krb5_error_code crypto_cert_release
+ (krb5_context context, /* IN */
+ pkinit_cert_handle cert_handle); /* IN */
+
+/*
+ * Get certificate matching information
+ */
+krb5_error_code crypto_cert_get_matching_data
+ (krb5_context context, /* IN */
+ pkinit_cert_handle cert_handle, /* IN */
+ pkinit_cert_matching_data **ret_data); /* OUT */
+
+/*
+ * Free certificate information
+ */
+krb5_error_code crypto_cert_free_matching_data
+ (krb5_context context, /* IN */
+ pkinit_cert_matching_data *data); /* IN */
+
+/*
+ * Make the given certificate "the chosen one"
+ */
+krb5_error_code crypto_cert_select
+ (krb5_context context, /* IN */
+ pkinit_cert_matching_data *data); /* IN */
+
+/*
+ * Select the default certificate as "the chosen one"
+ */
+krb5_error_code crypto_cert_select_default
+ (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 */
+
+/*
+ * process the values from idopts and obtain the anchor or
+ * intermediate certificates, or crls specified by idtype,
+ * catype, and id
+ */
+krb5_error_code crypto_load_cas_and_crls
+ (krb5_context context, /* IN */
+ pkinit_plg_crypto_context plg_cryptoctx, /* IN */
+ pkinit_req_crypto_context req_cryptoctx, /* IN */
+ pkinit_identity_opts *idopts, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */
+ int idtype, /* IN
+ defines the storage type (file, directory, etc) */
+ int catype, /* IN
+ defines the ca type (anchor, intermediate, crls) */
+ char *id); /* IN
+ defines the location (filename, directory name, etc) */
+
+/*
+ * on the client, obtain the kdc's certificate to include
+ * in a request
+ */
+krb5_error_code pkinit_get_kdc_cert
+ (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/OUT */
+ krb5_principal princ); /* IN */
+
+/*
+ * 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
new file mode 100644
index 0000000..1859b4f
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -0,0 +1,5612 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/*
+ * Q: What is this SILLYDECRYPT stuff about?
+ * A: When using the ActivCard Linux pkcs11 library (v2.0.1),
+ * the decrypt function fails. By inserting an extra
+ * function call, which serves nothing but to change the
+ * stack, we were able to work around the issue. If the
+ * ActivCard library is fixed in the future, this
+ * definition and related code can be removed.
+ */
+#define SILLYDECRYPT
+
+#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,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+unsigned char pkinit_2048_dhprime[2048/8] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+unsigned char pkinit_4096_dhprime[4096/8] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+ 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+ 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+ 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+ 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+ 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+ 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+ 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+ 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+ 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+ 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+ 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+ 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+ 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+ 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+ 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+ 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+ 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+ 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+ 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+ 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+ 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+ 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+ 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+ 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+ 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+ 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+ 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static int pkinit_oids_refs = 0;
+
+krb5_error_code
+pkinit_init_plg_crypto(pkinit_plg_crypto_context *cryptoctx) {
+
+ krb5_error_code retval = ENOMEM;
+ pkinit_plg_crypto_context ctx = NULL;
+
+ /* initialize openssl routines */
+ openssl_init();
+
+ ctx = (pkinit_plg_crypto_context)malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto out;
+ memset(ctx, 0, sizeof(*ctx));
+
+ pkiDebug("%s: initializing openssl crypto context at %p\n",
+ __FUNCTION__, ctx);
+ retval = pkinit_init_pkinit_oids(ctx);
+ if (retval)
+ goto out;
+
+ retval = pkinit_init_dh_params(ctx);
+ if (retval)
+ goto out;
+
+ *cryptoctx = ctx;
+
+out:
+ if (retval && ctx != NULL)
+ pkinit_fini_plg_crypto(ctx);
+
+ return retval;
+}
+
+void
+pkinit_fini_plg_crypto(pkinit_plg_crypto_context cryptoctx)
+{
+ pkiDebug("%s: freeing context at %p\n", __FUNCTION__, cryptoctx);
+
+ if (cryptoctx == NULL)
+ return;
+ pkinit_fini_pkinit_oids(cryptoctx);
+ pkinit_fini_dh_params(cryptoctx);
+ free(cryptoctx);
+}
+
+krb5_error_code
+pkinit_init_identity_crypto(pkinit_identity_crypto_context *idctx)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_identity_crypto_context ctx = NULL;
+
+ ctx = (pkinit_identity_crypto_context)malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto out;
+ memset(ctx, 0, sizeof(*ctx));
+
+ retval = pkinit_init_certs(ctx);
+ if (retval)
+ goto out;
+
+ retval = pkinit_init_pkcs11(ctx);
+ if (retval)
+ goto out;
+
+ pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);
+ *idctx = ctx;
+
+out:
+ if (retval) {
+ if (ctx)
+ pkinit_fini_identity_crypto(ctx);
+ }
+
+ return retval;
+}
+
+void
+pkinit_fini_identity_crypto(pkinit_identity_crypto_context idctx)
+{
+ if (idctx == NULL)
+ return;
+
+ pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, idctx);
+ pkinit_fini_certs(idctx);
+ pkinit_fini_pkcs11(idctx);
+ free(idctx);
+}
+
+krb5_error_code
+pkinit_init_req_crypto(pkinit_req_crypto_context *cryptoctx)
+{
+
+ krb5_error_code retval = ENOMEM;
+ pkinit_req_crypto_context ctx = NULL;
+
+ ctx = (pkinit_req_crypto_context)malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto out;
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->dh = NULL;
+ ctx->received_cert = NULL;
+
+ *cryptoctx = ctx;
+
+ pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);
+ retval = 0;
+out:
+ if (retval)
+ free(ctx);
+
+ return retval;
+}
+
+void
+pkinit_fini_req_crypto(pkinit_req_crypto_context req_cryptoctx)
+{
+ if (req_cryptoctx == NULL)
+ return;
+
+ pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, req_cryptoctx);
+ if (req_cryptoctx->dh != NULL)
+ DH_free(req_cryptoctx->dh);
+ if (req_cryptoctx->received_cert != NULL)
+ X509_free(req_cryptoctx->received_cert);
+
+ free(req_cryptoctx);
+}
+
+static krb5_error_code
+pkinit_init_pkinit_oids(pkinit_plg_crypto_context ctx)
+{
+ krb5_error_code retval = ENOMEM;
+ int nid = 0;
+
+ /*
+ * 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
+
+ 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");
+
+ 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");
+
+ 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;
+ pkinit_oids_refs++;
+
+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)
+{
+ if (ctx == NULL)
+ return;
+
+ /* Only call OBJ_cleanup once! */
+ if (--pkinit_oids_refs == 0)
+ OBJ_cleanup();
+}
+
+static krb5_error_code
+pkinit_init_dh_params(pkinit_plg_crypto_context plgctx)
+{
+ krb5_error_code retval = ENOMEM;
+
+ plgctx->dh_1024 = DH_new();
+ if (plgctx->dh_1024 == NULL)
+ goto cleanup;
+ plgctx->dh_1024->p = BN_bin2bn(pkinit_1024_dhprime,
+ sizeof(pkinit_1024_dhprime), NULL);
+ if ((plgctx->dh_1024->g = BN_new()) == NULL ||
+ (plgctx->dh_1024->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_1024->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_1024->q, plgctx->dh_1024->p);
+
+ plgctx->dh_2048 = DH_new();
+ if (plgctx->dh_2048 == NULL)
+ goto cleanup;
+ plgctx->dh_2048->p = BN_bin2bn(pkinit_2048_dhprime,
+ sizeof(pkinit_2048_dhprime), NULL);
+ if ((plgctx->dh_2048->g = BN_new()) == NULL ||
+ (plgctx->dh_2048->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_2048->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_2048->q, plgctx->dh_2048->p);
+
+ plgctx->dh_4096 = DH_new();
+ if (plgctx->dh_4096 == NULL)
+ goto cleanup;
+ plgctx->dh_4096->p = BN_bin2bn(pkinit_4096_dhprime,
+ sizeof(pkinit_4096_dhprime), NULL);
+ if ((plgctx->dh_4096->g = BN_new()) == NULL ||
+ (plgctx->dh_4096->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_4096->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_4096->q, plgctx->dh_4096->p);
+
+ retval = 0;
+
+cleanup:
+ if (retval)
+ pkinit_fini_dh_params(plgctx);
+
+ return retval;
+}
+
+static void
+pkinit_fini_dh_params(pkinit_plg_crypto_context plgctx)
+{
+ if (plgctx->dh_1024 != NULL)
+ DH_free(plgctx->dh_1024);
+ if (plgctx->dh_2048 != NULL)
+ DH_free(plgctx->dh_2048);
+ if (plgctx->dh_4096 != NULL)
+ DH_free(plgctx->dh_4096);
+
+ plgctx->dh_1024 = plgctx->dh_2048 = plgctx->dh_4096 = NULL;
+}
+
+static krb5_error_code
+pkinit_init_certs(pkinit_identity_crypto_context ctx)
+{
+ krb5_error_code retval = ENOMEM;
+ int i;
+
+ for (i = 0; i < MAX_CREDS_ALLOWED; i++)
+ ctx->creds[i] = NULL;
+ ctx->my_certs = NULL;
+ ctx->cert_index = 0;
+ ctx->my_key = NULL;
+ ctx->trustedCAs = NULL;
+ ctx->intermediateCAs = NULL;
+ ctx->revoked = NULL;
+
+ retval = 0;
+ return retval;
+}
+
+static void
+pkinit_fini_certs(pkinit_identity_crypto_context ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ if (ctx->my_certs != NULL)
+ sk_X509_pop_free(ctx->my_certs, X509_free);
+
+ if (ctx->my_key != NULL)
+ EVP_PKEY_free(ctx->my_key);
+
+ if (ctx->trustedCAs != NULL)
+ sk_X509_pop_free(ctx->trustedCAs, X509_free);
+
+ if (ctx->intermediateCAs != NULL)
+ sk_X509_pop_free(ctx->intermediateCAs, X509_free);
+
+ if (ctx->revoked != NULL)
+ sk_X509_CRL_pop_free(ctx->revoked, X509_CRL_free);
+}
+
+static krb5_error_code
+pkinit_init_pkcs11(pkinit_identity_crypto_context ctx)
+{
+ krb5_error_code retval = ENOMEM;
+
+#ifndef WITHOUT_PKCS11
+ ctx->p11_module_name = strdup(PKCS11_MODNAME);
+ if (ctx->p11_module_name == NULL)
+ return retval;
+ ctx->p11_module = NULL;
+ ctx->slotid = PK_NOSLOT;
+ ctx->token_label = NULL;
+ ctx->cert_label = NULL;
+ ctx->session = CK_INVALID_HANDLE;
+ ctx->p11 = NULL;
+#endif
+ ctx->pkcs11_method = 0;
+
+ retval = 0;
+ return retval;
+}
+
+static void
+pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx)
+{
+#ifndef WITHOUT_PKCS11
+ if (ctx == NULL)
+ return;
+
+ if (ctx->p11 != NULL) {
+ if (ctx->session) {
+ ctx->p11->C_CloseSession(ctx->session);
+ ctx->session = CK_INVALID_HANDLE;
+ }
+ ctx->p11->C_Finalize(NULL_PTR);
+ ctx->p11 = NULL;
+ }
+ if (ctx->p11_module != NULL) {
+ pkinit_C_UnloadModule(ctx->p11_module);
+ ctx->p11_module = NULL;
+ }
+ if (ctx->p11_module_name != NULL)
+ free(ctx->p11_module_name);
+ if (ctx->token_label != NULL)
+ free(ctx->token_label);
+ if (ctx->cert_id != NULL)
+ free(ctx->cert_id);
+ if (ctx->cert_label != NULL)
+ free(ctx->cert_label);
+#endif
+}
+
+krb5_error_code
+pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx,
+ krb5_prompter_fct prompter,
+ void *prompter_data)
+{
+ id_cryptoctx->prompter = prompter;
+ id_cryptoctx->prompter_data = prompter_data;
+
+ return 0;
+}
+
+krb5_error_code
+cms_signeddata_create(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int cms_msg_type,
+ int include_certchain,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **signed_data,
+ unsigned int *signed_data_len)
+{
+ krb5_error_code retval = ENOMEM;
+ PKCS7 *p7 = NULL, *inner_p7 = NULL;
+ PKCS7_SIGNED *p7s = NULL;
+ PKCS7_SIGNER_INFO *p7si = NULL;
+ unsigned char *p;
+ ASN1_TYPE *pkinit_data = NULL;
+ STACK_OF(X509) * cert_stack = NULL;
+ ASN1_OCTET_STRING *digest_attr = NULL;
+ EVP_MD_CTX ctx, ctx2;
+ 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;
+ STACK_OF(X509_ATTRIBUTE) * sk;
+ unsigned char *sig = NULL;
+ unsigned int sig_len = 0;
+ X509_ALGOR *alg = NULL;
+ ASN1_OCTET_STRING *digest = NULL;
+ unsigned int alg_len = 0, digest_len = 0;
+ unsigned char *y = NULL, *alg_buf = NULL, *digest_buf = NULL;
+ X509 *cert = NULL;
+ ASN1_OBJECT *oid = NULL;
+
+ /* start creating PKCS7 data */
+ if ((p7 = PKCS7_new()) == NULL)
+ goto cleanup;
+ p7->type = OBJ_nid2obj(NID_pkcs7_signed);
+
+ if ((p7s = PKCS7_SIGNED_new()) == NULL)
+ goto cleanup;
+ p7->d.sign = p7s;
+ if (!ASN1_INTEGER_set(p7s->version, 3))
+ goto cleanup;
+
+ /* create a cert chain that has at least the signer's certificate */
+ if ((cert_stack = sk_X509_new_null()) == NULL)
+ goto cleanup;
+
+ cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index);
+ if (!include_certchain) {
+ pkiDebug("only including signer's certificate\n");
+ sk_X509_push(cert_stack, X509_dup(cert));
+ } else {
+ /* create a cert chain */
+ X509_STORE *certstore = NULL;
+ X509_STORE_CTX certctx;
+ STACK_OF(X509) *certstack = NULL;
+ char buf[DN_BUF_LEN];
+ int i = 0, size = 0;
+
+ if ((certstore = X509_STORE_new()) == NULL)
+ goto cleanup;
+ pkiDebug("building certificate chain\n");
+ X509_STORE_set_verify_cb_func(certstore, openssl_callback);
+ X509_STORE_CTX_init(&certctx, certstore, cert,
+ id_cryptoctx->intermediateCAs);
+ X509_STORE_CTX_trusted_stack(&certctx, id_cryptoctx->trustedCAs);
+ if (!X509_verify_cert(&certctx)) {
+ pkiDebug("failed to create a certificate chain: %s\n",
+ X509_verify_cert_error_string(X509_STORE_CTX_get_error(&certctx)));
+ if (!sk_X509_num(id_cryptoctx->trustedCAs))
+ pkiDebug("No trusted CAs found. Check your X509_anchors\n");
+ goto cleanup;
+ }
+ certstack = X509_STORE_CTX_get1_chain(&certctx);
+ size = sk_X509_num(certstack);
+ pkiDebug("size of certificate chain = %d\n", size);
+ for(i = 0; i < size - 1; i++) {
+ X509 *x = sk_X509_value(certstack, i);
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", i, buf);
+ sk_X509_push(cert_stack, X509_dup(x));
+ }
+ X509_STORE_CTX_cleanup(&certctx);
+ X509_STORE_free(certstore);
+ sk_X509_pop_free(certstack, X509_free);
+ }
+ p7s->cert = cert_stack;
+
+ /* fill-in PKCS7_SIGNER_INFO */
+ if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL)
+ goto cleanup;
+ if (!ASN1_INTEGER_set(p7si->version, 1))
+ goto cleanup;
+ if (!X509_NAME_set(&p7si->issuer_and_serial->issuer,
+ X509_get_issuer_name(cert)))
+ goto cleanup;
+ /* because ASN1_INTEGER_set is used to set a 'long' we will do
+ * things the ugly way. */
+ M_ASN1_INTEGER_free(p7si->issuer_and_serial->serial);
+ if (!(p7si->issuer_and_serial->serial =
+ M_ASN1_INTEGER_dup(X509_get_serialNumber(cert))))
+ goto cleanup;
+
+ /* will not fill-out EVP_PKEY because it's on the smartcard */
+
+ /* Set digest algs */
+ p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1);
+
+ if (p7si->digest_alg->parameter != NULL)
+ ASN1_TYPE_free(p7si->digest_alg->parameter);
+ if ((p7si->digest_alg->parameter = ASN1_TYPE_new()) == NULL)
+ goto cleanup;
+ p7si->digest_alg->parameter->type = V_ASN1_NULL;
+
+ /* Set sig algs */
+ if (p7si->digest_enc_alg->parameter != NULL)
+ ASN1_TYPE_free(p7si->digest_enc_alg->parameter);
+ p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption);
+ if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new()))
+ goto cleanup;
+ p7si->digest_enc_alg->parameter->type = V_ASN1_NULL;
+
+ /* pick the correct oid for the eContentInfo */
+ oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type);
+ if (oid == NULL)
+ goto cleanup;
+
+ 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 */
+ /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash
+ * function and the hash value into an ASN.1 value of type DigestInfo
+ * DigestInfo::=SEQUENCE {
+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING }
+ */
+ if (id_cryptoctx->pkcs11_method == 1 &&
+ id_cryptoctx->mech == CKM_RSA_PKCS) {
+ pkiDebug("mech = CKM_RSA_PKCS\n");
+ EVP_MD_CTX_init(&ctx2);
+ /* 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);
+
+ alg = X509_ALGOR_new();
+ if (alg == NULL)
+ goto cleanup2;
+ alg->algorithm = OBJ_nid2obj(NID_sha1);
+ alg->parameter = NULL;
+ alg_len = i2d_X509_ALGOR(alg, NULL);
+ alg_buf = (unsigned char *)malloc(alg_len);
+ if (alg_buf == NULL)
+ goto cleanup2;
+
+ digest = ASN1_OCTET_STRING_new();
+ if (digest == NULL)
+ goto cleanup2;
+ ASN1_OCTET_STRING_set(digest, md_data2, (int)md_len2);
+ digest_len = i2d_ASN1_OCTET_STRING(digest, NULL);
+ digest_buf = (unsigned char *)malloc(digest_len);
+ if (digest_buf == NULL)
+ goto cleanup2;
+
+ digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len),
+ V_ASN1_SEQUENCE);
+ y = digestInfo_buf = (unsigned char *)malloc(digestInfo_len);
+ if (digestInfo_buf == NULL)
+ goto cleanup2;
+ ASN1_put_object(&y, 1, (int)(alg_len + digest_len), V_ASN1_SEQUENCE,
+ V_ASN1_UNIVERSAL);
+ i2d_X509_ALGOR(alg, &y);
+ i2d_ASN1_OCTET_STRING(digest, &y);
+#ifdef DEBUG_SIG
+ pkiDebug("signing buffer\n");
+ print_buffer(digestInfo_buf, digestInfo_len);
+ print_buffer_bin(digestInfo_buf, digestInfo_len, "/tmp/pkcs7_tosign");
+#endif
+ retval = pkinit_sign_data(context, id_cryptoctx, digestInfo_buf,
+ digestInfo_len, &sig, &sig_len);
+ } else
+#endif
+ {
+ pkiDebug("mech = %s\n",
+ id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS");
+ retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen,
+ &sig, &sig_len);
+ }
+#ifdef DEBUG_SIG
+ print_buffer(sig, sig_len);
+#endif
+ if (cms_msg_type != CMS_SIGN_DRAFT9)
+ free(abuf);
+ if (retval)
+ goto cleanup2;
+
+ /* Add signature */
+ if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig,
+ (int)sig_len)) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to add a signed digest attribute\n");
+ goto cleanup2;
+ }
+ /* adder signer_info to pkcs7 signed */
+ if (!PKCS7_add_signer(p7, p7si))
+ goto cleanup2;
+
+ /* start on adding data to the pkcs7 signed */
+ if ((inner_p7 = PKCS7_new()) == NULL)
+ goto cleanup2;
+ if ((pkinit_data = ASN1_TYPE_new()) == NULL)
+ goto cleanup2;
+ pkinit_data->type = V_ASN1_OCTET_STRING;
+ if ((pkinit_data->value.octet_string = ASN1_OCTET_STRING_new()) == NULL)
+ goto cleanup2;
+ if (!ASN1_OCTET_STRING_set(pkinit_data->value.octet_string, data,
+ (int)data_len)) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to add pkcs7 data\n");
+ goto cleanup2;
+ }
+
+ 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;
+
+ *signed_data_len = i2d_PKCS7(p7, NULL);
+ if (!(*signed_data_len)) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to der encode pkcs7\n");
+ goto cleanup2;
+ }
+ if ((p = *signed_data =
+ (unsigned char *) malloc((size_t)*signed_data_len)) == NULL)
+ goto cleanup2;
+
+ /* DER encode PKCS7 data */
+ retval = i2d_PKCS7(p7, &p);
+ if (!retval) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to der encode pkcs7\n");
+ goto cleanup2;
+ }
+ retval = 0;
+
+#ifdef DEBUG_ASN1
+ 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:
+ 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) {
+ 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);
+ cleanup:
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ if (sig != NULL)
+ free(sig);
+
+ return retval;
+}
+
+krb5_error_code
+cms_signeddata_verify(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_identity_crypto_context idctx,
+ int cms_msg_type,
+ int require_crl_checking,
+ unsigned char *signed_data,
+ unsigned int signed_data_len,
+ unsigned char **data,
+ unsigned int *data_len,
+ unsigned char **authz_data,
+ unsigned int *authz_data_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ PKCS7 *p7 = NULL;
+ BIO *out = NULL;
+ int flags = PKCS7_NOVERIFY, i = 0;
+ unsigned int vflags = 0, size = 0;
+ const unsigned char *p = signed_data;
+ STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL;
+ PKCS7_SIGNER_INFO *si = NULL;
+ X509 *x = NULL;
+ X509_STORE *store = NULL;
+ X509_STORE_CTX cert_ctx;
+ 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[DN_BUF_LEN];
+
+#ifdef DEBUG_ASN1
+ 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 */
+ oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type);
+ if (oid == NULL)
+ goto cleanup;
+
+ /* 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("Expected id-signedData PKCS7 msg (received type = %d)\n",
+ OBJ_obj2nid(p7->type));
+ krb5_set_error_message(context, retval, "wrong oid\n");
+ goto cleanup;
+ }
+
+ /* setup to verify X509 certificate used to sign PKCS7 message */
+ if (!(store = X509_STORE_new()))
+ goto cleanup;
+
+ /* check if we are inforcing CRL checking */
+ vflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;
+ if (require_crl_checking)
+ X509_STORE_set_verify_cb_func(store, openssl_callback);
+ else
+ X509_STORE_set_verify_cb_func(store, openssl_callback_ignore_crls);
+ X509_STORE_set_flags(store, vflags);
+
+ /* get the signer's information from the PKCS7 message */
+ if ((si_sk = PKCS7_get_signer_info(p7)) == NULL)
+ goto cleanup;
+ if ((si = sk_PKCS7_SIGNER_INFO_value(si_sk, 0)) == NULL)
+ goto cleanup;
+ if ((x = PKCS7_cert_from_signer_info(p7, si)) == NULL)
+ goto cleanup;
+
+ /* create available CRL information (get local CRLs and include CRLs
+ * received in the PKCS7 message
+ */
+ if (idctx->revoked == NULL)
+ revoked = p7->d.sign->crl;
+ else if (p7->d.sign->crl == NULL)
+ revoked = idctx->revoked;
+ else {
+ size = sk_X509_CRL_num(idctx->revoked);
+ revoked = sk_X509_CRL_new_null();
+ for (i = 0; i < size; i++)
+ sk_X509_CRL_push(revoked, sk_X509_CRL_value(idctx->revoked, i));
+ size = sk_X509_num(p7->d.sign->crl);
+ for (i = 0; i < size; i++)
+ sk_X509_CRL_push(revoked, sk_X509_CRL_value(p7->d.sign->crl, i));
+ }
+
+ /* create available intermediate CAs chains (get local intermediateCAs and
+ * include the CA chain received in the PKCS7 message
+ */
+ if (idctx->intermediateCAs == NULL)
+ intermediateCAs = p7->d.sign->cert;
+ else if (p7->d.sign->cert == NULL)
+ intermediateCAs = idctx->intermediateCAs;
+ else {
+ size = sk_X509_num(idctx->intermediateCAs);
+ intermediateCAs = sk_X509_new_null();
+ for (i = 0; i < size; i++) {
+ sk_X509_push(intermediateCAs,
+ sk_X509_value(idctx->intermediateCAs, i));
+ }
+ size = sk_X509_num(p7->d.sign->cert);
+ for (i = 0; i < size; i++) {
+ sk_X509_push(intermediateCAs, sk_X509_value(p7->d.sign->cert, i));
+ }
+ }
+
+ /* initialize x509 context with the received certificate and
+ * trusted and intermediate CA chains and CRLs
+ */
+ if (!X509_STORE_CTX_init(&cert_ctx, store, x, intermediateCAs))
+ goto cleanup;
+
+ X509_STORE_CTX_set0_crls(&cert_ctx, revoked);
+
+ /* add trusted CAs certificates for cert verification */
+ if (idctx->trustedCAs != NULL)
+ X509_STORE_CTX_trusted_stack(&cert_ctx, idctx->trustedCAs);
+ else {
+ pkiDebug("unable to find any trusted CAs\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_CERTCHAIN
+ if (intermediateCAs != NULL) {
+ size = sk_X509_num(intermediateCAs);
+ 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, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", i, buf);
+ }
+ }
+ if (idctx->trustedCAs != NULL) {
+ size = sk_X509_num(idctx->trustedCAs);
+ 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, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", i, buf);
+ }
+ }
+ if (revoked != NULL) {
+ size = sk_X509_CRL_num(revoked);
+ 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, sizeof(buf));
+ pkiDebug("crls by CA #%d: %s\n", i , buf);
+ }
+ }
+#endif
+
+ i = X509_verify_cert(&cert_ctx);
+ if (i <= 0) {
+ int j = X509_STORE_CTX_get_error(&cert_ctx);
+
+ reqctx->received_cert = X509_dup(cert_ctx.current_cert);
+ switch(j) {
+ case X509_V_ERR_CERT_REVOKED:
+ retval = KRB5KDC_ERR_REVOKED_CERTIFICATE;
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ retval = KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN;
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ retval = KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE;
+ break;
+ default:
+ retval = KRB5KDC_ERR_INVALID_CERTIFICATE;
+ }
+ X509_NAME_oneline(X509_get_subject_name(
+ 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",
+ X509_verify_cert_error_string(j));
+#ifdef DEBUG_CERTCHAIN
+ size = sk_X509_num(p7->d.sign->cert);
+ 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, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", j, buf);
+ }
+#endif
+ } else {
+ /* retrieve verified certificate chain */
+ if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9)
+ verified_chain = X509_STORE_CTX_get1_chain(&cert_ctx);
+ }
+ X509_STORE_CTX_cleanup(&cert_ctx);
+ if (i <= 0)
+ 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)) {
+ int valid_oid = 0;
+
+ if (!OBJ_cmp(p7->d.sign->contents->type, oid))
+ valid_oid = 1;
+ else if (cms_msg_type == CMS_SIGN_DRAFT9) {
+ /*
+ * Various implementations of the pa-type 15 request use
+ * different OIDS. We check that the returned object
+ * has any of the acceptable OIDs
+ */
+ ASN1_OBJECT *client_oid = NULL, *server_oid = NULL, *rsa_oid = NULL;
+ client_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_CLIENT);
+ server_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_SERVER);
+ rsa_oid = pkinit_pkcs7type2oid(plgctx, CMS_ENVEL_SERVER);
+ if (!OBJ_cmp(p7->d.sign->contents->type, client_oid) ||
+ !OBJ_cmp(p7->d.sign->contents->type, server_oid) ||
+ !OBJ_cmp(p7->d.sign->contents->type, rsa_oid))
+ valid_oid = 1;
+ }
+
+ if (valid_oid)
+ pkiDebug("PKCS7 Verification successful\n");
+ else {
+ pkiDebug("wrong oid in eContentType\n");
+ print_buffer(p7->d.sign->contents->type->data,
+ (unsigned int)p7->d.sign->contents->type->length);
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "wrong oid\n");
+ goto cleanup;
+ }
+ }
+ else {
+ unsigned long err = ERR_peek_error();
+ switch(ERR_GET_REASON(err)) {
+ case PKCS7_R_DIGEST_FAILURE:
+ retval = KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED;
+ break;
+ case PKCS7_R_SIGNATURE_FAILURE:
+ default:
+ retval = KRB5KDC_ERR_INVALID_SIG;
+ }
+ pkiDebug("PKCS7 Verification failure\n");
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ goto cleanup;
+ }
+
+ /* transfer the data from PKCS7 message into return buffer */
+ for (size = 0;;) {
+ if ((*data = realloc(*data, size + 1024 * 10)) == NULL)
+ goto cleanup;
+ i = BIO_read(out, &((*data)[size]), 1024 * 10);
+ if (i <= 0)
+ break;
+ else
+ size += i;
+ }
+ *data_len = size;
+
+ reqctx->received_cert = X509_dup(x);
+
+ /* generate authorization data */
+ if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9) {
+
+ if (authz_data == NULL || authz_data_len == NULL)
+ goto out;
+
+ *authz_data = NULL;
+ retval = create_identifiers_from_stack(verified_chain,
+ &krb5_verified_chain);
+ if (retval) {
+ pkiDebug("create_identifiers_from_stack failed\n");
+ goto cleanup;
+ }
+
+ retval = k5int_encode_krb5_td_trusted_certifiers((const krb5_external_principal_identifier **)krb5_verified_chain, &authz);
+ if (retval) {
+ pkiDebug("encode_krb5_td_trusted_certifiers failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)authz->data, authz->length,
+ "/tmp/kdc_ad_initial_verified_cas");
+#endif
+ *authz_data = (unsigned char *)malloc(authz->length);
+ if (*authz_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(*authz_data, authz->data, authz->length);
+ *authz_data_len = authz->length;
+ }
+ out:
+ retval = 0;
+
+ cleanup:
+ if (out != NULL)
+ BIO_free(out);
+ if (store != NULL)
+ X509_STORE_free(store);
+ 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)
+ free_krb5_external_principal_identifier(&krb5_verified_chain);
+ if (authz != NULL)
+ krb5_free_data(context, authz);
+
+ return retval;
+}
+
+krb5_error_code
+cms_envelopeddata_create(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_identity_crypto_context idctx,
+ krb5_preauthtype pa_type,
+ int include_certchain,
+ unsigned char *key_pack,
+ unsigned int key_pack_len,
+ unsigned char **out,
+ unsigned int *out_len)
+{
+
+ krb5_error_code retval = ENOMEM;
+ PKCS7 *p7 = NULL;
+ BIO *in = NULL;
+ unsigned char *p = NULL, *signed_data = NULL, *enc_data = NULL;
+ int signed_data_len = 0, enc_data_len = 0, flags = PKCS7_BINARY;
+ STACK_OF(X509) *encerts = NULL;
+ const EVP_CIPHER *cipher = NULL;
+ int cms_msg_type;
+
+ /* create the PKCS7 SignedData portion of the PKCS7 EnvelopedData */
+ switch ((int)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ cms_msg_type = CMS_SIGN_DRAFT9;
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ cms_msg_type = CMS_ENVEL_SERVER;
+ break;
+ default:
+ goto cleanup;
+ }
+
+ retval = cms_signeddata_create(context, plgctx, reqctx, idctx,
+ cms_msg_type, include_certchain, key_pack, key_pack_len,
+ &signed_data, (unsigned int *)&signed_data_len);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ /* check we have client's certificate */
+ if (reqctx->received_cert == NULL) {
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+ encerts = sk_X509_new_null();
+ sk_X509_push(encerts, reqctx->received_cert);
+
+ cipher = EVP_des_ede3_cbc();
+ in = BIO_new(BIO_s_mem());
+ switch (pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ prepare_enc_data(signed_data, signed_data_len, &enc_data,
+ &enc_data_len);
+ retval = BIO_write(in, enc_data, enc_data_len);
+ if (retval != enc_data_len) {
+ pkiDebug("BIO_write only wrote %d\n", retval);
+ goto cleanup;
+ }
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = BIO_write(in, signed_data, signed_data_len);
+ if (retval != signed_data_len) {
+ pkiDebug("BIO_write only wrote %d\n", retval);
+ goto cleanup;
+ }
+ break;
+ default:
+ retval = -1;
+ goto cleanup;
+ }
+
+ p7 = PKCS7_encrypt(encerts, in, cipher, flags);
+ if (p7 == NULL) {
+ pkiDebug("failed to encrypt PKCS7 object\n");
+ retval = -1;
+ goto cleanup;
+ }
+ switch (pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ p7->d.enveloped->enc_data->content_type =
+ OBJ_nid2obj(NID_pkcs7_signed);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ p7->d.enveloped->enc_data->content_type =
+ OBJ_nid2obj(NID_pkcs7_data);
+ break;
+ break;
+ break;
+break;
+ }
+
+ *out_len = i2d_PKCS7(p7, NULL);
+ if (!*out_len || (p = *out = (unsigned char *)malloc(*out_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = i2d_PKCS7(p7, &p);
+ if (!retval) {
+ pkiDebug("unable to write pkcs7 object\n");
+ goto cleanup;
+ }
+ retval = 0;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(*out, *out_len, "/tmp/kdc_enveloped_data");
+#endif
+
+cleanup:
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ if (in != NULL)
+ BIO_free(in);
+ if (signed_data != NULL)
+ free(signed_data);
+ if (enc_data != NULL)
+ free(enc_data);
+ if (encerts != NULL)
+ sk_X509_free(encerts);
+
+ return retval;
+}
+
+krb5_error_code
+cms_envelopeddata_verify(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_preauthtype pa_type,
+ int require_crl_checking,
+ unsigned char *enveloped_data,
+ unsigned int enveloped_data_len,
+ unsigned char **data,
+ unsigned int *data_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ PKCS7 *p7 = NULL;
+ BIO *out = NULL;
+ int i = 0;
+ unsigned int size = 0;
+ const unsigned char *p = enveloped_data;
+ unsigned int tmp_buf_len = 0, tmp_buf2_len = 0, vfy_buf_len = 0;
+ unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL, *vfy_buf = NULL;
+ int msg_type = 0;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(enveloped_data, enveloped_data_len,
+ "/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();
+ pkiDebug("failed to decode pkcs7\n");
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ goto cleanup;
+ }
+
+ /* verify that the received message is PKCS7 EnvelopedData message */
+ if (OBJ_obj2nid(p7->type) != NID_pkcs7_enveloped) {
+ 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;
+ }
+
+ /* decrypt received PKCS7 message */
+ out = BIO_new(BIO_s_mem());
+ if (pkcs7_decrypt(context, id_cryptoctx, p7, out)) {
+ pkiDebug("PKCS7 decryption successful\n");
+ } else {
+ unsigned long err = ERR_peek_error();
+ if (err != 0)
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("PKCS7 decryption failed\n");
+ goto cleanup;
+ }
+
+ /* transfer the decoded PKCS7 SignedData message into a separate buffer */
+ for (;;) {
+ if ((tmp_buf = realloc(tmp_buf, size + 1024 * 10)) == NULL)
+ goto cleanup;
+ i = BIO_read(out, &(tmp_buf[size]), 1024 * 10);
+ if (i <= 0)
+ break;
+ else
+ size += i;
+ }
+ tmp_buf_len = size;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(tmp_buf, tmp_buf_len, "/tmp/client_enc_keypack");
+#endif
+ /* verify PKCS7 SignedData message */
+ switch (pa_type) {
+ case KRB5_PADATA_PK_AS_REP:
+ msg_type = CMS_ENVEL_SERVER;
+
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ msg_type = CMS_SIGN_DRAFT9;
+ break;
+ default:
+ pkiDebug("%s: unrecognized pa_type = %d\n", __FUNCTION__, pa_type);
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+ /*
+ * If this is the RFC style, wrap the signed data to make
+ * decoding easier in the verify routine.
+ * For draft9-compatible, we don't do anything because it
+ * is already wrapped.
+ */
+#ifdef LONGHORN_BETA_COMPAT
+ /*
+ * The Longhorn server returns the expected RFC-style data, but
+ * it is missing the sequence tag and length, so it requires
+ * special processing when wrapping.
+ * This will hopefully be fixed before the final release and
+ * this can all be removed.
+ */
+ if (msg_type == CMS_ENVEL_SERVER || longhorn == 1) {
+ 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;
+ }
+#else
+ if (msg_type == CMS_ENVEL_SERVER) {
+ retval = wrap_signeddata(tmp_buf, tmp_buf_len,
+ &tmp_buf2, &tmp_buf2_len);
+ if (retval) {
+ pkiDebug("failed to encode signeddata\n");
+ goto cleanup;
+ }
+ vfy_buf = tmp_buf2;
+ vfy_buf_len = tmp_buf2_len;
+
+ } else {
+ vfy_buf = tmp_buf;
+ vfy_buf_len = tmp_buf_len;
+ }
+#endif
+
+#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 {
+ pkiDebug("PKCS7 Verification Failure\n");
+ goto cleanup;
+ }
+
+ retval = 0;
+
+ cleanup:
+
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ if (out != NULL)
+ BIO_free(out);
+ if (tmp_buf != NULL)
+ free(tmp_buf);
+ if (tmp_buf2 != NULL)
+ free(tmp_buf2);
+
+ return retval;
+}
+
+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 = 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;
+ }
+
+ if (cert == NULL) {
+ pkiDebug("%s: no certificate!\n", __FUNCTION__);
+ return retval;
+ }
+
+ 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(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(cert, i)) || !(ialt = X509V3_EXT_d2i(ext))) {
+ pkiDebug("%s: found no subject alt name extensions\n",
+ __FUNCTION__);
+ goto cleanup;
+ }
+ num_sans = sk_GENERAL_NAME_num(ialt);
+
+ pkiDebug("%s: found %d subject alt name extension(s)\n",
+ __FUNCTION__, num_sans);
+
+ /* OK, we're likely returning something. Allocate return values */
+ if (princs_ret != NULL) {
+ princs = calloc(num_sans + 1, sizeof(krb5_principal));
+ 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 < num_sans; i++) {
+ krb5_data name = { 0, 0, NULL };
+
+ gen = sk_GENERAL_NAME_value(ialt, i);
+ switch (gen->type) {
+ case GEN_OTHERNAME:
+ name.length = gen->d.otherName->value->value.sequence->length;
+ name.data = (char *)gen->d.otherName->value->value.sequence->data;
+ 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, &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("%s: unrecognized othername oid in SAN\n",
+ __FUNCTION__);
+ continue;
+ }
+
+ break;
+ case GEN_DNS:
+ 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);
+ if (dnss[d] == NULL) {
+ pkiDebug("%s: failed to duplicate dns name\n",
+ __FUNCTION__);
+ } else {
+ d++;
+ num_found++;
+ }
+ }
+ break;
+ default:
+ pkiDebug("%s: SAN type = %d expecting %d\n",
+ __FUNCTION__, gen->type, GEN_OTHERNAME);
+ }
+ }
+ sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
+ }
+
+ retval = 0;
+ if (princs)
+ *princs_ret = princs;
+ if (upns)
+ *upn_ret = upns;
+ if (dnss)
+ *dns_ret = dnss;
+
+ cleanup:
+ 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
+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 = EINVAL;
+
+ if (reqctx->received_cert == NULL) {
+ pkiDebug("%s: No certificate!\n", __FUNCTION__);
+ return retval;
+ }
+
+ return crypto_retrieve_X509_sans(context, plgctx, reqctx,
+ reqctx->received_cert, princs_ret,
+ upn_ret, dns_ret);
+}
+
+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) {
+ EXTENDED_KEY_USAGE *extusage;
+
+ 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);
+ 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:
+ pkiDebug("%s: returning retval %d, valid_eku %d\n",
+ __FUNCTION__, retval, *valid_eku);
+ return retval;
+}
+
+krb5_error_code
+pkinit_octetstring2key(krb5_context context,
+ krb5_enctype etype,
+ unsigned char *key,
+ unsigned int dh_key_len,
+ krb5_keyblock * key_block)
+{
+ krb5_error_code retval;
+ unsigned char *buf = NULL;
+ unsigned char md[SHA_DIGEST_LENGTH];
+ unsigned char counter;
+ size_t keybytes, keylength, offset;
+ krb5_data random_data;
+
+
+ if ((buf = (unsigned char *) malloc(dh_key_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memset(buf, 0, dh_key_len);
+
+ counter = 0;
+ offset = 0;
+ do {
+ SHA_CTX c;
+
+ SHA1_Init(&c);
+ SHA1_Update(&c, &counter, 1);
+ SHA1_Update(&c, key, dh_key_len);
+ SHA1_Final(md, &c);
+
+ if (dh_key_len - offset < sizeof(md))
+ memcpy(buf + offset, md, dh_key_len - offset);
+ else
+ memcpy(buf + offset, md, sizeof(md));
+
+ offset += sizeof(md);
+ counter++;
+ } while (offset < dh_key_len);
+
+ key_block->magic = 0;
+ key_block->enctype = etype;
+
+ retval = krb5_c_keylengths(context, etype, &keybytes, &keylength);
+ if (retval)
+ goto cleanup;
+
+ key_block->length = keylength;
+ key_block->contents = calloc(keylength, sizeof(unsigned char *));
+ if (key_block->contents == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ random_data.length = keybytes;
+ random_data.data = (char *)buf;
+
+ retval = krb5_c_random_to_key(context, etype, &random_data, key_block);
+
+ cleanup:
+ if (buf != NULL)
+ free(buf);
+ if (retval && key_block->contents != NULL && key_block->length != 0) {
+ memset(key_block->contents, 0, key_block->length);
+ key_block->length = 0;
+ }
+
+ return retval;
+}
+
+krb5_error_code
+client_create_dh(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int dh_size,
+ unsigned char **dh_params,
+ unsigned int *dh_params_len,
+ unsigned char **dh_pubkey,
+ unsigned int *dh_pubkey_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ unsigned char *buf = NULL;
+ int dh_err = 0;
+ ASN1_INTEGER *pub_key = NULL;
+
+ if (cryptoctx->dh == NULL) {
+ if ((cryptoctx->dh = DH_new()) == NULL)
+ goto cleanup;
+ if ((cryptoctx->dh->g = BN_new()) == NULL ||
+ (cryptoctx->dh->q = BN_new()) == NULL)
+ goto cleanup;
+
+ switch(dh_size) {
+ case 1024:
+ pkiDebug("client uses 1024 DH keys\n");
+ cryptoctx->dh->p = get_rfc2409_prime_1024(NULL);
+ break;
+ case 2048:
+ pkiDebug("client uses 2048 DH keys\n");
+ cryptoctx->dh->p = BN_bin2bn(pkinit_2048_dhprime,
+ sizeof(pkinit_2048_dhprime), NULL);
+ break;
+ case 4096:
+ pkiDebug("client uses 4096 DH keys\n");
+ cryptoctx->dh->p = BN_bin2bn(pkinit_4096_dhprime,
+ sizeof(pkinit_4096_dhprime), NULL);
+ break;
+ default:
+ goto cleanup;
+ }
+
+ BN_set_word((cryptoctx->dh->g), DH_GENERATOR_2);
+ BN_rshift1(cryptoctx->dh->q, cryptoctx->dh->p);
+ }
+
+ DH_generate_key(cryptoctx->dh);
+ DH_check(cryptoctx->dh, &dh_err);
+ if (dh_err != 0) {
+ pkiDebug("Warning: dh_check failed with %d\n", dh_err);
+ if (dh_err & DH_CHECK_P_NOT_PRIME)
+ pkiDebug("p value is not prime\n");
+ if (dh_err & DH_CHECK_P_NOT_SAFE_PRIME)
+ pkiDebug("p value is not a safe prime\n");
+ if (dh_err & DH_UNABLE_TO_CHECK_GENERATOR)
+ pkiDebug("unable to check the generator value\n");
+ if (dh_err & DH_NOT_SUITABLE_GENERATOR)
+ pkiDebug("the g value is not a generator\n");
+ }
+#ifdef DEBUG_DH
+ print_dh(cryptoctx->dh, "client's DH params\n");
+ print_pubkey(cryptoctx->dh->pub_key, "client's pub_key=");
+#endif
+
+ DH_check_pub_key(cryptoctx->dh, cryptoctx->dh->pub_key, &dh_err);
+ if (dh_err != 0) {
+ pkiDebug("dh_check_pub_key failed with %d\n", dh_err);
+ goto cleanup;
+ }
+
+ /* pack DHparams */
+ /* aglo: usually we could just call i2d_DHparams to encode DH params
+ * however, PKINIT requires RFC3279 encoding and openssl does pkcs#3.
+ */
+ retval = pkinit_encode_dh_params(cryptoctx->dh->p, cryptoctx->dh->g,
+ cryptoctx->dh->q, dh_params, dh_params_len);
+ if (retval)
+ goto cleanup;
+
+ /* pack DH public key */
+ /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this
+ * encoding shall be used as the contents (the value) of the
+ * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo
+ * data element
+ */
+ if ((pub_key = BN_to_ASN1_INTEGER(cryptoctx->dh->pub_key, NULL)) == NULL)
+ goto cleanup;
+ *dh_pubkey_len = i2d_ASN1_INTEGER(pub_key, NULL);
+ if ((buf = *dh_pubkey = (unsigned char *)
+ malloc((size_t) *dh_pubkey_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ i2d_ASN1_INTEGER(pub_key, &buf);
+
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+
+ retval = 0;
+ return retval;
+
+ cleanup:
+ if (cryptoctx->dh != NULL)
+ DH_free(cryptoctx->dh);
+ cryptoctx->dh = NULL;
+ if (*dh_params != NULL)
+ free(*dh_params);
+ *dh_params = NULL;
+ if (*dh_pubkey != NULL)
+ free(*dh_pubkey);
+ *dh_pubkey = NULL;
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+
+ return retval;
+}
+
+krb5_error_code
+client_process_dh(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *subjectPublicKey_data,
+ unsigned int subjectPublicKey_length,
+ unsigned char **client_key,
+ unsigned int *client_key_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ BIGNUM *server_pub_key = NULL;
+ ASN1_INTEGER *pub_key = NULL;
+ const unsigned char *p = NULL;
+ unsigned char *data = NULL;
+ long data_len;
+
+ /* decode subjectPublicKey (retrieve INTEGER from OCTET_STRING) */
+
+ if (der_decode_data(subjectPublicKey_data, (long)subjectPublicKey_length,
+ &data, &data_len) != 0) {
+ pkiDebug("failed to decode subjectPublicKey\n");
+ retval = -1;
+ goto cleanup;
+ }
+
+ *client_key_len = DH_size(cryptoctx->dh);
+ if ((*client_key = (unsigned char *)
+ malloc((size_t) *client_key_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ p = data;
+ if ((pub_key = d2i_ASN1_INTEGER(NULL, &p, data_len)) == NULL)
+ goto cleanup;
+ if ((server_pub_key = ASN1_INTEGER_to_BN(pub_key, NULL)) == NULL)
+ goto cleanup;
+
+ DH_compute_key(*client_key, server_pub_key, cryptoctx->dh);
+#ifdef DEBUG_DH
+ print_pubkey(server_pub_key, "server's pub_key=");
+ pkiDebug("client secret key (%d)= ", *client_key_len);
+ print_buffer(*client_key, *client_key_len);
+#endif
+
+ retval = 0;
+ if (server_pub_key != NULL)
+ BN_free(server_pub_key);
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+ if (data != NULL)
+ free (data);
+
+ return retval;
+
+ cleanup:
+ if (*client_key != NULL)
+ free(*client_key);
+ *client_key = NULL;
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+ if (data != NULL)
+ free (data);
+
+ return retval;
+}
+
+krb5_error_code
+server_check_dh(krb5_context context,
+ pkinit_plg_crypto_context cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_octet_data *dh_params,
+ int minbits)
+{
+ DH *dh = NULL;
+ unsigned char *tmp = NULL;
+ int dh_prime_bits;
+ krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+
+ tmp = dh_params->data;
+ dh = DH_new();
+ dh = pkinit_decode_dh_params(&dh, &tmp, dh_params->length);
+ if (dh == NULL) {
+ pkiDebug("failed to decode dhparams\n");
+ goto cleanup;
+ }
+
+ /* KDC SHOULD check to see if the key parameters satisfy its policy */
+ dh_prime_bits = BN_num_bits(dh->p);
+ if (minbits && dh_prime_bits < minbits) {
+ pkiDebug("client sent dh params with %d bits, we require %d\n",
+ dh_prime_bits, minbits);
+ goto cleanup;
+ }
+
+ /* check dhparams is group 2 */
+ if (pkinit_check_dh_params(cryptoctx->dh_1024->p,
+ dh->p, dh->g, dh->q) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ /* check dhparams is group 14 */
+ if (pkinit_check_dh_params(cryptoctx->dh_2048->p,
+ dh->p, dh->g, dh->q) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ /* check dhparams is group 16 */
+ if (pkinit_check_dh_params(cryptoctx->dh_4096->p,
+ dh->p, dh->g, dh->q) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ cleanup:
+ if (retval == 0)
+ req_cryptoctx->dh = dh;
+ else
+ DH_free(dh);
+
+ return retval;
+}
+
+/* kdc's dh function */
+krb5_error_code
+server_process_dh(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **dh_pubkey,
+ unsigned int *dh_pubkey_len,
+ unsigned char **server_key,
+ unsigned int *server_key_len)
+{
+ krb5_error_code retval = ENOMEM;
+ DH *dh = NULL, *dh_server = NULL;
+ unsigned char *p = NULL;
+ ASN1_INTEGER *pub_key = NULL;
+
+ /* get client's received DH parameters that we saved in server_check_dh */
+ dh = cryptoctx->dh;
+
+ dh_server = DH_new();
+ if (dh_server == NULL)
+ goto cleanup;
+ dh_server->p = BN_dup(dh->p);
+ dh_server->g = BN_dup(dh->g);
+ dh_server->q = BN_dup(dh->q);
+
+ /* decode client's public key */
+ p = data;
+ pub_key = d2i_ASN1_INTEGER(NULL, (const unsigned char **)&p, (int)data_len);
+ if (pub_key == NULL)
+ goto cleanup;
+ dh->pub_key = ASN1_INTEGER_to_BN(pub_key, NULL);
+ if (dh->pub_key == NULL)
+ goto cleanup;
+ ASN1_INTEGER_free(pub_key);
+
+ if (!DH_generate_key(dh_server))
+ goto cleanup;
+
+ /* generate DH session key */
+ *server_key_len = DH_size(dh_server);
+ if ((*server_key = (unsigned char *) malloc((size_t)*server_key_len)) == NULL)
+ goto cleanup;
+ DH_compute_key(*server_key, dh->pub_key, dh_server);
+
+#ifdef DEBUG_DH
+ print_dh(dh_server, "client&server's DH params\n");
+ print_pubkey(dh->pub_key, "client's pub_key=");
+ print_pubkey(dh_server->pub_key, "server's pub_key=");
+ pkiDebug("server secret key=");
+ print_buffer(*server_key, *server_key_len);
+#endif
+
+ /* KDC reply */
+ /* pack DH public key */
+ /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this
+ * encoding shall be used as the contents (the value) of the
+ * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo
+ * data element
+ */
+ if ((pub_key = BN_to_ASN1_INTEGER(dh_server->pub_key, NULL)) == NULL)
+ goto cleanup;
+ *dh_pubkey_len = i2d_ASN1_INTEGER(pub_key, NULL);
+ if ((p = *dh_pubkey = (unsigned char *) malloc((size_t)*dh_pubkey_len)) == NULL)
+ goto cleanup;
+ i2d_ASN1_INTEGER(pub_key, &p);
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+
+ retval = 0;
+
+ if (dh_server != NULL)
+ DH_free(dh_server);
+ return retval;
+
+ cleanup:
+ if (dh_server != NULL)
+ DH_free(dh_server);
+ if (*dh_pubkey != NULL)
+ free(*dh_pubkey);
+ if (*server_key != NULL)
+ free(*server_key);
+
+ return retval;
+}
+
+static void
+openssl_init()
+{
+ static int did_init = 0;
+
+ if (!did_init) {
+ /* initialize openssl routines */
+ CRYPTO_malloc_init();
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_algorithms();
+ did_init++;
+ }
+}
+
+static krb5_error_code
+pkinit_encode_dh_params(BIGNUM *p, BIGNUM *g, BIGNUM *q,
+ unsigned char **buf, unsigned int *buf_len)
+{
+ krb5_error_code retval = ENOMEM;
+ int bufsize = 0, r = 0;
+ unsigned char *tmp = NULL;
+ ASN1_INTEGER *ap = NULL, *ag = NULL, *aq = NULL;
+
+ if ((ap = BN_to_ASN1_INTEGER(p, NULL)) == NULL)
+ goto cleanup;
+ if ((ag = BN_to_ASN1_INTEGER(g, NULL)) == NULL)
+ goto cleanup;
+ if ((aq = BN_to_ASN1_INTEGER(q, NULL)) == NULL)
+ goto cleanup;
+ bufsize = i2d_ASN1_INTEGER(ap, NULL);
+ bufsize += i2d_ASN1_INTEGER(ag, NULL);
+ bufsize += i2d_ASN1_INTEGER(aq, NULL);
+
+ r = ASN1_object_size(1, bufsize, V_ASN1_SEQUENCE);
+
+ tmp = *buf = (unsigned char *)malloc((size_t) r);
+ if (tmp == NULL)
+ goto cleanup;
+
+ ASN1_put_object(&tmp, 1, bufsize, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_INTEGER(ap, &tmp);
+ i2d_ASN1_INTEGER(ag, &tmp);
+ i2d_ASN1_INTEGER(aq, &tmp);
+
+ *buf_len = r;
+
+ retval = 0;
+
+cleanup:
+ if (ap != NULL)
+ ASN1_INTEGER_free(ap);
+ if (ag != NULL)
+ ASN1_INTEGER_free(ag);
+ if (aq != NULL)
+ ASN1_INTEGER_free(aq);
+
+ return retval;
+}
+
+static DH *
+pkinit_decode_dh_params(DH ** a, unsigned char **pp, unsigned int len)
+{
+ ASN1_INTEGER ai, *aip = NULL;
+ long length = (long) len;
+
+ M_ASN1_D2I_vars(a, DH *, DH_new);
+
+ M_ASN1_D2I_Init();
+ M_ASN1_D2I_start_sequence();
+ aip = &ai;
+ ai.data = NULL;
+ ai.length = 0;
+ M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER);
+ if (aip == NULL)
+ return NULL;
+ else {
+ (*a)->p = ASN1_INTEGER_to_BN(aip, NULL);
+ if ((*a)->p == NULL)
+ return NULL;
+ if (ai.data != NULL) {
+ OPENSSL_free(ai.data);
+ ai.data = NULL;
+ ai.length = 0;
+ }
+ }
+ M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER);
+ if (aip == NULL)
+ return NULL;
+ else {
+ (*a)->g = ASN1_INTEGER_to_BN(aip, NULL);
+ if ((*a)->g == NULL)
+ return NULL;
+ if (ai.data != NULL) {
+ OPENSSL_free(ai.data);
+ ai.data = NULL;
+ ai.length = 0;
+ }
+
+ }
+ M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER);
+ if (aip == NULL)
+ return NULL;
+ else {
+ (*a)->q = ASN1_INTEGER_to_BN(aip, NULL);
+ if ((*a)->q == NULL)
+ return NULL;
+ if (ai.data != NULL) {
+ OPENSSL_free(ai.data);
+ ai.data = NULL;
+ ai.length = 0;
+ }
+
+ }
+ M_ASN1_D2I_end_sequence();
+ M_ASN1_D2I_Finish(a, DH_free, 0);
+
+}
+
+static krb5_error_code
+pkinit_create_sequence_of_principal_identifiers(
+ krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int type,
+ krb5_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+ krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
+ krb5_data *td_certifiers = NULL, *data = NULL;
+ 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) {
+ pkiDebug("create_krb5_trustedCertifiers failed\n");
+ goto cleanup;
+ }
+ break;
+ case TD_INVALID_CERTIFICATES:
+ retval = create_krb5_invalidCertificates(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("create_krb5_invalidCertificates failed\n");
+ goto cleanup;
+ }
+ break;
+ default:
+ retval = -1;
+ goto cleanup;
+ }
+
+ retval = k5int_encode_krb5_td_trusted_certifiers((const krb5_external_principal_identifier **)krb5_trusted_certifiers, &td_certifiers);
+ if (retval) {
+ pkiDebug("encode_krb5_td_trusted_certifiers failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)td_certifiers->data,
+ td_certifiers->length, "/tmp/kdc_td_certifiers");
+#endif
+ typed_data = malloc (2 * sizeof(krb5_typed_data *));
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[1] = NULL;
+ init_krb5_typed_data(&typed_data[0]);
+ if (typed_data[0] == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[0]->type = type;
+ typed_data[0]->length = td_certifiers->length;
+ typed_data[0]->data = (unsigned char *)td_certifiers->data;
+ retval = k5int_encode_krb5_typed_data((const krb5_typed_data **)typed_data,
+ &data);
+ if (retval) {
+ pkiDebug("encode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)data->data, data->length,
+ "/tmp/kdc_edata");
+#endif
+ *out_data = (krb5_data *)malloc(sizeof(krb5_data));
+ (*out_data)->length = data->length;
+ (*out_data)->data = (char *)malloc(data->length);
+ memcpy((*out_data)->data, data->data, data->length);
+
+ retval = 0;
+
+cleanup:
+ if (krb5_trusted_certifiers != NULL)
+ free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+
+ if (data != NULL) {
+ if (data->data != NULL)
+ free(data->data);
+ free(data);
+ }
+
+ if (td_certifiers != NULL)
+ free(td_certifiers);
+
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_create_td_trusted_certifiers(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+
+ retval = pkinit_create_sequence_of_principal_identifiers(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx,
+ TD_TRUSTED_CERTIFIERS, out_data);
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_create_td_invalid_certificate(
+ krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+
+ retval = pkinit_create_sequence_of_principal_identifiers(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx,
+ TD_INVALID_CERTIFICATES, out_data);
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_create_td_dh_parameters(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ pkinit_plg_opts *opts,
+ krb5_data **out_data)
+{
+ krb5_error_code retval = ENOMEM;
+ unsigned int buf1_len = 0, buf2_len = 0, buf3_len = 0, i = 0;
+ unsigned char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL;
+ krb5_typed_data **typed_data = NULL;
+ krb5_data *data = NULL, *encoded_algId = NULL;
+ krb5_algorithm_identifier **algId = NULL;
+
+ if (opts->dh_min_bits > 4096)
+ goto cleanup;
+
+ if (opts->dh_min_bits <= 1024) {
+ retval = pkinit_encode_dh_params(plg_cryptoctx->dh_1024->p,
+ plg_cryptoctx->dh_1024->g, plg_cryptoctx->dh_1024->q,
+ &buf1, &buf1_len);
+ if (retval)
+ goto cleanup;
+ }
+ if (opts->dh_min_bits <= 2048) {
+ retval = pkinit_encode_dh_params(plg_cryptoctx->dh_2048->p,
+ plg_cryptoctx->dh_2048->g, plg_cryptoctx->dh_2048->q,
+ &buf2, &buf2_len);
+ if (retval)
+ goto cleanup;
+ }
+ retval = pkinit_encode_dh_params(plg_cryptoctx->dh_4096->p,
+ plg_cryptoctx->dh_4096->g, plg_cryptoctx->dh_4096->q,
+ &buf3, &buf3_len);
+ if (retval)
+ goto cleanup;
+
+ if (opts->dh_min_bits <= 1024) {
+ algId = malloc(4 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[3] = NULL;
+ algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = (unsigned char *)malloc(buf2_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf2, buf2_len);
+ algId[0]->parameters.length = buf2_len;
+ algId[0]->algorithm = dh_oid;
+
+ algId[1] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[1] == NULL)
+ goto cleanup;
+ algId[1]->parameters.data = (unsigned char *)malloc(buf3_len);
+ if (algId[1]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[1]->parameters.data, buf3, buf3_len);
+ algId[1]->parameters.length = buf3_len;
+ algId[1]->algorithm = dh_oid;
+
+ algId[2] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[2] == NULL)
+ goto cleanup;
+ algId[2]->parameters.data = (unsigned char *)malloc(buf1_len);
+ if (algId[2]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[2]->parameters.data, buf1, buf1_len);
+ algId[2]->parameters.length = buf1_len;
+ algId[2]->algorithm = dh_oid;
+
+ } else if (opts->dh_min_bits <= 2048) {
+ algId = malloc(3 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[2] = NULL;
+ algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = (unsigned char *)malloc(buf2_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf2, buf2_len);
+ algId[0]->parameters.length = buf2_len;
+ algId[0]->algorithm = dh_oid;
+
+ algId[1] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[1] == NULL)
+ goto cleanup;
+ algId[1]->parameters.data = (unsigned char *)malloc(buf3_len);
+ if (algId[1]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[1]->parameters.data, buf3, buf3_len);
+ algId[1]->parameters.length = buf3_len;
+ algId[1]->algorithm = dh_oid;
+
+ } else if (opts->dh_min_bits <= 4096) {
+ algId = malloc(2 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[1] = NULL;
+ algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = (unsigned char *)malloc(buf3_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf3, buf3_len);
+ algId[0]->parameters.length = buf3_len;
+ algId[0]->algorithm = dh_oid;
+
+ }
+ retval = k5int_encode_krb5_td_dh_parameters((const krb5_algorithm_identifier **)algId, &encoded_algId);
+ if (retval)
+ goto cleanup;
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_algId->data,
+ encoded_algId->length, "/tmp/kdc_td_dh_params");
+#endif
+ typed_data = malloc (2 * sizeof(krb5_typed_data *));
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[1] = NULL;
+ init_krb5_typed_data(&typed_data[0]);
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[0]->type = TD_DH_PARAMETERS;
+ typed_data[0]->length = encoded_algId->length;
+ typed_data[0]->data = (unsigned char *)encoded_algId->data;
+ retval = k5int_encode_krb5_typed_data((const krb5_typed_data**)typed_data,
+ &data);
+ if (retval) {
+ pkiDebug("encode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)data->data, data->length,
+ "/tmp/kdc_edata");
+#endif
+ *out_data = (krb5_data *)malloc(sizeof(krb5_data));
+ if (*out_data == NULL)
+ goto cleanup;
+ (*out_data)->length = data->length;
+ (*out_data)->data = (char *)malloc(data->length);
+ if ((*out_data)->data == NULL) {
+ free(*out_data);
+ *out_data = NULL;
+ goto cleanup;
+ }
+ memcpy((*out_data)->data, data->data, data->length);
+
+ retval = 0;
+cleanup:
+
+ if (buf1 != NULL)
+ free(buf1);
+ if (buf2 != NULL)
+ free(buf2);
+ if (buf3 != NULL)
+ free(buf3);
+ if (data != NULL) {
+ if (data->data != NULL)
+ free(data->data);
+ free(data);
+ }
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+ if (encoded_algId != NULL)
+ free(encoded_algId);
+
+ if (algId != NULL) {
+ while(algId[i] != NULL) {
+ if (algId[i]->parameters.data != NULL)
+ free(algId[i]->parameters.data);
+ free(algId[i]);
+ i++;
+ }
+ free(algId);
+ }
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_check_kdc_pkid(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *pdid_buf,
+ unsigned int pkid_len,
+ int *valid_kdcPkId)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ 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, id_cryptoctx->cert_index);
+
+ *valid_kdcPkId = 0;
+ pkiDebug("found kdcPkId in AS REQ\n");
+ is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p, (int)pkid_len);
+ if (is == NULL)
+ goto cleanup;
+
+ status = X509_NAME_cmp(X509_get_issuer_name(kdc_cert), is->issuer);
+ if (!status) {
+ status = ASN1_INTEGER_cmp(X509_get_serialNumber(kdc_cert), is->serial);
+ if (!status)
+ *valid_kdcPkId = 1;
+ }
+
+ retval = 0;
+cleanup:
+ X509_NAME_free(is->issuer);
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+
+ return retval;
+}
+
+static int
+pkinit_check_dh_params(BIGNUM * p1, BIGNUM * p2, BIGNUM * g1, BIGNUM * q1)
+{
+ BIGNUM *g2 = NULL, *q2 = NULL;
+ int retval = -1;
+
+ if (!BN_cmp(p1, p2)) {
+ g2 = BN_new();
+ BN_set_word(g2, DH_GENERATOR_2);
+ if (!BN_cmp(g1, g2)) {
+ q2 = BN_new();
+ BN_rshift1(q2, p1);
+ if (!BN_cmp(q1, q2)) {
+ pkiDebug("good %d dhparams\n", BN_num_bits(p1));
+ retval = 0;
+ } else
+ pkiDebug("bad group 2 q dhparameter\n");
+ BN_free(q2);
+ } else
+ pkiDebug("bad g dhparameter\n");
+ BN_free(g2);
+ } else
+ pkiDebug("p is not well-known group 2 dhparameter\n");
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_process_td_dh_params(krb5_context context,
+ pkinit_plg_crypto_context cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_algorithm_identifier **algId,
+ int *new_dh_size)
+{
+ krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+ int i = 0, use_sent_dh = 0, ok = 0;
+
+ pkiDebug("dh parameters\n");
+
+ while (algId[i] != NULL) {
+ DH *dh = NULL;
+ unsigned char *tmp = NULL;
+ int dh_prime_bits = 0;
+
+ if (algId[i]->algorithm.length != dh_oid.length ||
+ memcmp(algId[i]->algorithm.data, dh_oid.data, dh_oid.length))
+ goto cleanup;
+
+ tmp = algId[i]->parameters.data;
+ dh = DH_new();
+ dh = pkinit_decode_dh_params(&dh, &tmp, algId[i]->parameters.length);
+ dh_prime_bits = BN_num_bits(dh->p);
+ pkiDebug("client sent %d DH bits server prefers %d DH bits\n",
+ *new_dh_size, dh_prime_bits);
+ switch(dh_prime_bits) {
+ case 1024:
+ if (pkinit_check_dh_params(cryptoctx->dh_1024->p, dh->p,
+ dh->g, dh->q) == 0) {
+ *new_dh_size = 1024;
+ ok = 1;
+ }
+ break;
+ case 2048:
+ if (pkinit_check_dh_params(cryptoctx->dh_2048->p, dh->p,
+ dh->g, dh->q) == 0) {
+ *new_dh_size = 2048;
+ ok = 1;
+ }
+ break;
+ case 4096:
+ if (pkinit_check_dh_params(cryptoctx->dh_4096->p, dh->p,
+ dh->g, dh->q) == 0) {
+ *new_dh_size = 4096;
+ ok = 1;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!ok) {
+ DH_check(dh, &retval);
+ if (retval != 0) {
+ pkiDebug("DH parameters provided by server are unacceptable\n");
+ retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+ }
+ else {
+ use_sent_dh = 1;
+ ok = 1;
+ }
+ }
+ if (!use_sent_dh)
+ DH_free(dh);
+ if (ok) {
+ if (req_cryptoctx->dh != NULL) {
+ DH_free(req_cryptoctx->dh);
+ req_cryptoctx->dh = NULL;
+ }
+ if (use_sent_dh)
+ req_cryptoctx->dh = dh;
+ break;
+ }
+ i++;
+ }
+
+ if (ok)
+ retval = 0;
+
+cleanup:
+ return retval;
+}
+
+static int
+openssl_callback(int ok, X509_STORE_CTX * ctx)
+{
+#ifdef DEBUG
+ if (!ok) {
+ char buf[DN_BUF_LEN];
+
+ 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));
+ }
+#endif
+ return ok;
+}
+
+static int
+openssl_callback_ignore_crls(int ok, X509_STORE_CTX * ctx)
+{
+ if (!ok) {
+ switch (ctx->error) {
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+ return ok;
+}
+
+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;
+ case CMS_ENVEL_SERVER:
+ return cryptoctx->id_pkinit_rkeyData;
+ default:
+ return NULL;
+ }
+
+}
+
+#ifdef LONGHORN_BETA_COMPAT
+#if 0
+/*
+ * This is a version that worked with Longhorn Beta 3.
+ */
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server)
+{
+
+ unsigned int orig_len = 0, oid_len = 0, tot_len = 0;
+ ASN1_OBJECT *oid = NULL;
+ unsigned char *p = NULL;
+
+ pkiDebug("%s: This is the Longhorn version and is_longhorn_server = %d\n",
+ __FUNCTION__, is_longhorn_server);
+
+ /* 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);
+ }
+
+ p = *out = (unsigned char *)malloc(tot_len);
+ if (p == NULL) return -1;
+
+ 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 = tot_len;
+
+ return 0;
+}
+#else
+/*
+ * This is a version that works with a patched Longhorn KDC.
+ * (Which should match SP1 ??).
+ */
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server)
+{
+
+ unsigned int oid_len = 0, tot_len = 0, wrap_len = 0, tag_len = 0;
+ ASN1_OBJECT *oid = NULL;
+ unsigned char *p = NULL;
+
+ pkiDebug("%s: This is the Longhorn version and is_longhorn_server = %d\n",
+ __FUNCTION__, is_longhorn_server);
+
+ /* New longhorn is missing another sequence */
+ if (is_longhorn_server == 1)
+ wrap_len = ASN1_object_size(1, (int)(data_len), V_ASN1_SEQUENCE);
+ else
+ wrap_len = data_len;
+
+ /* Get length to wrap the original data with SEQUENCE tag */
+ tag_len = ASN1_object_size(1, (int)wrap_len, V_ASN1_SEQUENCE);
+
+ /* Always add oid */
+ oid = OBJ_nid2obj(NID_pkcs7_signed);
+ oid_len = i2d_ASN1_OBJECT(oid, NULL);
+ oid_len += tag_len;
+
+ tot_len = ASN1_object_size(1, (int)(oid_len), V_ASN1_SEQUENCE);
+
+ p = *out = (unsigned char *)malloc(tot_len);
+ if (p == NULL)
+ return -1;
+
+ ASN1_put_object(&p, 1, (int)(oid_len),
+ V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_OBJECT(oid, &p);
+
+ ASN1_put_object(&p, 1, (int)wrap_len, 0, V_ASN1_CONTEXT_SPECIFIC);
+
+ /* Wrap in extra seq tag */
+ if (is_longhorn_server == 1) {
+ ASN1_put_object(&p, 1, (int)data_len, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+ }
+ memcpy(p, data, data_len);
+
+ *out_len = tot_len;
+
+ return 0;
+}
+
+#endif
+#else
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len)
+{
+
+ unsigned int orig_len = 0, oid_len = 0, tot_len = 0;
+ ASN1_OBJECT *oid = NULL;
+ unsigned char *p = NULL;
+
+ /* Get length to wrap the original data with SEQUENCE tag */
+ tot_len = orig_len = ASN1_object_size(1, (int)data_len, V_ASN1_SEQUENCE);
+
+ /* 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);
+
+ p = *out = (unsigned char *)malloc(tot_len);
+ if (p == NULL) return -1;
+
+ 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);
+ memcpy(p, data, data_len);
+
+ *out_len = tot_len;
+
+ return 0;
+}
+#endif
+
+static int
+prepare_enc_data(unsigned char *indata,
+ int indata_len,
+ unsigned char **outdata,
+ int *outdata_len)
+{
+ int retval = -1;
+ ASN1_const_CTX c;
+ long length = indata_len;
+ int Ttag, Tclass;
+ long Tlen;
+
+ c.pp = (const unsigned char **)&indata;
+ c.q = *(const unsigned char **)&indata;
+ c.error = ERR_R_NESTED_ASN1_ERROR;
+ c.p= *(const unsigned char **)&indata;
+ c.max = (length == 0)?0:(c.p+length);
+
+ asn1_GetSequence(&c,&length);
+
+ ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen);
+ c.p += Tlen;
+ ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen);
+
+ asn1_const_Finish(&c);
+
+ *outdata = (unsigned char *)malloc((size_t)Tlen);
+ if (outdata == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(*outdata, c.p, (size_t)Tlen);
+ *outdata_len = Tlen;
+
+ retval = 0;
+cleanup:
+
+ return retval;
+}
+
+#ifndef WITHOUT_PKCS11
+static void *
+pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p)
+{
+ void *handle;
+ CK_RV (*getflist)(CK_FUNCTION_LIST_PTR_PTR);
+
+ pkiDebug("loading module \"%s\"... ", modname);
+ handle = dlopen(modname, RTLD_NOW);
+ if (handle == NULL) {
+ pkiDebug("not found\n");
+ return NULL;
+ }
+ getflist = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(handle, "C_GetFunctionList");
+ if (getflist == NULL || (*getflist)(p11p) != CKR_OK) {
+ dlclose(handle);
+ pkiDebug("failed\n");
+ return NULL;
+ }
+ pkiDebug("ok\n");
+ return handle;
+}
+
+static CK_RV
+pkinit_C_UnloadModule(void *handle)
+{
+ dlclose(handle);
+ return CKR_OK;
+}
+
+static krb5_error_code
+pkinit_login(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ CK_TOKEN_INFO *tip)
+{
+ krb5_data rdat;
+ char *prompt;
+ krb5_prompt kprompt;
+ krb5_prompt_type prompt_type;
+ int r = 0;
+
+ if (tip->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
+ rdat.data = NULL;
+ rdat.length = 0;
+ } else {
+ 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;
+
+ kprompt.prompt = prompt;
+ kprompt.hidden = 1;
+ kprompt.reply = &rdat;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+
+ /* PROMPTER_INVOCATION */
+ k5int_set_prompt_types(context, &prompt_type);
+ r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+ NULL, NULL, 1, &kprompt);
+ k5int_set_prompt_types(context, 0);
+ free(prompt);
+ }
+
+ if (r == 0) {
+ r = id_cryptoctx->p11->C_Login(id_cryptoctx->session, CKU_USER,
+ (u_char *) rdat.data, rdat.length);
+
+ if (r != CKR_OK) {
+ pkiDebug("C_Login: %s\n", pkinit_pkcs11_code_to_text(r));
+ r = KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ }
+ if (rdat.data)
+ free(rdat.data);
+
+ return r;
+}
+
+static krb5_error_code
+pkinit_open_session(krb5_context context,
+ pkinit_identity_crypto_context cctx)
+{
+ int i, r;
+ unsigned char *cp;
+ CK_ULONG count = 0;
+ CK_SLOT_ID_PTR slotlist;
+ CK_TOKEN_INFO tinfo;
+
+ if (cctx->p11_module != NULL)
+ return 0; /* session already open */
+
+ /* Load module */
+ cctx->p11_module =
+ pkinit_C_LoadModule(cctx->p11_module_name, &cctx->p11);
+ if (cctx->p11_module == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ /* Init */
+ if ((r = cctx->p11->C_Initialize(NULL)) != CKR_OK) {
+ pkiDebug("C_Initialize: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Get the list of available slots */
+ if (cctx->slotid != PK_NOSLOT) {
+ /* A slot was specified, so that's the only one in the list */
+ count = 1;
+ slotlist = (CK_SLOT_ID_PTR) malloc(sizeof (CK_SLOT_ID));
+ slotlist[0] = cctx->slotid;
+ } else {
+ if (cctx->p11->C_GetSlotList(TRUE, NULL, &count) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ if (count == 0)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ slotlist = (CK_SLOT_ID_PTR) malloc(count * sizeof (CK_SLOT_ID));
+ if (cctx->p11->C_GetSlotList(TRUE, slotlist, &count) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Look for the given token label, or if none given take the first one */
+ for (i = 0; i < count; i++) {
+ /* Open session */
+ if ((r = cctx->p11->C_OpenSession(slotlist[i], CKF_SERIAL_SESSION,
+ NULL, NULL, &cctx->session)) != CKR_OK) {
+ pkiDebug("C_OpenSession: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Get token info */
+ if ((r = cctx->p11->C_GetTokenInfo(slotlist[i], &tinfo)) != CKR_OK) {
+ pkiDebug("C_GetTokenInfo: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ for (cp = tinfo.label + sizeof (tinfo.label) - 1;
+ *cp == '\0' || *cp == ' '; cp--)
+ *cp = '\0';
+ pkiDebug("open_session: slotid %d token \"%s\"\n",
+ (int) slotlist[i], tinfo.label);
+ if (cctx->token_label == NULL ||
+ !strcmp((char *) cctx->token_label, (char *) tinfo.label))
+ break;
+ cctx->p11->C_CloseSession(cctx->session);
+ }
+ if (i >= count) {
+ free(slotlist);
+ pkiDebug("open_session: no matching token found\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ cctx->slotid = slotlist[i];
+ free(slotlist);
+ pkiDebug("open_session: slotid %d (%d of %d)\n", (int) cctx->slotid,
+ i + 1, (int) count);
+
+ /* Login if needed */
+ if (tinfo.flags & CKF_LOGIN_REQUIRED)
+ r = pkinit_login(context, cctx, &tinfo);
+
+ return r;
+}
+
+/*
+ * Look for a key that's:
+ * 1. private
+ * 2. capable of the specified operation (usually signing or decrypting)
+ * 3. RSA (this may be wrong but it's all we can do for now)
+ * 4. matches the id of the cert we chose
+ *
+ * You must call pkinit_get_certs before calling pkinit_find_private_key
+ * (that's because we need the ID of the private key)
+ *
+ * pkcs11 says the id of the key doesn't have to match that of the cert, but
+ * I can't figure out any other way to decide which key to use.
+ *
+ * We should only find one key that fits all the requirements.
+ * If there are more than one, we just take the first one.
+ */
+
+krb5_error_code
+pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx,
+ CK_ATTRIBUTE_TYPE usage,
+ CK_OBJECT_HANDLE *objp)
+{
+ CK_OBJECT_CLASS cls;
+ CK_ATTRIBUTE attrs[4];
+ CK_ULONG count;
+ CK_KEY_TYPE keytype;
+ unsigned int nattrs = 0;
+ int r;
+#ifdef PKINIT_USE_KEY_USAGE
+ CK_BBOOL true_false;
+#endif
+
+ cls = CKO_PRIVATE_KEY;
+ attrs[nattrs].type = CKA_CLASS;
+ attrs[nattrs].pValue = &cls;
+ attrs[nattrs].ulValueLen = sizeof cls;
+ nattrs++;
+
+#ifdef PKINIT_USE_KEY_USAGE
+ /*
+ * Some cards get confused if you try to specify a key usage,
+ * so don't, and hope for the best. This will fail if you have
+ * several keys with the same id and different usages but I have
+ * not seen this on real cards.
+ */
+ true_false = TRUE;
+ attrs[nattrs].type = usage;
+ attrs[nattrs].pValue = &true_false;
+ attrs[nattrs].ulValueLen = sizeof true_false;
+ nattrs++;
+#endif
+
+ keytype = CKK_RSA;
+ attrs[nattrs].type = CKA_KEY_TYPE;
+ attrs[nattrs].pValue = &keytype;
+ attrs[nattrs].ulValueLen = sizeof keytype;
+ nattrs++;
+
+ attrs[nattrs].type = CKA_ID;
+ attrs[nattrs].pValue = id_cryptoctx->cert_id;
+ attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len;
+ nattrs++;
+
+ 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 (%s)\n", (int) count, pkinit_pkcs11_code_to_text(r));
+ if (r != CKR_OK || count < 1)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ return 0;
+}
+#endif
+
+static krb5_error_code
+pkinit_decode_data_fs(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **decoded_data,
+ unsigned int *decoded_data_len)
+{
+ if (decode_data(decoded_data, decoded_data_len, data, data_len,
+ id_cryptoctx->my_key, sk_X509_value(id_cryptoctx->my_certs,
+ id_cryptoctx->cert_index)) <= 0) {
+ pkiDebug("failed to decode data\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ return 0;
+}
+
+#ifndef WITHOUT_PKCS11
+#ifdef SILLYDECRYPT
+CK_RV
+pkinit_C_Decrypt(pkinit_identity_crypto_context id_cryptoctx,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen)
+{
+ CK_RV rv = CKR_OK;
+
+ rv = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, pEncryptedData,
+ ulEncryptedDataLen, pData, pulDataLen);
+ if (rv == CKR_OK) {
+ pkiDebug("pData %x *pulDataLen %d\n", (int) pData, (int) *pulDataLen);
+ }
+ return rv;
+}
+#endif
+
+static krb5_error_code
+pkinit_decode_data_pkcs11(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **decoded_data,
+ unsigned int *decoded_data_len)
+{
+ CK_OBJECT_HANDLE obj;
+ CK_ULONG len;
+ CK_MECHANISM mech;
+ unsigned char *cp;
+ int r;
+
+ if (pkinit_open_session(context, id_cryptoctx)) {
+ pkiDebug("can't open pkcs11 session\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ pkinit_find_private_key(id_cryptoctx, CKA_DECRYPT, &obj);
+
+ mech.mechanism = CKM_RSA_PKCS;
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+
+ if ((r = id_cryptoctx->p11->C_DecryptInit(id_cryptoctx->session, &mech,
+ obj)) != CKR_OK) {
+ pkiDebug("C_DecryptInit: 0x%x\n", (int) r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("data_len = %d\n", data_len);
+ cp = (unsigned char *)malloc((size_t) data_len);
+ if (cp == NULL)
+ return ENOMEM;
+ len = data_len;
+#ifdef SILLYDECRYPT
+ pkiDebug("session %x edata %x edata_len %d data %x datalen @%x %d\n",
+ (int) id_cryptoctx->session, (int) data, (int) data_len, (int) cp,
+ (int) &len, (int) len);
+ if ((r = pkinit_C_Decrypt(id_cryptoctx, data, (CK_ULONG) data_len,
+ cp, &len)) != CKR_OK) {
+#else
+ if ((r = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, data,
+ (CK_ULONG) data_len, cp, &len)) != CKR_OK) {
+#endif
+ 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;
+ }
+ pkiDebug("decrypt %d -> %d\n", (int) data_len, (int) len);
+ *decoded_data_len = len;
+ *decoded_data = cp;
+
+ return 0;
+}
+#endif
+
+krb5_error_code
+pkinit_decode_data(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **decoded_data,
+ unsigned int *decoded_data_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ 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
+ else
+ retval = pkinit_decode_data_pkcs11(context, id_cryptoctx, data,
+ data_len, decoded_data, decoded_data_len);
+#endif
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_sign_data_fs(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **sig,
+ unsigned int *sig_len)
+{
+ if (create_signature(sig, sig_len, data, data_len,
+ id_cryptoctx->my_key) != 0) {
+ pkiDebug("failed to create the signature\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ return 0;
+}
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code
+pkinit_sign_data_pkcs11(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **sig,
+ unsigned int *sig_len)
+{
+ CK_OBJECT_HANDLE obj;
+ CK_ULONG len;
+ CK_MECHANISM mech;
+ unsigned char *cp;
+ int r;
+
+ if (pkinit_open_session(context, id_cryptoctx)) {
+ pkiDebug("can't open pkcs11 session\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ pkinit_find_private_key(id_cryptoctx, CKA_SIGN, &obj);
+
+ mech.mechanism = id_cryptoctx->mech;
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+
+ if ((r = id_cryptoctx->p11->C_SignInit(id_cryptoctx->session, &mech,
+ obj)) != CKR_OK) {
+ pkiDebug("C_SignInit: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /*
+ * Key len would give an upper bound on sig size, but there's no way to
+ * get that. So guess, and if it's too small, re-malloc.
+ */
+ len = PK_SIGLEN_GUESS;
+ cp = (unsigned char *)malloc((size_t) len);
+ if (cp == NULL)
+ return ENOMEM;
+
+ r = id_cryptoctx->p11->C_Sign(id_cryptoctx->session, data,
+ (CK_ULONG) data_len, cp, &len);
+ if (r == CKR_BUFFER_TOO_SMALL || (r == CKR_OK && len >= PK_SIGLEN_GUESS)) {
+ free(cp);
+ pkiDebug("C_Sign realloc %d\n", (int) len);
+ cp = (unsigned char *)malloc((size_t) len);
+ r = id_cryptoctx->p11->C_Sign(id_cryptoctx->session, data,
+ (CK_ULONG) data_len, cp, &len);
+ }
+ if (r != CKR_OK) {
+ 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);
+ *sig_len = len;
+ *sig = cp;
+
+ return 0;
+}
+#endif
+
+krb5_error_code
+pkinit_sign_data(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **sig,
+ unsigned int *sig_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ 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
+ else
+ retval = pkinit_sign_data_pkcs11(context, id_cryptoctx, data, data_len,
+ sig, sig_len);
+#endif
+
+ return retval;
+}
+
+
+static krb5_error_code
+decode_data(unsigned char **out_data, unsigned int *out_data_len,
+ unsigned char *data, unsigned int data_len,
+ EVP_PKEY *pkey, X509 *cert)
+{
+ krb5_error_code retval = ENOMEM;
+ unsigned char *buf = NULL;
+ int buf_len = 0;
+
+ if (cert && !X509_check_private_key(cert, pkey)) {
+ pkiDebug("private key does not match certificate\n");
+ goto cleanup;
+ }
+
+ buf_len = EVP_PKEY_size(pkey);
+ buf = (unsigned char *)malloc((size_t) buf_len + 10);
+ if (buf == NULL)
+ goto cleanup;
+
+ retval = EVP_PKEY_decrypt(buf, data, (int)data_len, pkey);
+ if (retval <= 0) {
+ pkiDebug("unable to decrypt received data (len=%d)\n", data_len);
+ goto cleanup;
+ }
+ *out_data = buf;
+ *out_data_len = retval;
+
+ cleanup:
+ if (retval == ENOMEM)
+ free(buf);
+
+ return retval;
+}
+
+static krb5_error_code
+create_signature(unsigned char **sig, unsigned int *sig_len,
+ unsigned char *data, unsigned int data_len, EVP_PKEY *pkey)
+{
+ krb5_error_code retval = ENOMEM;
+ EVP_MD_CTX md_ctx;
+
+ if (pkey == NULL)
+ return retval;
+
+ EVP_VerifyInit(&md_ctx, EVP_sha1());
+ EVP_SignUpdate(&md_ctx, data, data_len);
+ *sig_len = EVP_PKEY_size(pkey);
+ if ((*sig = (unsigned char *) malloc((size_t) *sig_len)) == NULL)
+ goto cleanup;
+ EVP_SignFinal(&md_ctx, *sig, sig_len, pkey);
+
+ retval = 0;
+
+ cleanup:
+ EVP_MD_CTX_cleanup(&md_ctx);
+
+ return retval;
+}
+
+/*
+ * Note:
+ * This is not the routine the KDC uses to get its certificate.
+ * This routine is intended to be called by the client
+ * to obtain the KDC's certificate from some local storage
+ * to be sent as a hint in its request to the KDC.
+ */
+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,
+ krb5_principal princ)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ req_cryptoctx->received_cert = NULL;
+ retval = 0;
+ return retval;
+}
+
+static krb5_error_code
+pkinit_get_certs_pkcs12(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ X509 *x = NULL;
+ PKCS12 *p12 = NULL;
+ int ret;
+ FILE *fp;
+ EVP_PKEY *y = NULL;
+
+ if (idopts->cert_filename == NULL) {
+ pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__);
+ goto cleanup;
+ }
+
+ if (idopts->key_filename == NULL) {
+ pkiDebug("%s: failed to get user's private key location\n", __FUNCTION__);
+ goto cleanup;
+ }
+
+ fp = fopen(idopts->cert_filename, "rb");
+ if (fp == NULL) {
+ pkiDebug("Failed to open PKCS12 file '%s', error %d\n",
+ idopts->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",
+ idopts->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, idopts->cert_filename);
+ if (r >= sizeof(prompt_string)) {
+ pkiDebug("Prompt string, '%s %s', is too long!\n",
+ prompt_prefix, idopts->cert_filename);
+ goto cleanup;
+ }
+ kprompt.prompt = prompt_string;
+ kprompt.hidden = 1;
+ kprompt.reply = &rdat;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+
+ /* PROMPTER_INVOCATION */
+ k5int_set_prompt_types(context, &prompt_type);
+ r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+ NULL, NULL, 1, &kprompt);
+ k5int_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;
+ }
+ }
+ id_cryptoctx->creds[0] = malloc(sizeof(struct _pkinit_cred_info));
+ if (id_cryptoctx->creds[0] == NULL)
+ goto cleanup;
+ id_cryptoctx->creds[0]->cert = x;
+#ifndef WITHOUT_PKCS11
+ id_cryptoctx->creds[0]->cert_id = NULL;
+ id_cryptoctx->creds[0]->cert_id_len = 0;
+#endif
+ id_cryptoctx->creds[0]->key = y;
+ id_cryptoctx->creds[1] = NULL;
+
+ retval = 0;
+
+cleanup:
+ if (p12)
+ PKCS12_free(p12);
+ if (retval) {
+ if (x != NULL)
+ X509_free(x);
+ if (y != NULL)
+ EVP_PKEY_free(y);
+ }
+ return retval;
+}
+
+static krb5_error_code
+pkinit_load_fs_cert_and_key(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ char *certname,
+ char *keyname,
+ int cindex)
+{
+ krb5_error_code retval;
+ X509 *x = NULL;
+ EVP_PKEY *y = NULL;
+
+ /* load the certificate */
+ retval = get_cert(certname, &x);
+ if (retval != 0 || x == NULL) {
+ pkiDebug("failed to load user's certificate from '%s'\n", certname);
+ goto cleanup;
+ }
+ 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) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ id_cryptoctx->creds[cindex]->cert = x;
+#ifndef WITHOUT_PKCS11
+ id_cryptoctx->creds[cindex]->cert_id = NULL;
+ id_cryptoctx->creds[cindex]->cert_id_len = 0;
+#endif
+ id_cryptoctx->creds[cindex]->key = y;
+ id_cryptoctx->creds[cindex+1] = NULL;
+
+ retval = 0;
+
+cleanup:
+ if (retval) {
+ if (x != NULL)
+ X509_free(x);
+ if (y != NULL)
+ EVP_PKEY_free(y);
+ }
+ return retval;
+}
+
+static krb5_error_code
+pkinit_get_certs_fs(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ if (idopts->cert_filename == NULL) {
+ pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__);
+ goto cleanup;
+ }
+
+ if (idopts->key_filename == NULL) {
+ pkiDebug("%s: failed to get user's private key location\n",
+ __FUNCTION__);
+ goto cleanup;
+ }
+
+ retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx,
+ idopts->cert_filename,
+ idopts->key_filename, 0);
+cleanup:
+ return retval;
+}
+
+static krb5_error_code
+pkinit_get_certs_dir(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ)
+{
+ krb5_error_code retval = ENOMEM;
+ DIR *d = NULL;
+ struct dirent *dentry = NULL;
+ char certname[1024];
+ char keyname[1024];
+ int i = 0, len;
+ char *dirname, *suf;
+
+ if (idopts->cert_filename == NULL) {
+ pkiDebug("%s: failed to get user's certificate directory location\n",
+ __FUNCTION__);
+ return ENOENT;
+ }
+
+ dirname = idopts->cert_filename;
+ d = opendir(dirname);
+ if (d == NULL)
+ return errno;
+
+ /*
+ * We'll assume that certs are named XXX.crt and the corresponding
+ * key is named XXX.key
+ */
+ while ((i < MAX_CREDS_ALLOWED) && (dentry = readdir(d)) != NULL) {
+ /* Ignore subdirectories and anything starting with a dot */
+#ifdef DT_DIR
+ if (dentry->d_type == DT_DIR)
+ continue;
+#endif
+ if (dentry->d_name[0] == '.')
+ continue;
+ len = strlen(dentry->d_name);
+ if (len < 5)
+ continue;
+ suf = dentry->d_name + (len - 4);
+ if (strncmp(suf, ".crt", 4) != 0)
+ continue;
+
+ /* Checked length */
+ if (strlen(dirname) + strlen(dentry->d_name) + 2 > sizeof(certname)) {
+ pkiDebug("%s: Path too long -- directory '%s' and file '%s'\n",
+ __FUNCTION__, dirname, dentry->d_name);
+ continue;
+ }
+ snprintf(certname, sizeof(certname), "%s/%s", dirname, dentry->d_name);
+ snprintf(keyname, sizeof(keyname), "%s/%s", dirname, dentry->d_name);
+ len = strlen(keyname);
+ keyname[len - 3] = 'k';
+ keyname[len - 2] = 'e';
+ keyname[len - 1] = 'y';
+
+ retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx,
+ certname, keyname, i);
+ if (retval == 0) {
+ pkiDebug("%s: Successfully loaded cert (and key) for %s\n",
+ __FUNCTION__, dentry->d_name);
+ i++;
+ }
+ else
+ continue;
+ }
+
+ if (i == 0) {
+ pkiDebug("%s: No cert/key pairs found in directory '%s'\n",
+ __FUNCTION__, idopts->cert_filename);
+ retval = ENOENT;
+ goto cleanup;
+ }
+
+ retval = 0;
+
+ cleanup:
+ if (d)
+ closedir(d);
+
+ return retval;
+}
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code
+pkinit_get_certs_pkcs11(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ)
+{
+#ifdef PKINIT_USE_MECH_LIST
+ CK_MECHANISM_TYPE_PTR mechp;
+ CK_MECHANISM_INFO info;
+#endif
+ CK_OBJECT_CLASS cls;
+ CK_OBJECT_HANDLE obj;
+ CK_ATTRIBUTE attrs[4];
+ CK_ULONG count;
+ CK_CERTIFICATE_TYPE certtype;
+ CK_BYTE_PTR cert = NULL, cert_id;
+ const unsigned char *cp;
+ int i, r;
+ unsigned int nattrs;
+ X509 *x = NULL;
+
+ /* Copy stuff from idopts -> id_cryptoctx */
+ if (idopts->p11_module_name != NULL) {
+ id_cryptoctx->p11_module_name = strdup(idopts->p11_module_name);
+ if (id_cryptoctx->p11_module_name == NULL)
+ return ENOMEM;
+ }
+ if (idopts->token_label != NULL) {
+ id_cryptoctx->token_label = strdup(idopts->token_label);
+ if (id_cryptoctx->token_label == NULL)
+ return ENOMEM;
+ }
+ if (idopts->cert_label != NULL) {
+ id_cryptoctx->cert_label = strdup(idopts->cert_label);
+ if (id_cryptoctx->cert_label == NULL)
+ return ENOMEM;
+ }
+ /* Convert the ascii cert_id string into a binary blob */
+ if (idopts->cert_id_string != NULL) {
+ BIGNUM *bn = NULL;
+ BN_hex2bn(&bn, idopts->cert_id_string);
+ if (bn == NULL)
+ return ENOMEM;
+ id_cryptoctx->cert_id_len = BN_num_bytes(bn);
+ id_cryptoctx->cert_id = malloc((size_t) id_cryptoctx->cert_id_len);
+ if (id_cryptoctx->cert_id == NULL) {
+ BN_free(bn);
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, id_cryptoctx->cert_id);
+ BN_free(bn);
+ }
+ id_cryptoctx->slotid = idopts->slotid;
+ id_cryptoctx->pkcs11_method = 1;
+
+
+
+ if (pkinit_open_session(context, id_cryptoctx)) {
+ pkiDebug("can't open pkcs11 session\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+#ifndef PKINIT_USE_MECH_LIST
+ /*
+ * We'd like to use CKM_SHA1_RSA_PKCS for signing if it's available, but
+ * many cards seems to be confused about whether they are capable of
+ * this or not. The safe thing seems to be to ignore the mechanism list,
+ * always use CKM_RSA_PKCS and calculate the sha1 digest ourselves.
+ */
+
+ id_cryptoctx->mech = CKM_RSA_PKCS;
+#else
+ if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid, NULL,
+ &count)) != CKR_OK || count <= 0) {
+ pkiDebug("C_GetMechanismList: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ mechp = (CK_MECHANISM_TYPE_PTR) malloc(count * sizeof (CK_MECHANISM_TYPE));
+ if (mechp == NULL)
+ return ENOMEM;
+ if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid,
+ mechp, &count)) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ for (i = 0; i < count; i++) {
+ if ((r = id_cryptoctx->p11->C_GetMechanismInfo(id_cryptoctx->slotid,
+ mechp[i], &info)) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+#ifdef DEBUG_MECHINFO
+ pkiDebug("mech %x flags %x\n", (int) mechp[i], (int) info.flags);
+ if ((info.flags & (CKF_SIGN|CKF_DECRYPT)) == (CKF_SIGN|CKF_DECRYPT))
+ pkiDebug(" this mech is good for sign & decrypt\n");
+#endif
+ if (mechp[i] == CKM_RSA_PKCS) {
+ /* This seems backwards... */
+ id_cryptoctx->mech =
+ (info.flags & CKF_SIGN) ? CKM_SHA1_RSA_PKCS : CKM_RSA_PKCS;
+ }
+ }
+ free(mechp);
+
+ pkiDebug("got %d mechs from card\n", (int) count);
+#endif
+
+ cls = CKO_CERTIFICATE;
+ attrs[0].type = CKA_CLASS;
+ attrs[0].pValue = &cls;
+ attrs[0].ulValueLen = sizeof cls;
+
+ certtype = CKC_X_509;
+ attrs[1].type = CKA_CERTIFICATE_TYPE;
+ attrs[1].pValue = &certtype;
+ attrs[1].ulValueLen = sizeof certtype;
+
+ nattrs = 2;
+
+ /* If a cert id and/or label were given, use them too */
+ if (id_cryptoctx->cert_id_len > 0) {
+ attrs[nattrs].type = CKA_ID;
+ attrs[nattrs].pValue = id_cryptoctx->cert_id;
+ attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len;
+ nattrs++;
+ }
+ if (id_cryptoctx->cert_label != NULL) {
+ attrs[nattrs].type = CKA_LABEL;
+ attrs[nattrs].pValue = id_cryptoctx->cert_label;
+ attrs[nattrs].ulValueLen = strlen(id_cryptoctx->cert_label);
+ nattrs++;
+ }
+
+ r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs);
+ if (r != CKR_OK) {
+ pkiDebug("C_FindObjectsInit: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ for (i = 0; ; i++) {
+ if (i >= MAX_CREDS_ALLOWED)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ /* Look for x.509 cert */
+ if ((r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session,
+ &obj, 1, &count)) != CKR_OK || count <= 0) {
+ id_cryptoctx->creds[i] = NULL;
+ break;
+ }
+
+ /* Get cert and id len */
+ attrs[0].type = CKA_VALUE;
+ attrs[0].pValue = NULL;
+ attrs[0].ulValueLen = 0;
+
+ attrs[1].type = CKA_ID;
+ attrs[1].pValue = NULL;
+ attrs[1].ulValueLen = 0;
+
+ if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session,
+ obj, attrs, 2)) != CKR_OK && r != CKR_BUFFER_TOO_SMALL) {
+ pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ cert = (CK_BYTE_PTR) malloc((size_t) attrs[0].ulValueLen + 1);
+ cert_id = (CK_BYTE_PTR) malloc((size_t) attrs[1].ulValueLen + 1);
+ if (cert == NULL || cert_id == NULL)
+ return ENOMEM;
+
+ /* Read the cert and id off the card */
+
+ attrs[0].type = CKA_VALUE;
+ attrs[0].pValue = cert;
+
+ attrs[1].type = CKA_ID;
+ attrs[1].pValue = cert_id;
+
+ if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session,
+ obj, attrs, 2)) != CKR_OK) {
+ pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ pkiDebug("cert %d size %d id %d idlen %d\n", i,
+ (int) attrs[0].ulValueLen, (int) cert_id[0],
+ (int) attrs[1].ulValueLen);
+
+ cp = (unsigned char *) cert;
+ x = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen);
+ if (x == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ id_cryptoctx->creds[i] = malloc(sizeof(struct _pkinit_cred_info));
+ if (id_cryptoctx->creds[i] == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ id_cryptoctx->creds[i]->cert = x;
+ id_cryptoctx->creds[i]->key = NULL;
+ id_cryptoctx->creds[i]->cert_id = cert_id;
+ id_cryptoctx->creds[i]->cert_id_len = attrs[1].ulValueLen;
+ free(cert);
+ }
+ id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session);
+ if (cert == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ return 0;
+}
+#endif
+
+
+static void
+free_cred_info(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ struct _pkinit_cred_info *cred)
+{
+ if (cred != NULL) {
+ if (cred->cert != NULL)
+ X509_free(cred->cert);
+ if (cred->key != NULL)
+ EVP_PKEY_free(cred->key);
+#ifndef WITHOUT_PKCS11
+ if (cred->cert_id != NULL)
+ free(cred->cert_id);
+#endif
+ free(cred);
+ }
+}
+
+krb5_error_code
+crypto_free_cert_info(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx)
+{
+ int i;
+
+ if (id_cryptoctx == NULL)
+ return EINVAL;
+
+ for (i = 0; i < MAX_CREDS_ALLOWED; i++) {
+ if (id_cryptoctx->creds[i] != NULL) {
+ free_cred_info(context, id_cryptoctx, id_cryptoctx->creds[i]);
+ id_cryptoctx->creds[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code
+crypto_load_certs(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ)
+{
+ krb5_error_code retval;
+
+ switch(idopts->idtype) {
+ case IDTYPE_FILE:
+ retval = pkinit_get_certs_fs(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+ case IDTYPE_DIR:
+ retval = pkinit_get_certs_dir(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+#ifndef WITHOUT_PKCS11
+ case IDTYPE_PKCS11:
+ retval = pkinit_get_certs_pkcs11(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+#endif
+ case IDTYPE_PKCS12:
+ retval = pkinit_get_certs_pkcs12(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+ default:
+ retval = EINVAL;
+ }
+ if (retval)
+ goto cleanup;
+
+cleanup:
+ return retval;
+}
+
+/*
+ * Get number of certificates available after crypto_load_certs()
+ */
+krb5_error_code
+crypto_cert_get_count(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int *cert_count)
+{
+ int count;
+
+ if (id_cryptoctx == NULL || id_cryptoctx->creds[0] == NULL)
+ return EINVAL;
+
+ for (count = 0;
+ count <= MAX_CREDS_ALLOWED && id_cryptoctx->creds[count] != NULL;
+ count++);
+ *cert_count = count;
+ return 0;
+}
+
+
+/*
+ * Begin iteration over the certs loaded in crypto_load_certs()
+ */
+krb5_error_code
+crypto_cert_iteration_begin(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ pkinit_cert_iter_handle *ih_ret)
+{
+ struct _pkinit_cert_iter_data *id;
+
+ if (id_cryptoctx == NULL || ih_ret == NULL)
+ return EINVAL;
+ if (id_cryptoctx->creds[0] == NULL) /* No cred info available */
+ return ENOENT;
+
+ id = calloc(1, sizeof(*id));
+ if (id == NULL)
+ return ENOMEM;
+ id->magic = ITER_MAGIC;
+ id->plgctx = plg_cryptoctx,
+ id->reqctx = req_cryptoctx,
+ id->idctx = id_cryptoctx;
+ id->index = 0;
+ *ih_ret = (pkinit_cert_iter_handle) id;
+ return 0;
+}
+
+/*
+ * End iteration over the certs loaded in crypto_load_certs()
+ */
+krb5_error_code
+crypto_cert_iteration_end(krb5_context context,
+ pkinit_cert_iter_handle ih)
+{
+ struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih;
+
+ if (id == NULL || id->magic != ITER_MAGIC)
+ return EINVAL;
+ free(ih);
+ return 0;
+}
+
+/*
+ * Get next certificate handle
+ */
+krb5_error_code
+crypto_cert_iteration_next(krb5_context context,
+ pkinit_cert_iter_handle ih,
+ pkinit_cert_handle *ch_ret)
+{
+ struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih;
+ struct _pkinit_cert_data *cd;
+ pkinit_identity_crypto_context id_cryptoctx;
+
+ if (id == NULL || id->magic != ITER_MAGIC)
+ return EINVAL;
+
+ if (ch_ret == NULL)
+ return EINVAL;
+
+ id_cryptoctx = id->idctx;
+ if (id_cryptoctx == NULL)
+ return EINVAL;
+
+ if (id_cryptoctx->creds[id->index] == NULL)
+ return PKINIT_ITER_NO_MORE;
+
+ cd = calloc(1, sizeof(*cd));
+ if (cd == NULL)
+ return ENOMEM;
+
+ cd->magic = CERT_MAGIC;
+ cd->plgctx = id->plgctx;
+ cd->reqctx = id->reqctx;
+ cd->idctx = id->idctx;
+ cd->index = id->index;
+ cd->cred = id_cryptoctx->creds[id->index++];
+ *ch_ret = (pkinit_cert_handle)cd;
+ return 0;
+}
+
+/*
+ * Release cert handle
+ */
+krb5_error_code
+crypto_cert_release(krb5_context context,
+ pkinit_cert_handle ch)
+{
+ struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch;
+ if (cd == NULL || cd->magic != CERT_MAGIC)
+ return EINVAL;
+ free(cd);
+ return 0;
+}
+
+/*
+ * Get certificate Key Usage and Extended Key Usage
+ */
+static krb5_error_code
+crypto_retieve_X509_key_usage(krb5_context context,
+ pkinit_plg_crypto_context plgcctx,
+ pkinit_req_crypto_context reqcctx,
+ X509 *x,
+ unsigned int *ret_ku_bits,
+ unsigned int *ret_eku_bits)
+{
+ krb5_error_code retval = 0;
+ int i;
+ unsigned int eku_bits = 0, ku_bits = 0;
+ ASN1_BIT_STRING *usage = NULL;
+
+ if (ret_ku_bits == NULL && ret_eku_bits == NULL)
+ return EINVAL;
+
+ if (ret_eku_bits)
+ *ret_eku_bits = 0;
+ else {
+ pkiDebug("%s: EKUs not requested, not checking\n", __FUNCTION__);
+ goto check_kus;
+ }
+
+ /* Start with Extended Key usage */
+ i = X509_get_ext_by_NID(x, NID_ext_key_usage, -1);
+ if (i >= 0) {
+ EXTENDED_KEY_USAGE *eku;
+
+ eku = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL);
+ if (eku) {
+ for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
+ ASN1_OBJECT *certoid;
+ certoid = sk_ASN1_OBJECT_value(eku, i);
+ if ((OBJ_cmp(certoid, plgcctx->id_pkinit_KPClientAuth)) == 0)
+ eku_bits |= PKINIT_EKU_PKINIT;
+ else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_ms_smartcard_login))) == 0)
+ eku_bits |= PKINIT_EKU_MSSCLOGIN;
+ else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_client_auth))) == 0)
+ eku_bits |= PKINIT_EKU_CLIENTAUTH;
+ else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_email_protect))) == 0)
+ eku_bits |= PKINIT_EKU_EMAILPROTECTION;
+ }
+ EXTENDED_KEY_USAGE_free(eku);
+ }
+ }
+ pkiDebug("%s: returning eku 0x%08x\n", __FUNCTION__, eku_bits);
+ *ret_eku_bits = eku_bits;
+
+check_kus:
+ /* Now the Key Usage bits */
+ if (ret_ku_bits)
+ *ret_ku_bits = 0;
+ else {
+ pkiDebug("%s: KUs not requested, not checking\n", __FUNCTION__);
+ goto out;
+ }
+
+ /* Make sure usage exists before checking bits */
+ usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL);
+ if (usage) {
+ if (!ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE))
+ ku_bits |= PKINIT_KU_DIGITALSIGNATURE;
+ if (!ku_reject(x, X509v3_KU_KEY_ENCIPHERMENT))
+ ku_bits |= PKINIT_KU_KEYENCIPHERMENT;
+ ASN1_BIT_STRING_free(usage);
+ }
+
+ pkiDebug("%s: returning ku 0x%08x\n", __FUNCTION__, ku_bits);
+ *ret_ku_bits = ku_bits;
+ retval = 0;
+out:
+ return retval;
+}
+
+/*
+ * Return a string format of an X509_NAME in buf where
+ * size is an in/out parameter. On input it is the size
+ * of the buffer, and on output it is the actual length
+ * of the name.
+ * If buf is NULL, returns the length req'd to hold name
+ */
+static char *
+X509_NAME_oneline_ex(X509_NAME * a,
+ char *buf,
+ unsigned int *size,
+ unsigned long flag)
+{
+ BIO *out = NULL;
+
+ out = BIO_new(BIO_s_mem ());
+ if (X509_NAME_print_ex(out, a, 0, flag) > 0) {
+ if (buf != NULL && *size > (int) BIO_number_written(out)) {
+ memset(buf, 0, *size);
+ BIO_read(out, buf, (int) BIO_number_written(out));
+ }
+ else {
+ *size = BIO_number_written(out);
+ }
+ }
+ BIO_free(out);
+ return (buf);
+}
+
+/*
+ * Get certificate information
+ */
+krb5_error_code
+crypto_cert_get_matching_data(krb5_context context,
+ pkinit_cert_handle ch,
+ pkinit_cert_matching_data **ret_md)
+{
+ krb5_error_code retval;
+ pkinit_cert_matching_data *md;
+ krb5_principal *pkinit_sans =NULL, *upn_sans = NULL;
+ struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch;
+ int i, j;
+ char buf[DN_BUF_LEN];
+ unsigned int bufsize = sizeof(buf);
+
+ if (cd == NULL || cd->magic != CERT_MAGIC)
+ return EINVAL;
+ if (ret_md == NULL)
+ return EINVAL;
+
+ md = calloc(1, sizeof(*md));
+ if (md == NULL)
+ return ENOMEM;
+
+ md->ch = ch;
+
+ /* get the subject name (in rfc2253 format) */
+ X509_NAME_oneline_ex(X509_get_subject_name(cd->cred->cert),
+ buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS);
+ md->subject_dn = strdup(buf);
+ if (md->subject_dn == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ /* get the issuer name (in rfc2253 format) */
+ X509_NAME_oneline_ex(X509_get_issuer_name(cd->cred->cert),
+ buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS);
+ md->issuer_dn = strdup(buf);
+ if (md->issuer_dn == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ /* get the san data */
+ retval = crypto_retrieve_X509_sans(context, cd->plgctx, cd->reqctx,
+ cd->cred->cert, &pkinit_sans,
+ &upn_sans, NULL);
+ if (retval)
+ goto cleanup;
+
+ j = 0;
+ if (pkinit_sans != NULL) {
+ for (i = 0; pkinit_sans[i] != NULL; i++)
+ j++;
+ }
+ if (upn_sans != NULL) {
+ for (i = 0; upn_sans[i] != NULL; i++)
+ j++;
+ }
+ if (j != 0) {
+ md->sans = calloc((size_t)j+1, sizeof(*md->sans));
+ if (md->sans == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ j = 0;
+ if (pkinit_sans != NULL) {
+ for (i = 0; pkinit_sans[i] != NULL; i++)
+ md->sans[j++] = pkinit_sans[i];
+ free(pkinit_sans);
+ }
+ if (upn_sans != NULL) {
+ for (i = 0; upn_sans[i] != NULL; i++)
+ md->sans[j++] = upn_sans[i];
+ free(upn_sans);
+ }
+ md->sans[j] = NULL;
+ } else
+ md->sans = NULL;
+
+ /* get the KU and EKU data */
+
+ retval = crypto_retieve_X509_key_usage(context, cd->plgctx, cd->reqctx,
+ cd->cred->cert,
+ &md->ku_bits, &md->eku_bits);
+ if (retval)
+ goto cleanup;
+
+ *ret_md = md;
+ retval = 0;
+cleanup:
+ if (retval) {
+ if (md)
+ crypto_cert_free_matching_data(context, md);
+ }
+ return retval;
+}
+
+/*
+ * Free certificate information
+ */
+krb5_error_code
+crypto_cert_free_matching_data(krb5_context context,
+ pkinit_cert_matching_data *md)
+{
+ krb5_principal p;
+ int i;
+
+ if (md == NULL)
+ return EINVAL;
+ if (md->subject_dn)
+ free(md->subject_dn);
+ if (md->issuer_dn)
+ free(md->issuer_dn);
+ if (md->sans) {
+ for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i])
+ krb5_free_principal(context, p);
+ free(md->sans);
+ }
+ free(md);
+ return 0;
+}
+
+/*
+ * Make this matching certificate "the chosen one"
+ */
+krb5_error_code
+crypto_cert_select(krb5_context context,
+ pkinit_cert_matching_data *md)
+{
+ struct _pkinit_cert_data *cd;
+ if (md == NULL)
+ return EINVAL;
+
+ cd = (struct _pkinit_cert_data *)md->ch;
+ if (cd == NULL || cd->magic != CERT_MAGIC)
+ return EINVAL;
+
+ /* copy the selected cert into our id_cryptoctx */
+ if (cd->idctx->my_certs != NULL) {
+ 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);
+ cd->idctx->creds[cd->index]->cert = NULL; /* Don't free it twice */
+ cd->idctx->cert_index = 0;
+
+ if (cd->idctx->pkcs11_method != 1) {
+ cd->idctx->my_key = cd->cred->key;
+ cd->idctx->creds[cd->index]->key = NULL; /* Don't free it twice */
+ }
+#ifndef WITHOUT_PKCS11
+ else {
+ cd->idctx->cert_id = cd->cred->cert_id;
+ cd->idctx->creds[cd->index]->cert_id = NULL; /* Don't free it twice */
+ cd->idctx->cert_id_len = cd->cred->cert_id_len;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Choose the default certificate as "the chosen one"
+ */
+krb5_error_code
+crypto_cert_select_default(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ 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) {
+ 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);
+ id_cryptoctx->creds[0]->cert = NULL; /* Don't free it twice */
+ id_cryptoctx->cert_index = 0;
+
+ if (id_cryptoctx->pkcs11_method != 1) {
+ id_cryptoctx->my_key = id_cryptoctx->creds[0]->key;
+ id_cryptoctx->creds[0]->key = NULL; /* Don't free it twice */
+ }
+#ifndef WITHOUT_PKCS11
+ else {
+ id_cryptoctx->cert_id = id_cryptoctx->creds[0]->cert_id;
+ id_cryptoctx->creds[0]->cert_id = NULL; /* Don't free it twice */
+ id_cryptoctx->cert_id_len = id_cryptoctx->creds[0]->cert_id_len;
+ }
+#endif
+ retval = 0;
+errout:
+ return retval;
+}
+
+
+
+static krb5_error_code
+load_cas_and_crls(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int catype,
+ char *filename)
+{
+ STACK_OF(X509_INFO) *sk = NULL;
+ STACK_OF(X509) *ca_certs = NULL;
+ STACK_OF(X509_CRL) *ca_crls = NULL;
+ BIO *in = NULL;
+ krb5_error_code retval = ENOMEM;
+ int i = 0;
+
+ /* If there isn't already a stack in the context,
+ * create a temporary one now */
+ switch(catype) {
+ case CATYPE_ANCHORS:
+ if (id_cryptoctx->trustedCAs != NULL)
+ ca_certs = id_cryptoctx->trustedCAs;
+ else {
+ ca_certs = sk_X509_new_null();
+ if (ca_certs == NULL)
+ return ENOMEM;
+ }
+ break;
+ case CATYPE_INTERMEDIATES:
+ if (id_cryptoctx->intermediateCAs != NULL)
+ ca_certs = id_cryptoctx->intermediateCAs;
+ else {
+ ca_certs = sk_X509_new_null();
+ if (ca_certs == NULL)
+ return ENOMEM;
+ }
+ break;
+ case CATYPE_CRLS:
+ if (id_cryptoctx->revoked != NULL)
+ ca_crls = id_cryptoctx->revoked;
+ else {
+ ca_crls = sk_X509_CRL_new_null();
+ if (ca_crls == NULL)
+ return ENOMEM;
+ }
+ break;
+ default:
+ return ENOTSUP;
+ }
+
+ if (!(in = BIO_new_file(filename, "r"))) {
+ retval = errno;
+ pkiDebug("%s: error opening file '%s': %s\n", __FUNCTION__,
+ filename, error_message(errno));
+ goto cleanup;
+ }
+
+ /* This loads from a file, a stack of x509/crl/pkey sets */
+ if ((sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL)) == NULL) {
+ pkiDebug("%s: error reading file '%s'\n", __FUNCTION__, filename);
+ retval = EIO;
+ goto cleanup;
+ }
+
+ /* scan over the stack created from loading the file contents,
+ * weed out duplicates, and push new ones onto the return stack
+ */
+ for (i = 0; i < sk_X509_INFO_num(sk); i++) {
+ X509_INFO *xi = sk_X509_INFO_value(sk, i);
+ if (xi != NULL && xi->x509 != NULL && catype != CATYPE_CRLS) {
+ int j = 0, size = sk_X509_num(ca_certs), flag = 0;
+
+ if (!size) {
+ sk_X509_push(ca_certs, xi->x509);
+ xi->x509 = NULL;
+ continue;
+ }
+ for (j = 0; j < size; j++) {
+ X509 *x = sk_X509_value(ca_certs, j);
+ flag = X509_cmp(x, xi->x509);
+ if (flag == 0)
+ break;
+ else
+ continue;
+ }
+ if (flag != 0) {
+ sk_X509_push(ca_certs, X509_dup(xi->x509));
+ }
+ } else if (xi != NULL && xi->crl != NULL && catype == CATYPE_CRLS) {
+ int j = 0, size = sk_X509_CRL_num(ca_crls), flag = 0;
+ if (!size) {
+ sk_X509_CRL_push(ca_crls, xi->crl);
+ xi->crl = NULL;
+ continue;
+ }
+ for (j = 0; j < size; j++) {
+ X509_CRL *x = sk_X509_CRL_value(ca_crls, j);
+ flag = X509_CRL_cmp(x, xi->crl);
+ if (flag == 0)
+ break;
+ else
+ continue;
+ }
+ if (flag != 0) {
+ sk_X509_push(ca_crls, X509_CRL_dup(xi->crl));
+ }
+ }
+ }
+
+ /* If we added something and there wasn't a stack in the
+ * context before, add the temporary stack to the context.
+ */
+ switch(catype) {
+ case CATYPE_ANCHORS:
+ if (sk_X509_num(ca_certs) == 0) {
+ pkiDebug("no anchors in file, %s\n", filename);
+ if (id_cryptoctx->trustedCAs == NULL)
+ sk_X509_free(ca_certs);
+ } else {
+ if (id_cryptoctx->trustedCAs == NULL)
+ id_cryptoctx->trustedCAs = ca_certs;
+ }
+ break;
+ case CATYPE_INTERMEDIATES:
+ if (sk_X509_num(ca_certs) == 0) {
+ pkiDebug("no intermediates in file, %s\n", filename);
+ if (id_cryptoctx->intermediateCAs == NULL)
+ sk_X509_free(ca_certs);
+ } else {
+ if (id_cryptoctx->intermediateCAs == NULL)
+ id_cryptoctx->intermediateCAs = ca_certs;
+ }
+ break;
+ case CATYPE_CRLS:
+ if (sk_X509_num(ca_crls) == 0) {
+ pkiDebug("no crls in file, %s\n", filename);
+ if (id_cryptoctx->revoked == NULL)
+ sk_X509_CRL_free(ca_crls);
+ } else {
+ if (id_cryptoctx->revoked == NULL)
+ id_cryptoctx->revoked = ca_crls;
+ }
+ break;
+ default:
+ /* Should have been caught above! */
+ retval = EINVAL;
+ goto cleanup;
+ break;
+ }
+
+ retval = 0;
+
+ cleanup:
+ if (in != NULL)
+ BIO_free(in);
+ if (sk != NULL)
+ sk_X509_INFO_pop_free(sk, X509_INFO_free);
+
+ return retval;
+}
+
+static krb5_error_code
+load_cas_and_crls_dir(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int catype,
+ char *dirname)
+{
+ krb5_error_code retval = EINVAL;
+ DIR *d = NULL;
+ struct dirent *dentry = NULL;
+ char filename[1024];
+
+ if (dirname == NULL)
+ return EINVAL;
+
+ d = opendir(dirname);
+ if (d == NULL)
+ return ENOENT;
+
+ while ((dentry = readdir(d))) {
+ if (strlen(dirname) + strlen(dentry->d_name) + 2 > sizeof(filename)) {
+ pkiDebug("%s: Path too long -- directory '%s' and file '%s'\n",
+ __FUNCTION__, dirname, dentry->d_name);
+ goto cleanup;
+ }
+ /* Ignore subdirectories and anything starting with a dot */
+#ifdef DT_DIR
+ if (dentry->d_type == DT_DIR)
+ continue;
+#endif
+ if (dentry->d_name[0] == '.')
+ continue;
+ snprintf(filename, sizeof(filename), "%s/%s", dirname, dentry->d_name);
+
+ retval = load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, catype, filename);
+ if (retval)
+ goto cleanup;
+ }
+
+ retval = 0;
+
+ cleanup:
+ if (d != NULL)
+ closedir(d);
+
+ return retval;
+}
+
+krb5_error_code
+crypto_load_cas_and_crls(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int idtype,
+ int catype,
+ char *id)
+{
+ 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,
+ id_cryptoctx, catype, id);
+ break;
+ case IDTYPE_DIR:
+ return load_cas_and_crls_dir(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, catype, id);
+ break;
+ default:
+ return ENOTSUP;
+ break;
+ }
+}
+
+static krb5_error_code
+create_identifiers_from_stack(STACK_OF(X509) *sk,
+ krb5_external_principal_identifier *** ids)
+{
+ krb5_error_code retval = ENOMEM;
+ int i = 0, sk_size = sk_X509_num(sk);
+ krb5_external_principal_identifier **krb5_cas = NULL;
+ X509 *x = NULL;
+ X509_NAME *xn = NULL;
+ unsigned char *p = NULL;
+ int len = 0;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ char buf[DN_BUF_LEN];
+
+ *ids = NULL;
+
+ krb5_cas =
+ malloc((sk_size + 1) * sizeof(krb5_external_principal_identifier *));
+ if (krb5_cas == NULL)
+ return ENOMEM;
+ krb5_cas[sk_size] = NULL;
+
+ for (i = 0; i < sk_size; i++) {
+ krb5_cas[i] = (krb5_external_principal_identifier *)malloc(sizeof(krb5_external_principal_identifier));
+
+ x = sk_X509_value(sk, i);
+
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
+ pkiDebug("#%d cert= %s\n", i, buf);
+
+ /* fill-in subjectName */
+ krb5_cas[i]->subjectName.magic = 0;
+ krb5_cas[i]->subjectName.length = 0;
+ krb5_cas[i]->subjectName.data = NULL;
+
+ xn = X509_get_subject_name(x);
+ len = i2d_X509_NAME(xn, NULL);
+ if ((p = krb5_cas[i]->subjectName.data = (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_X509_NAME(xn, &p);
+ krb5_cas[i]->subjectName.length = len;
+
+ /* fill-in issuerAndSerialNumber */
+ krb5_cas[i]->issuerAndSerialNumber.length = 0;
+ krb5_cas[i]->issuerAndSerialNumber.magic = 0;
+ krb5_cas[i]->issuerAndSerialNumber.data = NULL;
+
+#ifdef LONGHORN_BETA_COMPAT
+if (longhorn == 0) { /* XXX Longhorn doesn't like this */
+#endif
+ is = PKCS7_ISSUER_AND_SERIAL_new();
+ X509_NAME_set(&is->issuer, X509_get_issuer_name(x));
+ M_ASN1_INTEGER_free(is->serial);
+ is->serial = M_ASN1_INTEGER_dup(X509_get_serialNumber(x));
+ len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);
+ if ((p = krb5_cas[i]->issuerAndSerialNumber.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
+ krb5_cas[i]->issuerAndSerialNumber.length = len;
+#ifdef LONGHORN_BETA_COMPAT
+}
+#endif
+
+ /* fill-in subjectKeyIdentifier */
+ krb5_cas[i]->subjectKeyIdentifier.length = 0;
+ krb5_cas[i]->subjectKeyIdentifier.magic = 0;
+ krb5_cas[i]->subjectKeyIdentifier.data = NULL;
+
+
+#ifdef LONGHORN_BETA_COMPAT
+if (longhorn == 0) { /* XXX Longhorn doesn't like this */
+#endif
+ if (X509_get_ext_by_NID(x, NID_subject_key_identifier, -1) >= 0) {
+ ASN1_OCTET_STRING *ikeyid = NULL;
+
+ if ((ikeyid = X509_get_ext_d2i(x, NID_subject_key_identifier, NULL,
+ NULL))) {
+ len = i2d_ASN1_OCTET_STRING(ikeyid, NULL);
+ if ((p = krb5_cas[i]->subjectKeyIdentifier.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_ASN1_OCTET_STRING(ikeyid, &p);
+ krb5_cas[i]->subjectKeyIdentifier.length = len;
+ }
+ if (ikeyid != NULL)
+ ASN1_OCTET_STRING_free(ikeyid);
+ }
+#ifdef LONGHORN_BETA_COMPAT
+}
+#endif
+ if (is != NULL) {
+ if (is->issuer != NULL)
+ X509_NAME_free(is->issuer);
+ if (is->serial != NULL)
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+ }
+ }
+
+ *ids = krb5_cas;
+
+ retval = 0;
+ cleanup:
+ if (retval)
+ free_krb5_external_principal_identifier(&krb5_cas);
+
+ return retval;
+}
+
+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)
+{
+
+ krb5_error_code retval = ENOMEM;
+ STACK_OF(X509) *sk = NULL;
+
+ *ids = NULL;
+ if (req_cryptoctx->received_cert == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ sk = sk_X509_new_null();
+ if (sk == NULL)
+ goto cleanup;
+ sk_X509_push(sk, req_cryptoctx->received_cert);
+
+ retval = create_identifiers_from_stack(sk, ids);
+
+ sk_X509_free(sk);
+cleanup:
+
+ return retval;
+}
+
+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) {
+ free(loids);
+ goto cleanup;
+ }
+ retval = pkinit_copy_krb5_octet_data(&loids[0]->algorithm, &des3oid);
+ if (retval) {
+ free(loids[0]);
+ free(loids);
+ goto cleanup;
+ }
+ 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,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_external_principal_identifier *** ids)
+{
+
+ krb5_error_code retval = ENOMEM;
+ STACK_OF(X509) *sk = id_cryptoctx->trustedCAs;
+
+ *ids = NULL;
+ if (id_cryptoctx->trustedCAs == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ retval = create_identifiers_from_stack(sk, ids);
+
+ return retval;
+}
+
+krb5_error_code
+create_krb5_trustedCas(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int flag,
+ krb5_trusted_ca *** ids)
+{
+ krb5_error_code retval = ENOMEM;
+ 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;
+ char buf[DN_BUF_LEN];
+ X509_NAME *xn = NULL;
+ unsigned char *p = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+
+ *ids = NULL;
+ if (id_cryptoctx->trustedCAs == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ krb5_cas = malloc((sk_size + 1) * sizeof(krb5_trusted_ca *));
+ if (krb5_cas == NULL)
+ return ENOMEM;
+ krb5_cas[sk_size] = NULL;
+
+ for (i = 0; i < sk_size; i++) {
+ krb5_cas[i] = (krb5_trusted_ca *)malloc(sizeof(krb5_trusted_ca));
+ if (krb5_cas[i] == NULL)
+ goto cleanup;
+ x = sk_X509_value(sk, i);
+
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
+ pkiDebug("#%d cert= %s\n", i, buf);
+
+ switch (flag) {
+ case choice_trusted_cas_principalName:
+ krb5_cas[i]->choice = choice_trusted_cas_principalName;
+ break;
+ case choice_trusted_cas_caName:
+ krb5_cas[i]->choice = choice_trusted_cas_caName;
+ krb5_cas[i]->u.caName.data = NULL;
+ krb5_cas[i]->u.caName.length = 0;
+ xn = X509_get_subject_name(x);
+ len = i2d_X509_NAME(xn, NULL);
+ if ((p = krb5_cas[i]->u.caName.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_X509_NAME(xn, &p);
+ krb5_cas[i]->u.caName.length = len;
+ break;
+ case choice_trusted_cas_issuerAndSerial:
+ krb5_cas[i]->choice = choice_trusted_cas_issuerAndSerial;
+ krb5_cas[i]->u.issuerAndSerial.data = NULL;
+ krb5_cas[i]->u.issuerAndSerial.length = 0;
+ is = PKCS7_ISSUER_AND_SERIAL_new();
+ X509_NAME_set(&is->issuer, X509_get_issuer_name(x));
+ M_ASN1_INTEGER_free(is->serial);
+ is->serial = M_ASN1_INTEGER_dup(X509_get_serialNumber(x));
+ len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);
+ if ((p = krb5_cas[i]->u.issuerAndSerial.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
+ krb5_cas[i]->u.issuerAndSerial.length = len;
+ if (is != NULL) {
+ if (is->issuer != NULL)
+ X509_NAME_free(is->issuer);
+ if (is->serial != NULL)
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+ }
+ break;
+ default: break;
+ }
+ }
+ retval = 0;
+ *ids = krb5_cas;
+cleanup:
+ if (retval)
+ free_krb5_trusted_ca(&krb5_cas);
+
+ return retval;
+}
+
+krb5_error_code
+create_issuerAndSerial(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char **out,
+ unsigned int *out_len)
+{
+ unsigned char *p = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ int len = 0;
+ krb5_error_code retval = ENOMEM;
+ X509 *cert = req_cryptoctx->received_cert;
+
+ *out = NULL;
+ *out_len = 0;
+ if (req_cryptoctx->received_cert == NULL)
+ return 0;
+
+ is = PKCS7_ISSUER_AND_SERIAL_new();
+ X509_NAME_set(&is->issuer, X509_get_issuer_name(cert));
+ M_ASN1_INTEGER_free(is->serial);
+ is->serial = M_ASN1_INTEGER_dup(X509_get_serialNumber(cert));
+ len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);
+ if ((p = *out = (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
+ *out_len = len;
+ retval = 0;
+
+cleanup:
+ X509_NAME_free(is->issuer);
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+
+ return retval;
+}
+
+static int
+pkcs7_decrypt(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7,
+ BIO *data)
+{
+ BIO *tmpmem = NULL;
+ int retval = 0, i = 0;
+ char buf[4096];
+
+ if(p7 == NULL)
+ return 0;
+
+ if(!PKCS7_type_is_enveloped(p7)) {
+ pkiDebug("wrong pkcs7 content type\n");
+ return 0;
+ }
+
+ if(!(tmpmem = pkcs7_dataDecode(context, id_cryptoctx, p7))) {
+ pkiDebug("unable to decrypt pkcs7 object\n");
+ return 0;
+ }
+
+ for(;;) {
+ i = BIO_read(tmpmem, buf, sizeof(buf));
+ if (i <= 0) break;
+ BIO_write(data, buf, i);
+ BIO_free_all(tmpmem);
+ return 1;
+ }
+ return retval;
+}
+
+krb5_error_code
+pkinit_process_td_trusted_certifiers(
+ krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_external_principal_identifier **krb5_trusted_certifiers,
+ int td_type)
+{
+ krb5_error_code retval = ENOMEM;
+ STACK_OF(X509_NAME) *sk_xn = NULL;
+ X509_NAME *xn = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ ASN1_OCTET_STRING *id = NULL;
+ const unsigned char *p = NULL;
+ char buf[DN_BUF_LEN];
+ int i = 0;
+
+ if (td_type == TD_TRUSTED_CERTIFIERS)
+ pkiDebug("received trusted certifiers\n");
+ else
+ pkiDebug("received invalid certificate\n");
+
+ sk_xn = sk_X509_NAME_new_null();
+ while(krb5_trusted_certifiers[i] != NULL) {
+ if (krb5_trusted_certifiers[i]->subjectName.data != NULL) {
+ p = krb5_trusted_certifiers[i]->subjectName.data;
+ xn = d2i_X509_NAME(NULL, &p,
+ (int)krb5_trusted_certifiers[i]->subjectName.length);
+ if (xn == NULL)
+ goto cleanup;
+ 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
+ pkiDebug("#%d cert = %s is invalid\n", i, buf);
+ sk_X509_NAME_push(sk_xn, xn);
+ }
+
+ if (krb5_trusted_certifiers[i]->issuerAndSerialNumber.data != NULL) {
+ p = krb5_trusted_certifiers[i]->issuerAndSerialNumber.data;
+ is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p,
+ (int)krb5_trusted_certifiers[i]->issuerAndSerialNumber.length);
+ if (is == NULL)
+ goto cleanup;
+ 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));
+ else
+ pkiDebug("#%d issuer = %s serial = %ld is invalid\n", i, buf,
+ ASN1_INTEGER_get(is->serial));
+ PKCS7_ISSUER_AND_SERIAL_free(is);
+ }
+
+ if (krb5_trusted_certifiers[i]->subjectKeyIdentifier.data != NULL) {
+ p = krb5_trusted_certifiers[i]->subjectKeyIdentifier.data;
+ id = d2i_ASN1_OCTET_STRING(NULL, &p,
+ (int)krb5_trusted_certifiers[i]->subjectKeyIdentifier.length);
+ if (id == NULL)
+ goto cleanup;
+ /* XXX */
+ ASN1_OCTET_STRING_free(id);
+ }
+ i++;
+ }
+ /* XXX Since we not doing anything with received trusted certifiers
+ * return an error. this is the place where we can pick a different
+ * client certificate based on the information in td_trusted_certifiers
+ */
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+cleanup:
+ if (sk_xn != NULL)
+ sk_X509_NAME_pop_free(sk_xn, X509_NAME_free);
+
+ return retval;
+}
+
+static BIO *
+pkcs7_dataDecode(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7)
+{
+ int i = 0;
+ unsigned int jj = 0, tmp_len = 0;
+ BIO *out=NULL,*etmp=NULL,*bio=NULL;
+ unsigned char *tmp=NULL;
+ ASN1_OCTET_STRING *data_body=NULL;
+ const EVP_CIPHER *evp_cipher=NULL;
+ EVP_CIPHER_CTX *evp_ctx=NULL;
+ X509_ALGOR *enc_alg=NULL;
+ STACK_OF(PKCS7_RECIP_INFO) *rsk=NULL;
+ X509_ALGOR *xalg=NULL;
+ PKCS7_RECIP_INFO *ri=NULL;
+ X509 *cert = sk_X509_value(id_cryptoctx->my_certs,
+ id_cryptoctx->cert_index);
+
+ p7->state=PKCS7_S_HEADER;
+
+ rsk=p7->d.enveloped->recipientinfo;
+ enc_alg=p7->d.enveloped->enc_data->algorithm;
+ data_body=p7->d.enveloped->enc_data->enc_data;
+ evp_cipher=EVP_get_cipherbyobj(enc_alg->algorithm);
+ if (evp_cipher == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,PKCS7_R_UNSUPPORTED_CIPHER_TYPE);
+ goto cleanup;
+ }
+ xalg=p7->d.enveloped->enc_data->algorithm;
+
+ if ((etmp=BIO_new(BIO_f_cipher())) == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,ERR_R_BIO_LIB);
+ goto cleanup;
+ }
+
+ /* It was encrypted, we need to decrypt the secret key
+ * with the private key */
+
+ /* Find the recipientInfo which matches the passed certificate
+ * (if any)
+ */
+
+ if (cert) {
+ for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++) {
+ int tmp_ret = 0;
+ ri=sk_PKCS7_RECIP_INFO_value(rsk,i);
+ tmp_ret = X509_NAME_cmp(ri->issuer_and_serial->issuer,
+ cert->cert_info->issuer);
+ if (!tmp_ret) {
+ tmp_ret = M_ASN1_INTEGER_cmp(cert->cert_info->serialNumber,
+ ri->issuer_and_serial->serial);
+ if (!tmp_ret)
+ break;
+ }
+ ri=NULL;
+ }
+ if (ri == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+ PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE);
+ goto cleanup;
+ }
+
+ }
+
+ /* 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++) {
+ ri=sk_PKCS7_RECIP_INFO_value(rsk,i);
+ jj = pkinit_decode_data(context, id_cryptoctx,
+ M_ASN1_STRING_data(ri->enc_key),
+ (unsigned int) M_ASN1_STRING_length(ri->enc_key),
+ &tmp, &tmp_len);
+ if (jj) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE, ERR_R_EVP_LIB);
+ goto cleanup;
+ }
+
+ if (!jj && tmp_len > 0) {
+ jj = tmp_len;
+ break;
+ }
+
+ ERR_clear_error();
+ ri = NULL;
+ }
+
+ if (ri == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE, PKCS7_R_NO_RECIPIENT_MATCHES_KEY);
+ goto cleanup;
+ }
+ }
+ else {
+ jj = pkinit_decode_data(context, id_cryptoctx,
+ M_ASN1_STRING_data(ri->enc_key),
+ (unsigned int) M_ASN1_STRING_length(ri->enc_key),
+ &tmp, &tmp_len);
+ if (jj || tmp_len <= 0) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE, ERR_R_EVP_LIB);
+ goto cleanup;
+ }
+ jj = tmp_len;
+ }
+
+ evp_ctx=NULL;
+ BIO_get_cipher_ctx(etmp,&evp_ctx);
+ if (EVP_CipherInit_ex(evp_ctx,evp_cipher,NULL,NULL,NULL,0) <= 0)
+ goto cleanup;
+ if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0)
+ 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.
+ */
+ if(!EVP_CIPHER_CTX_set_key_length(evp_ctx, (int)jj)) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+ PKCS7_R_DECRYPTED_KEY_IS_WRONG_LENGTH);
+ goto cleanup;
+ }
+ }
+ if (EVP_CipherInit_ex(evp_ctx,NULL,NULL,tmp,NULL,0) <= 0)
+ goto cleanup;
+
+ OPENSSL_cleanse(tmp,jj);
+
+ if (out == NULL)
+ out=etmp;
+ else
+ BIO_push(out,etmp);
+ etmp=NULL;
+
+ if (data_body->length > 0)
+ bio = BIO_new_mem_buf(data_body->data, data_body->length);
+ else {
+ bio=BIO_new(BIO_s_mem());
+ BIO_set_mem_eof_return(bio,0);
+ }
+ BIO_push(out,bio);
+ bio=NULL;
+
+ if (0) {
+cleanup:
+ if (out != NULL) BIO_free_all(out);
+ if (etmp != NULL) BIO_free_all(etmp);
+ if (bio != NULL) BIO_free_all(bio);
+ out=NULL;
+ }
+
+ if (tmp != NULL)
+ free(tmp);
+
+ return(out);
+}
+
+static krb5_error_code
+der_decode_data(unsigned char *data, long data_len,
+ unsigned char **out, long *out_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ ASN1_OCTET_STRING *s = NULL;
+ const unsigned char *p = data;
+
+ if ((s = d2i_ASN1_BIT_STRING(NULL, &p, data_len)) == NULL)
+ goto cleanup;
+ *out_len = s->length;
+ if ((*out = (unsigned char *) malloc((size_t) *out_len + 1)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(*out, s->data, (size_t) s->length);
+ (*out)[s->length] = '\0';
+
+ retval = 0;
+ cleanup:
+ if (s != NULL)
+ ASN1_OCTET_STRING_free(s);
+
+ return retval;
+}
+
+
+#ifdef DEBUG_DH
+static void
+print_dh(DH * dh, char *msg)
+{
+ BIO *bio_err = NULL;
+
+ bio_err = BIO_new(BIO_s_file());
+ BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+
+ if (msg)
+ BIO_puts(bio_err, (const char *)msg);
+ if (dh)
+ DHparams_print(bio_err, dh);
+
+ BN_print(bio_err, dh->q);
+ BIO_puts(bio_err, (const char *)"\n");
+ BIO_free(bio_err);
+
+}
+
+static void
+print_pubkey(BIGNUM * key, char *msg)
+{
+ BIO *bio_err = NULL;
+
+ bio_err = BIO_new(BIO_s_file());
+ BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+
+ if (msg)
+ BIO_puts(bio_err, (const char *)msg);
+ if (key)
+ BN_print(bio_err, key);
+ BIO_puts(bio_err, "\n");
+
+ BIO_free(bio_err);
+
+}
+#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
new file mode 100644
index 0000000..9c824c8
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
@@ -0,0 +1,265 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#ifndef _PKINIT_CRYPTO_OPENSSL_H
+#define _PKINIT_CRYPTO_OPENSSL_H
+
+#include <openssl/bn.h>
+#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>
+#include <openssl/evp.h>
+#include <openssl/asn1_mac.h>
+#include <openssl/sha.h>
+#include <openssl/asn1.h>
+#include <openssl/pem.h>
+
+#include "pkinit.h"
+
+#define DN_BUF_LEN 256
+#define MAX_CREDS_ALLOWED 20
+
+struct _pkinit_cred_info {
+ X509 *cert;
+ EVP_PKEY *key;
+#ifndef WITHOUT_PKCS11
+ CK_BYTE_PTR cert_id;
+ int cert_id_len;
+#endif
+};
+typedef struct _pkinit_cred_info * pkinit_cred_info;
+
+struct _pkinit_identity_crypto_context {
+ pkinit_cred_info creds[MAX_CREDS_ALLOWED+1];
+ STACK_OF(X509) *my_certs; /* available user certs */
+ int cert_index; /* cert to use out of available certs*/
+ EVP_PKEY *my_key; /* available user keys if in filesystem */
+ STACK_OF(X509) *trustedCAs; /* available trusted ca certs */
+ STACK_OF(X509) *intermediateCAs; /* available intermediate ca certs */
+ STACK_OF(X509_CRL) *revoked; /* available crls */
+ int pkcs11_method;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+#ifndef WITHOUT_PKCS11
+ char *p11_module_name;
+ CK_SLOT_ID slotid;
+ char *token_label;
+ char *cert_label;
+ /* These are crypto-specific */
+ void *p11_module;
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST_PTR p11;
+ CK_BYTE_PTR cert_id;
+ int cert_id_len;
+ CK_MECHANISM_TYPE mech;
+#endif
+};
+
+struct _pkinit_plg_crypto_context {
+ DH *dh_1024;
+ DH *dh_2048;
+ DH *dh_4096;
+ ASN1_OBJECT *id_pkinit_authData;
+ ASN1_OBJECT *id_pkinit_authData9;
+ ASN1_OBJECT *id_pkinit_DHKeyData;
+ ASN1_OBJECT *id_pkinit_rkeyData;
+ ASN1_OBJECT *id_pkinit_san;
+ ASN1_OBJECT *id_ms_san_upn;
+ ASN1_OBJECT *id_pkinit_KPClientAuth;
+ ASN1_OBJECT *id_pkinit_KPKdc;
+ ASN1_OBJECT *id_ms_kp_sc_logon;
+ ASN1_OBJECT *id_kp_serverAuth;
+};
+
+struct _pkinit_req_crypto_context {
+ X509 *received_cert;
+ DH *dh;
+};
+
+#define CERT_MAGIC 0x53534c43
+struct _pkinit_cert_data {
+ unsigned int magic;
+ pkinit_plg_crypto_context plgctx;
+ pkinit_req_crypto_context reqctx;
+ pkinit_identity_crypto_context idctx;
+ pkinit_cred_info cred;
+ unsigned int index; /* Index of this cred in the creds[] array */
+};
+
+#define ITER_MAGIC 0x53534c49
+struct _pkinit_cert_iter_data {
+ unsigned int magic;
+ pkinit_plg_crypto_context plgctx;
+ pkinit_req_crypto_context reqctx;
+ pkinit_identity_crypto_context idctx;
+ unsigned int index;
+};
+
+static void openssl_init(void);
+
+static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );
+static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context );
+
+static krb5_error_code pkinit_init_dh_params(pkinit_plg_crypto_context );
+static void pkinit_fini_dh_params(pkinit_plg_crypto_context );
+
+static krb5_error_code pkinit_init_certs(pkinit_identity_crypto_context ctx);
+static void pkinit_fini_certs(pkinit_identity_crypto_context ctx);
+
+static krb5_error_code pkinit_init_pkcs11(pkinit_identity_crypto_context ctx);
+static void pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx);
+
+static krb5_error_code pkinit_encode_dh_params
+ (BIGNUM *, BIGNUM *, BIGNUM *, unsigned char **, unsigned int *);
+static DH *pkinit_decode_dh_params
+ (DH **, unsigned char **, unsigned int );
+static int pkinit_check_dh_params
+ (BIGNUM * p1, BIGNUM * p2, BIGNUM * g1, BIGNUM * q1);
+
+static krb5_error_code pkinit_sign_data
+ (krb5_context context, pkinit_identity_crypto_context cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **sig, unsigned int *sig_len);
+
+static krb5_error_code create_signature
+ (unsigned char **, unsigned int *, unsigned char *, unsigned int,
+ EVP_PKEY *pkey);
+
+static krb5_error_code pkinit_decode_data
+ (krb5_context context, pkinit_identity_crypto_context cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **decoded, unsigned int *decoded_len);
+
+static krb5_error_code decode_data
+ (unsigned char **, unsigned int *, unsigned char *, unsigned int,
+ EVP_PKEY *pkey, X509 *cert);
+
+#ifdef DEBUG_DH
+static void print_dh(DH *, char *);
+static void print_pubkey(BIGNUM *, char *);
+#endif
+
+static int prepare_enc_data
+ (unsigned char *indata, int indata_len, unsigned char **outdata,
+ int *outdata_len);
+
+static int openssl_callback (int, X509_STORE_CTX *);
+static int openssl_callback_ignore_crls (int, X509_STORE_CTX *);
+
+static int pkcs7_decrypt
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7, BIO *bio);
+
+static BIO * pkcs7_dataDecode
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7);
+
+static ASN1_OBJECT * pkinit_pkcs7type2oid
+ (pkinit_plg_crypto_context plg_cryptoctx, int pkcs7_type);
+
+static krb5_error_code pkinit_create_sequence_of_principal_identifiers
+ (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int type, krb5_data **out_data);
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code pkinit_find_private_key
+ (pkinit_identity_crypto_context, CK_ATTRIBUTE_TYPE usage,
+ CK_OBJECT_HANDLE *objp);
+static krb5_error_code pkinit_login
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ CK_TOKEN_INFO *tip);
+static krb5_error_code pkinit_open_session
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx);
+static void * pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p);
+static CK_RV pkinit_C_UnloadModule(void *handle);
+#ifdef SILLYDECRYPT
+CK_RV pkinit_C_Decrypt
+ (pkinit_identity_crypto_context id_cryptoctx,
+ CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen);
+#endif
+
+static krb5_error_code pkinit_sign_data_pkcs11
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **sig, unsigned int *sig_len);
+static krb5_error_code pkinit_decode_data_pkcs11
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **decoded_data, unsigned int *decoded_data_len);
+#endif /* WITHOUT_PKCS11 */
+
+static krb5_error_code pkinit_sign_data_fs
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **sig, unsigned int *sig_len);
+static krb5_error_code pkinit_decode_data_fs
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **decoded_data, unsigned int *decoded_data_len);
+
+static krb5_error_code der_decode_data
+ (unsigned char *, long, unsigned char **, long *);
+
+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);
+
+static krb5_error_code
+create_identifiers_from_stack(STACK_OF(X509) *sk,
+ krb5_external_principal_identifier *** ids);
+#ifdef LONGHORN_BETA_COMPAT
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server);
+#else
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len);
+#endif
+
+/* This handy macro borrowed from crypto/x509v3/v3_purp.c */
+#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
new file mode 100644
index 0000000..227c55d
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_identity.c
@@ -0,0 +1,668 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pkinit.h"
+
+static void
+free_list(char **list)
+{
+ int i;
+
+ if (list == NULL)
+ return;
+
+ for (i = 0; list[i] != NULL; i++)
+ free(list[i]);
+ free(list);
+}
+
+static krb5_error_code
+copy_list(char ***dst, char **src)
+{
+ int i;
+ char **newlist;
+
+ if (dst == NULL)
+ return EINVAL;
+ *dst = NULL;
+
+ if (src == NULL)
+ return 0;
+
+ for (i = 0; src[i] != NULL; i++);
+
+ newlist = calloc(1, (i + 1) * sizeof(*newlist));
+ if (newlist == NULL)
+ return ENOMEM;
+
+ for (i = 0; src[i] != NULL; i++) {
+ newlist[i] = strdup(src[i]);
+ if (newlist[i] == NULL)
+ goto cleanup;
+ }
+ newlist[i] = NULL;
+ *dst = newlist;
+ return 0;
+cleanup:
+ free_list(newlist);
+ 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)
+{
+ pkinit_identity_opts *opts = NULL;
+
+ *idopts = NULL;
+ opts = (pkinit_identity_opts *) calloc(1, sizeof(pkinit_identity_opts));
+ if (opts == NULL)
+ return ENOMEM;
+
+ opts->identity = NULL;
+ opts->anchors = NULL;
+ opts->intermediates = NULL;
+ opts->crls = NULL;
+ opts->ocsp = NULL;
+ opts->dn_mapping_file = NULL;
+
+ opts->cert_filename = NULL;
+ opts->key_filename = NULL;
+#ifndef WITHOUT_PKCS11
+ opts->p11_module_name = NULL;
+ opts->slotid = PK_NOSLOT;
+ opts->token_label = NULL;
+ opts->cert_id_string = NULL;
+ opts->cert_label = NULL;
+#endif
+
+ *idopts = opts;
+
+ return 0;
+}
+
+krb5_error_code
+pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
+ pkinit_identity_opts **dest_opts)
+{
+ pkinit_identity_opts *newopts;
+ krb5_error_code retval;
+
+ *dest_opts = NULL;
+ retval = pkinit_init_identity_opts(&newopts);
+ if (retval)
+ return retval;
+
+ retval = ENOMEM;
+
+ if (src_opts->identity != NULL) {
+ newopts->identity = strdup(src_opts->identity);
+ if (newopts->identity == NULL)
+ goto cleanup;
+ }
+
+ retval = copy_list(&newopts->anchors, src_opts->anchors);
+ if (retval)
+ goto cleanup;
+
+ retval = copy_list(&newopts->intermediates,src_opts->intermediates);
+ if (retval)
+ goto cleanup;
+
+ retval = copy_list(&newopts->crls, src_opts->crls);
+ if (retval)
+ goto cleanup;
+
+ if (src_opts->ocsp != NULL) {
+ newopts->ocsp = strdup(src_opts->ocsp);
+ if (newopts->ocsp == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_filename != NULL) {
+ newopts->cert_filename = strdup(src_opts->cert_filename);
+ if (newopts->cert_filename == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->key_filename != NULL) {
+ newopts->key_filename = strdup(src_opts->key_filename);
+ if (newopts->key_filename == NULL)
+ goto cleanup;
+ }
+
+#ifndef WITHOUT_PKCS11
+ if (src_opts->p11_module_name != NULL) {
+ newopts->p11_module_name = strdup(src_opts->p11_module_name);
+ if (newopts->p11_module_name == NULL)
+ goto cleanup;
+ }
+
+ newopts->slotid = src_opts->slotid;
+
+ if (src_opts->token_label != NULL) {
+ newopts->token_label = strdup(src_opts->token_label);
+ if (newopts->token_label == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_id_string != NULL) {
+ newopts->cert_id_string = strdup(src_opts->cert_id_string);
+ if (newopts->cert_id_string == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_label != NULL) {
+ newopts->cert_label = strdup(src_opts->cert_label);
+ if (newopts->cert_label == NULL)
+ goto cleanup;
+ }
+#endif
+
+
+ *dest_opts = newopts;
+ return 0;
+cleanup:
+ pkinit_fini_identity_opts(newopts);
+ return retval;
+}
+
+void
+pkinit_fini_identity_opts(pkinit_identity_opts *idopts)
+{
+ if (idopts == NULL)
+ return;
+
+ if (idopts->identity != NULL)
+ free(idopts->identity);
+ free_list(idopts->anchors);
+ free_list(idopts->intermediates);
+ free_list(idopts->crls);
+ free_list(idopts->identity_alt);
+
+ if (idopts->cert_filename != NULL)
+ free(idopts->cert_filename);
+ if (idopts->key_filename != NULL)
+ free(idopts->key_filename);
+#ifndef WITHOUT_PKCS11
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ if (idopts->token_label != NULL)
+ free(idopts->token_label);
+ if (idopts->cert_id_string != NULL)
+ free(idopts->cert_id_string);
+ if (idopts->cert_label != NULL)
+ free(idopts->cert_label);
+#endif
+ free(idopts);
+}
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code
+parse_pkcs11_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ char *s, *cp, *vp;
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ /* Split string into attr=value substrings */
+ s = strdup(residual);
+ if (s == NULL)
+ return retval;
+
+ for ((cp = strtok(s, ":")); cp; (cp = strtok(NULL, ":"))) {
+ vp = strchr(cp, '=');
+
+ /* If there is no "=", this is a pkcs11 module name */
+ if (vp == NULL) {
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ idopts->p11_module_name = strdup(cp);
+ if (idopts->p11_module_name == NULL)
+ goto cleanup;
+ continue;
+ }
+ *vp++ = '\0';
+ if (!strcmp(cp, "module_name")) {
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ idopts->p11_module_name = strdup(vp);
+ if (idopts->p11_module_name == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "slotid")) {
+ long slotid = strtol(vp, NULL, 10);
+ if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) {
+ retval = EINVAL;
+ goto cleanup;
+ }
+ if ((long) (int) slotid != slotid) {
+ retval = EINVAL;
+ goto cleanup;
+ }
+ idopts->slotid = slotid;
+ } else if (!strcmp(cp, "token")) {
+ if (idopts->token_label != NULL)
+ free(idopts->token_label);
+ idopts->token_label = strdup(vp);
+ if (idopts->token_label == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "certid")) {
+ if (idopts->cert_id_string != NULL)
+ free(idopts->cert_id_string);
+ idopts->cert_id_string = strdup(vp);
+ if (idopts->cert_id_string == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "certlabel")) {
+ if (idopts->cert_label != NULL)
+ free(idopts->cert_label);
+ idopts->cert_label = strdup(vp);
+ if (idopts->cert_label == NULL)
+ goto cleanup;
+ }
+ }
+ retval = 0;
+cleanup:
+ free(s);
+ return retval;
+}
+#endif
+
+static krb5_error_code
+parse_fs_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ char *certname, *keyname;
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ certname = strdup(residual);
+ if (certname == NULL)
+ goto cleanup;
+
+ certname = strtok(certname, ",");
+ keyname = strtok(NULL, ",");
+
+ idopts->cert_filename = strdup(certname);
+ if (idopts->cert_filename == NULL)
+ goto cleanup;
+
+ idopts->key_filename = strdup(keyname ? keyname : certname);
+ if (idopts->key_filename == NULL)
+ goto cleanup;
+
+ retval = 0;
+cleanup:
+ if (certname != NULL)
+ free(certname);
+ return retval;
+}
+
+static krb5_error_code
+parse_pkcs12_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ idopts->cert_filename = strdup(residual);
+ if (idopts->cert_filename == NULL)
+ goto cleanup;
+
+ idopts->key_filename = strdup(residual);
+ if (idopts->key_filename == NULL)
+ goto cleanup;
+
+ pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
+ __FUNCTION__, idopts->cert_filename,
+ idopts->key_filename);
+ retval = 0;
+cleanup:
+ return retval;
+}
+
+static krb5_error_code
+process_option_identity(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *value)
+{
+ const char *residual;
+ int idtype;
+ krb5_error_code retval = 0;
+
+ pkiDebug("%s: processing value '%s'\n",
+ __FUNCTION__, value ? value : "NULL");
+ if (value == NULL)
+ return EINVAL;
+
+ residual = strchr(value, ':');
+ if (residual != NULL) {
+ unsigned int typelen;
+ residual++; /* skip past colon */
+ typelen = residual - value;
+ if (strncmp(value, "FILE:", typelen) == 0) {
+ idtype = IDTYPE_FILE;
+#ifndef WITHOUT_PKCS11
+ } else if (strncmp(value, "PKCS11:", typelen) == 0) {
+ idtype = IDTYPE_PKCS11;
+#endif
+ } 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);
+ return KRB5_PREAUTH_FAILED;
+ }
+ } else {
+ idtype = IDTYPE_FILE;
+ residual = value;
+ }
+
+ idopts->idtype = 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,
+ idopts, id_cryptoctx, getenv(residual));
+ break;
+ case IDTYPE_FILE:
+ retval = parse_fs_options(context, idopts, residual);
+ break;
+ case IDTYPE_PKCS12:
+ retval = parse_pkcs12_options(context, idopts, residual);
+ break;
+#ifndef WITHOUT_PKCS11
+ case IDTYPE_PKCS11:
+ retval = parse_pkcs11_options(context, idopts, residual);
+ break;
+#endif
+ case IDTYPE_DIR:
+ idopts->cert_filename = strdup(residual);
+ if (idopts->cert_filename == NULL)
+ retval = ENOMEM;
+ break;
+ default:
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ "Internal error parsing X509_user_identity\n");
+ retval = EINVAL;
+ break;
+ }
+ return retval;
+}
+
+static krb5_error_code
+process_option_ca_crl(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *value,
+ int catype)
+{
+ char *residual;
+ unsigned int typelen;
+ int idtype;
+
+ 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;
+ }
+ residual++; /* skip past colon */
+ typelen = residual - value;
+ if (strncmp(value, "FILE:", typelen) == 0) {
+ idtype = IDTYPE_FILE;
+ } else if (strncmp(value, "DIR:", typelen) == 0) {
+ idtype = IDTYPE_DIR;
+ } else {
+ return ENOTSUP;
+ }
+ return crypto_load_cas_and_crls(context,
+ plg_cryptoctx,
+ req_cryptoctx,
+ idopts, id_cryptoctx,
+ idtype, catype, residual);
+}
+
+static krb5_error_code
+pkinit_identity_process_option(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int attr,
+ const char *value)
+{
+ krb5_error_code retval = 0;
+
+ switch (attr) {
+ case PKINIT_ID_OPT_USER_IDENTITY:
+ retval = process_option_identity(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, value);
+ break;
+ case PKINIT_ID_OPT_ANCHOR_CAS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, value,
+ CATYPE_ANCHORS);
+ break;
+ case PKINIT_ID_OPT_INTERMEDIATE_CAS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ value, CATYPE_INTERMEDIATES);
+ break;
+ case PKINIT_ID_OPT_CRLS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ value, CATYPE_CRLS);
+ break;
+ case PKINIT_ID_OPT_OCSP:
+ retval = ENOTSUP;
+ break;
+ default:
+ retval = EINVAL;
+ break;
+ }
+ return retval;
+}
+
+krb5_error_code
+pkinit_identity_initialize(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int do_matching,
+ krb5_principal princ)
+{
+ krb5_error_code retval = EINVAL;
+ int i;
+
+ pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
+ if (idopts == NULL || id_cryptoctx == NULL)
+ goto errout;
+
+ /*
+ * If identity was specified, use that. (For the kdc, this
+ * is specified as pkinit_identity in the kdc.conf. For users,
+ * this is specified on the command line via X509_user_identity.)
+ * If a user did not specify identity on the command line,
+ * then we will try alternatives which may have been specified
+ * in the config file.
+ */
+ if (idopts->identity != NULL) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_USER_IDENTITY,
+ idopts->identity);
+ } else if (idopts->identity_alt != NULL) {
+ for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++)
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_USER_IDENTITY,
+ idopts->identity_alt[i]);
+ } else {
+ pkiDebug("%s: no user identity options specified\n", __FUNCTION__);
+ goto errout;
+ }
+ if (retval)
+ goto errout;
+
+ retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx,
+ idopts, id_cryptoctx, princ);
+ if (retval)
+ goto errout;
+
+ if (do_matching) {
+ retval = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, princ);
+ if (retval) {
+ pkiDebug("%s: No matching certificate found\n", __FUNCTION__);
+ crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx);
+ goto errout;
+ }
+ } else {
+ /* Tell crypto code to use the "default" */
+ retval = crypto_cert_select_default(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx);
+ if (retval) {
+ pkiDebug("%s: Failed while selecting default certificate\n",
+ __FUNCTION__);
+ crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx);
+ goto errout;
+ }
+ }
+
+ retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx);
+ if (retval)
+ goto errout;
+
+ for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_ANCHOR_CAS,
+ idopts->anchors[i]);
+ if (retval)
+ goto errout;
+ }
+ for (i = 0; idopts->intermediates != NULL
+ && idopts->intermediates[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_INTERMEDIATE_CAS,
+ idopts->intermediates[i]);
+ if (retval)
+ goto errout;
+ }
+ for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_CRLS,
+ idopts->crls[i]);
+ if (retval)
+ goto errout;
+ }
+ if (idopts->ocsp != NULL) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_OCSP,
+ idopts->ocsp);
+ if (retval)
+ goto errout;
+ }
+
+errout:
+ return retval;
+}
+
diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c
new file mode 100644
index 0000000..f49ef5e
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_lib.c
@@ -0,0 +1,477 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "pkinit.h"
+
+#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;
+ pkinit_req_opts *opts = NULL;
+
+ *reqopts = NULL;
+ opts = (pkinit_req_opts *) calloc(1, sizeof(pkinit_req_opts));
+ if (opts == NULL)
+ return retval;
+
+ opts->require_eku = 1;
+ opts->accept_secondary_eku = 0;
+ opts->allow_upn = 0;
+ opts->dh_or_rsa = DH_PROTOCOL;
+ opts->require_crl_checking = 0;
+ opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS;
+ opts->win2k_target = 0;
+ opts->win2k_require_cksum = 0;
+
+ *reqopts = opts;
+
+ return 0;
+}
+
+void
+pkinit_fini_req_opts(pkinit_req_opts *opts)
+{
+ if (opts != NULL)
+ free(opts);
+ return;
+}
+
+krb5_error_code
+pkinit_init_plg_opts(pkinit_plg_opts **plgopts)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_plg_opts *opts = NULL;
+
+ *plgopts = NULL;
+ opts = (pkinit_plg_opts *) calloc(1, sizeof(pkinit_plg_opts));
+ if (opts == NULL)
+ return retval;
+
+ opts->require_eku = 1;
+ opts->accept_secondary_eku = 0;
+ opts->dh_or_rsa = DH_PROTOCOL;
+ opts->allow_upn = 0;
+ opts->require_crl_checking = 0;
+
+ opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
+
+ *plgopts = opts;
+
+ return 0;
+}
+
+void
+pkinit_fini_plg_opts(pkinit_plg_opts *opts)
+{
+ if (opts != NULL)
+ free(opts);
+ return;
+}
+
+void
+free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->signedAuthPack.data != NULL)
+ free((*in)->signedAuthPack.data);
+ if ((*in)->trustedCertifiers != NULL)
+ free_krb5_external_principal_identifier(&(*in)->trustedCertifiers);
+ if ((*in)->kdcPkId.data != NULL)
+ free((*in)->kdcPkId.data);
+ free(*in);
+}
+
+void
+free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->signedAuthPack.data != NULL)
+ free((*in)->signedAuthPack.data);
+ if ((*in)->kdcCert.data != NULL)
+ free((*in)->kdcCert.data);
+ if ((*in)->encryptionCert.data != NULL)
+ free((*in)->encryptionCert.data);
+ if ((*in)->trustedCertifiers != NULL)
+ free_krb5_trusted_ca(&(*in)->trustedCertifiers);
+ free(*in);
+}
+
+void
+free_krb5_reply_key_pack(krb5_reply_key_pack **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->replyKey.contents != NULL)
+ free((*in)->replyKey.contents);
+ if ((*in)->asChecksum.contents != NULL)
+ free((*in)->asChecksum.contents);
+ free(*in);
+}
+
+void
+free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->replyKey.contents != NULL)
+ free((*in)->replyKey.contents);
+ free(*in);
+}
+
+void
+free_krb5_auth_pack(krb5_auth_pack **in)
+{
+ if ((*in) == NULL) return;
+ if ((*in)->clientPublicValue != NULL) {
+ if ((*in)->clientPublicValue->algorithm.algorithm.data != NULL)
+ free((*in)->clientPublicValue->algorithm.algorithm.data);
+ if ((*in)->clientPublicValue->algorithm.parameters.data != NULL)
+ free((*in)->clientPublicValue->algorithm.parameters.data);
+ if ((*in)->clientPublicValue->subjectPublicKey.data != NULL)
+ free((*in)->clientPublicValue->subjectPublicKey.data);
+ free((*in)->clientPublicValue);
+ }
+ if ((*in)->pkAuthenticator.paChecksum.contents != NULL)
+ free((*in)->pkAuthenticator.paChecksum.contents);
+ if ((*in)->supportedCMSTypes != NULL)
+ free_krb5_algorithm_identifiers(&((*in)->supportedCMSTypes));
+ free(*in);
+}
+
+void
+free_krb5_auth_pack_draft9(krb5_context context,
+ krb5_auth_pack_draft9 **in)
+{
+ if ((*in) == NULL) return;
+ krb5_free_principal(context, (*in)->pkAuthenticator.kdcName);
+ free(*in);
+}
+
+void
+free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in)
+{
+ if (*in == NULL) return;
+ switch ((*in)->choice) {
+ case choice_pa_pk_as_rep_dhInfo:
+ if ((*in)->u.dh_Info.dhSignedData.data != NULL)
+ free((*in)->u.dh_Info.dhSignedData.data);
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ if ((*in)->u.encKeyPack.data != NULL)
+ free((*in)->u.encKeyPack.data);
+ break;
+ default:
+ break;
+ }
+ free(*in);
+}
+
+void
+free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->u.encKeyPack.data != NULL)
+ free((*in)->u.encKeyPack.data);
+ free(*in);
+}
+
+void
+free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ if ((*in)[i]->subjectName.data != NULL)
+ free((*in)[i]->subjectName.data);
+ if ((*in)[i]->issuerAndSerialNumber.data != NULL)
+ free((*in)[i]->issuerAndSerialNumber.data);
+ if ((*in)[i]->subjectKeyIdentifier.data != NULL)
+ free((*in)[i]->subjectKeyIdentifier.data);
+ free((*in)[i]);
+ i++;
+ }
+ free(*in);
+}
+
+void
+free_krb5_trusted_ca(krb5_trusted_ca ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ switch((*in)[i]->choice) {
+ case choice_trusted_cas_principalName:
+ break;
+ case choice_trusted_cas_caName:
+ if ((*in)[i]->u.caName.data != NULL)
+ free((*in)[i]->u.caName.data);
+ break;
+ case choice_trusted_cas_issuerAndSerial:
+ if ((*in)[i]->u.issuerAndSerial.data != NULL)
+ free((*in)[i]->u.issuerAndSerial.data);
+ break;
+ case choice_trusted_cas_UNKNOWN:
+ break;
+ }
+ free((*in)[i]);
+ i++;
+ }
+ free(*in);
+}
+
+void
+free_krb5_typed_data(krb5_typed_data ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ if ((*in)[i]->data != NULL)
+ free((*in)[i]->data);
+ free((*in)[i]);
+ i++;
+ }
+ free(*in);
+}
+
+void
+free_krb5_algorithm_identifier(krb5_algorithm_identifier *in)
+{
+ if (in == NULL)
+ return;
+ if (in->algorithm.data != NULL)
+ free(in->algorithm.data);
+ if (in->parameters.data != NULL)
+ free(in->parameters.data);
+ free(in);
+}
+
+void
+free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in)
+{
+ int i;
+ if (in == NULL || *in == NULL)
+ return;
+ for (i = 0; (*in)[i] != NULL; i++) {
+ free_krb5_algorithm_identifier((*in)[i]);
+ }
+ free(*in);
+}
+
+void
+free_krb5_subject_pk_info(krb5_subject_pk_info **in)
+{
+ if ((*in) == NULL) return;
+ if ((*in)->algorithm.parameters.data != NULL)
+ free((*in)->algorithm.parameters.data);
+ if ((*in)->subjectPublicKey.data != NULL)
+ free((*in)->subjectPublicKey.data);
+ free(*in);
+}
+
+void
+free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->subjectPublicKey.data != NULL)
+ free((*in)->subjectPublicKey.data);
+ free(*in);
+}
+
+void
+init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_req));
+ if ((*in) == NULL) return;
+ (*in)->signedAuthPack.data = NULL;
+ (*in)->signedAuthPack.length = 0;
+ (*in)->trustedCertifiers = NULL;
+ (*in)->kdcPkId.data = NULL;
+ (*in)->kdcPkId.length = 0;
+}
+
+void
+init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_req_draft9));
+ if ((*in) == NULL) return;
+ (*in)->signedAuthPack.data = NULL;
+ (*in)->signedAuthPack.length = 0;
+ (*in)->trustedCertifiers = NULL;
+ (*in)->kdcCert.data = NULL;
+ (*in)->kdcCert.length = 0;
+ (*in)->encryptionCert.data = NULL;
+ (*in)->encryptionCert.length = 0;
+}
+
+void
+init_krb5_reply_key_pack(krb5_reply_key_pack **in)
+{
+ (*in) = malloc(sizeof(krb5_reply_key_pack));
+ if ((*in) == NULL) return;
+ (*in)->replyKey.contents = NULL;
+ (*in)->replyKey.length = 0;
+ (*in)->asChecksum.contents = NULL;
+ (*in)->asChecksum.length = 0;
+}
+
+void
+init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_reply_key_pack_draft9));
+ if ((*in) == NULL) return;
+ (*in)->replyKey.contents = NULL;
+ (*in)->replyKey.length = 0;
+}
+
+void
+init_krb5_auth_pack(krb5_auth_pack **in)
+{
+ (*in) = malloc(sizeof(krb5_auth_pack));
+ if ((*in) == NULL) return;
+ (*in)->clientPublicValue = NULL;
+ (*in)->supportedCMSTypes = NULL;
+ (*in)->clientDHNonce.length = 0;
+ (*in)->clientDHNonce.data = NULL;
+ (*in)->pkAuthenticator.paChecksum.contents = NULL;
+}
+
+void
+init_krb5_auth_pack_draft9(krb5_auth_pack_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_auth_pack_draft9));
+ if ((*in) == NULL) return;
+ (*in)->clientPublicValue = NULL;
+}
+
+void
+init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_rep));
+ if ((*in) == NULL) return;
+ (*in)->u.dh_Info.serverDHNonce.length = 0;
+ (*in)->u.dh_Info.serverDHNonce.data = NULL;
+ (*in)->u.dh_Info.dhSignedData.length = 0;
+ (*in)->u.dh_Info.dhSignedData.data = NULL;
+ (*in)->u.encKeyPack.length = 0;
+ (*in)->u.encKeyPack.data = NULL;
+}
+
+void
+init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_rep_draft9));
+ if ((*in) == NULL) return;
+ (*in)->u.dhSignedData.length = 0;
+ (*in)->u.dhSignedData.data = NULL;
+ (*in)->u.encKeyPack.length = 0;
+ (*in)->u.encKeyPack.data = NULL;
+}
+
+void
+init_krb5_typed_data(krb5_typed_data **in)
+{
+ (*in) = malloc(sizeof(krb5_typed_data));
+ if ((*in) == NULL) return;
+ (*in)->type = 0;
+ (*in)->length = 0;
+ (*in)->data = NULL;
+}
+
+void
+init_krb5_subject_pk_info(krb5_subject_pk_info **in)
+{
+ (*in) = malloc(sizeof(krb5_subject_pk_info));
+ if ((*in) == NULL) return;
+ (*in)->algorithm.parameters.data = NULL;
+ (*in)->algorithm.parameters.length = 0;
+ (*in)->subjectPublicKey.data = NULL;
+ (*in)->subjectPublicKey.length = 0;
+}
+
+krb5_error_code
+pkinit_copy_krb5_octet_data(krb5_octet_data *dst, const krb5_octet_data *src)
+{
+ if (dst == NULL || src == NULL)
+ return EINVAL;
+ if (src->data == NULL) {
+ dst->data = NULL;
+ dst->length = 0;
+ return 0;
+ }
+ dst->data = malloc(src->length);
+ if (dst->data == NULL)
+ return ENOMEM;
+ memcpy(dst->data, src->data, src->length);
+ dst->length = src->length;
+ return 0;
+}
+
+/* debugging functions */
+void
+print_buffer(unsigned char *buf, unsigned int len)
+{
+ int i = 0;
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i++)
+ pkiDebug("%02x ", buf[i]);
+ pkiDebug("\n");
+}
+
+void
+print_buffer_bin(unsigned char *buf, unsigned int len, char *filename)
+{
+ FILE *f = NULL;
+ int i = 0;
+
+ if (len <= 0 || filename == NULL)
+ return;
+
+ if ((f = fopen(filename, "w")) == NULL)
+ return;
+
+ for (i = 0; i < len; i++)
+ fputc(buf[i], f);
+
+ fclose(f);
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_matching.c b/src/plugins/preauth/pkinit/pkinit_matching.c
new file mode 100644
index 0000000..b790c38
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_matching.c
@@ -0,0 +1,830 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <regex.h>
+#include <krb5.h>
+#include "pkinit.h"
+
+typedef struct _pkinit_cert_info pkinit_cert_info;
+
+typedef enum {
+ kw_undefined = 0,
+ kw_subject = 1,
+ kw_issuer = 2,
+ kw_san = 3,
+ kw_eku = 4,
+ kw_ku = 5
+} keyword_type;
+
+static char *
+keyword2string(unsigned int kw)
+{
+ switch(kw) {
+ case kw_undefined: return "NONE"; break;
+ case kw_subject: return "SUBJECT"; break;
+ case kw_issuer: return "ISSUER"; break;
+ case kw_san: return "SAN"; break;
+ case kw_eku: return "EKU"; break;
+ case kw_ku: return "KU"; break;
+ default: return "INVALID"; break;
+ }
+}
+typedef enum {
+ relation_none = 0,
+ relation_and = 1,
+ relation_or = 2
+} relation_type;
+
+static char *
+relation2string(unsigned int rel)
+{
+ switch(rel) {
+ case relation_none: return "NONE"; break;
+ case relation_and: return "AND"; break;
+ case relation_or: return "OR"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+typedef enum {
+ kwvaltype_undefined = 0,
+ kwvaltype_regexp = 1,
+ kwvaltype_list = 2
+} kw_value_type;
+
+static char *
+kwval2string(unsigned int kwval)
+{
+ switch(kwval) {
+ case kwvaltype_undefined: return "NONE"; break;
+ case kwvaltype_regexp: return "REGEXP"; break;
+ case kwvaltype_list: return "LIST"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+struct keyword_desc {
+ const char *value;
+ size_t length;
+ keyword_type kwtype;
+ kw_value_type kwvaltype;
+} matching_keywords[] = {
+ { "<KU>", 4, kw_ku, kwvaltype_list },
+ { "<EKU>", 5, kw_eku, kwvaltype_list },
+ { "<SAN>", 5, kw_san, kwvaltype_regexp },
+ { "<ISSUER>", 8, kw_issuer, kwvaltype_regexp },
+ { "<SUBJECT>", 9, kw_subject, kwvaltype_regexp },
+ { NULL, 0, kw_undefined, kwvaltype_undefined},
+};
+
+struct ku_desc {
+ const char *value;
+ size_t length;
+ unsigned int bitval;
+};
+
+struct ku_desc ku_keywords[] = {
+ { "digitalSignature", 16, PKINIT_KU_DIGITALSIGNATURE },
+ { "keyEncipherment", 15, PKINIT_KU_KEYENCIPHERMENT },
+ { NULL, 0, 0 },
+};
+
+struct ku_desc eku_keywords[] = {
+ { "pkinit", 6, PKINIT_EKU_PKINIT },
+ { "msScLogin", 9, PKINIT_EKU_MSSCLOGIN },
+ { "clientAuth", 10, PKINIT_EKU_CLIENTAUTH },
+ { "emailProtection", 15, PKINIT_EKU_EMAILPROTECTION },
+ { NULL, 0, 0 },
+};
+
+/* Rule component */
+typedef struct _rule_component {
+ struct _rule_component *next;
+ keyword_type kw_type;
+ kw_value_type kwval_type;
+ regex_t regexp; /* Compiled regular expression */
+ char *regsrc; /* The regular expression source (for debugging) */
+ unsigned int ku_bits;
+ unsigned int eku_bits;
+} rule_component;
+
+/* Set rule components */
+typedef struct _rule_set {
+ relation_type relation;
+ int num_crs;
+ rule_component *crs;
+} rule_set;
+
+static krb5_error_code
+free_rule_component(krb5_context context,
+ rule_component *rc)
+{
+ if (rc == NULL)
+ return 0;
+
+ if (rc->kwval_type == kwvaltype_regexp) {
+ if (rc->regsrc)
+ free(rc->regsrc);
+ regfree(&rc->regexp);
+ }
+ free(rc);
+ return 0;
+}
+
+static krb5_error_code
+free_rule_set(krb5_context context,
+ rule_set *rs)
+{
+ rule_component *rc, *trc;
+
+ if (rs == NULL)
+ return 0;
+ for (rc = rs->crs; rc != NULL;) {
+ trc = rc->next;
+ free_rule_component(context, rc);
+ rc = trc;
+ }
+ free(rs);
+ return 0;
+}
+
+static krb5_error_code
+parse_list_value(krb5_context context,
+ keyword_type type,
+ char *value,
+ rule_component *rc)
+{
+ krb5_error_code retval;
+ char *comma;
+ struct ku_desc *ku = NULL;
+ int found;
+ size_t len;
+ unsigned int *bitptr;
+
+
+ if (value == NULL || value[0] == '\0') {
+ pkiDebug("%s: Missing or empty value for list keyword type %d\n",
+ __FUNCTION__, type);
+ retval = EINVAL;
+ goto out;
+ }
+
+ if (type == kw_eku) {
+ bitptr = &rc->eku_bits;
+ } else if (type == kw_ku) {
+ bitptr = &rc->ku_bits;
+ } else {
+ pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type);
+ retval = EINVAL;
+ goto out;
+ }
+
+ do {
+ found = 0;
+ comma = strchr(value, ',');
+ if (comma != NULL)
+ len = comma - value;
+ else
+ len = strlen(value);
+
+ if (type == kw_eku) {
+ ku = eku_keywords;
+ } else if (type == kw_ku) {
+ ku = ku_keywords;
+ }
+
+ for (; ku->value != NULL; ku++) {
+ if (strncasecmp(value, ku->value, len) == 0) {
+ *bitptr |= ku->bitval;
+ found = 1;
+ pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n",
+ __FUNCTION__, ku->value, *bitptr);
+ break;
+ }
+ }
+ if (found) {
+ value += ku->length;
+ if (*value == ',')
+ value += 1;
+ } else {
+ pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value);
+ retval = EINVAL;
+ goto out;
+ }
+ } while (found && *value != '\0');
+
+ retval = 0;
+out:
+ pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static krb5_error_code
+parse_rule_component(krb5_context context,
+ const char **rule,
+ int *remaining,
+ rule_component **ret_rule)
+{
+ krb5_error_code retval;
+ rule_component *rc = NULL;
+ keyword_type kw_type;
+ kw_value_type kwval_type;
+ char err_buf[128];
+ int ret;
+ struct keyword_desc *kw, *nextkw;
+ char *nk;
+ int found_next_kw = 0;
+ char *value = NULL;
+ size_t len;
+
+ for (kw = matching_keywords; kw->value != NULL; kw++) {
+ if (strncmp(*rule, kw->value, kw->length) == 0) {
+ kw_type = kw->kwtype;
+ kwval_type = kw->kwvaltype;
+ *rule += kw->length;
+ *remaining -= kw->length;
+ break;
+ }
+ }
+ if (kw->value == NULL) {
+ pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
+ __FUNCTION__, *rule);
+ retval = ENOENT;
+ goto out;
+ }
+
+ pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);
+
+ rc = calloc(1, sizeof(*rc));
+ if (rc == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ rc->next = NULL;
+ rc->kw_type = kw_type;
+ rc->kwval_type = kwval_type;
+
+ /*
+ * Before procesing the value for this keyword,
+ * (compiling the regular expression or processing the list)
+ * we need to find the end of it. That means parsing for the
+ * beginning of the next keyword (or the end of the rule).
+ */
+ nk = strchr(*rule, '<');
+ while (nk != NULL) {
+ /* Possibly another keyword, check it out */
+ for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
+ if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
+ /* Found a keyword, nk points to the beginning */
+ found_next_kw = 1;
+ break; /* Need to break out of the while! */
+ }
+ }
+ if (!found_next_kw)
+ nk = strchr(nk+1, '<'); /* keep looking */
+ else
+ break;
+ }
+
+ if (nk != NULL && found_next_kw)
+ len = (nk - *rule);
+ else
+ len = (*remaining);
+
+ if (len == 0) {
+ pkiDebug("%s: Missing value for keyword '%s'\n",
+ __FUNCTION__, kw->value);
+ retval = EINVAL;
+ goto out;
+ }
+
+ value = calloc(1, len+1);
+ if (value == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ memcpy(value, *rule, len);
+ *remaining -= len;
+ *rule += len;
+ pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);
+
+ if (kw->kwvaltype == kwvaltype_regexp) {
+ ret = regcomp(&rc->regexp, value, REG_EXTENDED);
+ if (ret) {
+ regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
+ pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
+ __FUNCTION__, value, err_buf);
+ retval = ret;
+ goto out;
+ }
+ rc->regsrc = strdup(value);
+ if (rc->regsrc == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ } else if (kw->kwvaltype == kwvaltype_list) {
+ retval = parse_list_value(context, rc->kw_type, value, rc);
+ if (retval) {
+ pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
+ __FUNCTION__, retval, kw->value);
+ goto out;
+ }
+ }
+
+ *ret_rule = rc;
+ retval = 0;
+out:
+ if (value != NULL)
+ free(value);
+ if (retval && rc != NULL)
+ free_rule_component(context, rc);
+ pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static krb5_error_code
+parse_rule_set(krb5_context context,
+ const char *rule_in,
+ rule_set **out_rs)
+{
+ const char *rule;
+ int remaining, totlen;
+ krb5_error_code ret, retval;
+ rule_component *rc = NULL, *trc;
+ rule_set *rs;
+
+
+ if (rule_in == NULL)
+ return EINVAL;
+ rule = rule_in;
+ totlen = remaining = strlen(rule);
+
+ rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ rs->relation = relation_none;
+ if (remaining > 1) {
+ if (rule[0] == '&' && rule[1] == '&') {
+ rs->relation = relation_and;
+ rule += 2;
+ remaining -= 2;
+ } else if (rule_in[0] == '|' && rule_in[1] == '|') {
+ rs->relation = relation_or;
+ rule +=2;
+ remaining -= 2;
+ }
+ }
+ rs->num_crs = 0;
+ while (remaining > 0) {
+ if (rs->relation == relation_none && rs->num_crs > 1) {
+ pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
+ __FUNCTION__, rule_in);
+ rs->relation = relation_and;
+ }
+ ret = parse_rule_component(context, &rule, &remaining, &rc);
+ if (ret) {
+ retval = ret;
+ goto cleanup;
+ }
+ pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
+ __FUNCTION__, remaining, rule);
+ rs->num_crs++;
+
+ /*
+ * Chain the new component on the end (order matters since
+ * we can short-circuit an OR or an AND relation if an
+ * earlier check passes
+ */
+ for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
+ if (trc == NULL)
+ rs->crs = rc;
+ else {
+ trc->next = rc;
+ }
+ }
+
+ *out_rs = rs;
+
+ retval = 0;
+cleanup:
+ if (retval && rs != NULL) {
+ free_rule_set(context, rs);
+ }
+ pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static int
+regexp_match(krb5_context context, rule_component *rc, char *value)
+{
+ int code;
+
+ pkiDebug("%s: checking %s rule '%s' with value '%s'\n",
+ __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value);
+
+ code = regexec(&rc->regexp, value, 0, NULL, 0);
+
+ pkiDebug("%s: the result is%s a match\n", __FUNCTION__,
+ code == REG_NOMATCH ? " NOT" : "");
+
+ return (code == 0 ? 1: 0);
+}
+
+static int
+component_match(krb5_context context,
+ rule_component *rc,
+ pkinit_cert_matching_data *md)
+{
+ int match = 0;
+ int i;
+ krb5_principal p;
+ char *princ_string;
+
+ switch (rc->kwval_type) {
+ case kwvaltype_regexp:
+ switch (rc->kw_type) {
+ case kw_subject:
+ match = regexp_match(context, rc, md->subject_dn);
+ break;
+ case kw_issuer:
+ 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);
+ krb5_free_unparsed_name(context, princ_string);
+ if (match)
+ break;
+ }
+ break;
+ default:
+ pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ kwval2string(kwvaltype_regexp));
+ break;
+ }
+ break;
+ case kwvaltype_list:
+ switch(rc->kw_type) {
+ case kw_eku:
+ pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ rc->eku_bits, md->eku_bits);
+ if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
+ match = 1;
+ break;
+ case kw_ku:
+ pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ rc->ku_bits, md->ku_bits);
+ if ((rc->ku_bits & md->ku_bits) == rc->ku_bits)
+ match = 1;
+ break;
+ default:
+ pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ kwval2string(kwvaltype_regexp));
+ break;
+ }
+ break;
+ default:
+ pkiDebug("%s: unknown keyword value type %d\n",
+ __FUNCTION__, rc->kwval_type);
+ break;
+ }
+ pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
+ return match;
+}
+/*
+ * Returns match_found == 1 only if exactly one certificate matches
+ * the given rule
+ */
+static krb5_error_code
+check_all_certs(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ,
+ rule_set *rs, /* rule to check */
+ pkinit_cert_matching_data **matchdata,
+ int *match_found,
+ pkinit_cert_matching_data **matching_cert)
+{
+ krb5_error_code retval;
+ pkinit_cert_matching_data *md;
+ int i;
+ int comp_match = 0;
+ int total_cert_matches = 0;
+ rule_component *rc;
+ int certs_checked = 0;
+ pkinit_cert_matching_data *save_match = NULL;
+
+ if (match_found == NULL || matching_cert == NULL)
+ return EINVAL;
+
+ *matching_cert = NULL;
+ *match_found = 0;
+
+ pkiDebug("%s: matching rule relation is %s with %d components\n",
+ __FUNCTION__, relation2string(rs->relation), rs->num_crs);
+
+ /*
+ * Loop through all the certs available and count
+ * how many match the rule
+ */
+ for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
+ pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
+#if 0
+ pkiDebug("%s: issuer: '%s'\n", __FUNCTION__, md->subject_dn);
+ for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) {
+ char *san_string;
+ krb5_unparse_name(context, p, &san_string);
+ pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string);
+ krb5_free_unparsed_name(context, san_string);
+ }
+#endif
+ certs_checked++;
+ for (rc = rs->crs; rc != NULL; rc = rc->next) {
+ comp_match = component_match(context, rc, md);
+ if (comp_match) {
+ pkiDebug("%s: match for keyword type %s\n",
+ __FUNCTION__, keyword2string(rc->kw_type));
+ }
+ if (comp_match && rs->relation == relation_or) {
+ pkiDebug("%s: cert matches rule (OR relation)\n",
+ __FUNCTION__);
+ total_cert_matches++;
+ save_match = md;
+ goto nextcert;
+ }
+ if (!comp_match && rs->relation == relation_and) {
+ pkiDebug("%s: cert does not match rule (AND relation)\n",
+ __FUNCTION__);
+ goto nextcert;
+ }
+ }
+ if (rc == NULL && comp_match) {
+ pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
+ total_cert_matches++;
+ save_match = md;
+ }
+nextcert:
+ continue;
+ }
+ pkiDebug("%s: After checking %d certs, we found %d matches\n",
+ __FUNCTION__, certs_checked, total_cert_matches);
+ if (total_cert_matches == 1) {
+ *match_found = 1;
+ *matching_cert = save_match;
+ }
+
+ retval = 0;
+
+ pkiDebug("%s: returning %d, match_found %d\n",
+ __FUNCTION__, retval, *match_found);
+ return retval;
+}
+
+static krb5_error_code
+free_all_cert_matching_data(krb5_context context,
+ pkinit_cert_matching_data **matchdata)
+{
+ krb5_error_code retval;
+ pkinit_cert_matching_data *md;
+ int i;
+
+ if (matchdata == NULL)
+ return EINVAL;
+
+ for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
+ pkinit_cert_handle ch = md->ch;
+ retval = crypto_cert_free_matching_data(context, md);
+ if (retval) {
+ pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto cleanup;
+ }
+ retval = crypto_cert_release(context, ch);
+ if (retval) {
+ pkiDebug("%s: crypto_cert_release error %d, %s\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto cleanup;
+ }
+ }
+ free(matchdata);
+ retval = 0;
+
+cleanup:
+ return retval;
+}
+
+static krb5_error_code
+obtain_all_cert_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ pkinit_cert_matching_data ***all_matching_data)
+{
+ krb5_error_code retval;
+ int i, cert_count;
+ pkinit_cert_iter_handle ih = NULL;
+ pkinit_cert_handle ch;
+ pkinit_cert_matching_data **matchdata = NULL;
+
+ 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 cleanup;
+ }
+
+ pkiDebug("%s: crypto_cert_get_count says there are %d certs\n",
+ __FUNCTION__, cert_count);
+
+ matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata));
+ if (matchdata == NULL)
+ return ENOMEM;
+
+ retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, &ih);
+ if (retval) {
+ pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto cleanup;
+ }
+
+ for (i = 0; i < cert_count; i++) {
+ retval = crypto_cert_iteration_next(context, ih, &ch);
+ if (retval) {
+ if (retval == PKINIT_ITER_NO_MORE)
+ pkiDebug("%s: We thought there were %d certs, but "
+ "crypto_cert_iteration_next stopped after %d?\n",
+ __FUNCTION__, cert_count, i);
+ else
+ pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto cleanup;
+ }
+
+ retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]);
+ if (retval) {
+ pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto cleanup;
+ }
+
+ }
+
+ *all_matching_data = matchdata;
+ retval = 0;
+cleanup:
+ if (ih != NULL)
+ crypto_cert_iteration_end(context, ih);
+ if (retval) {
+ if (matchdata != NULL)
+ free_all_cert_matching_data(context, matchdata);
+ }
+ pkiDebug("%s: returning %d, certinfo %p\n",
+ __FUNCTION__, retval, *all_matching_data);
+ return retval;
+}
+
+krb5_error_code
+pkinit_cert_matching(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ)
+{
+
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ int x;
+ char **rules = NULL;
+ rule_set *rs = NULL;
+ int match_found = 0;
+ pkinit_cert_matching_data **matchdata = NULL;
+ 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),
+ "pkinit_cert_match", &rules);
+ if (rules == NULL) {
+ pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
+ retval = crypto_cert_select_default(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx);
+ goto cleanup;
+ }
+
+ /* parse each rule line one at a time and check all the certs against it */
+ for (x = 0; rules[x] != NULL; x++) {
+ pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]);
+
+ /* Free rules from previous time through... */
+ if (rs != NULL) {
+ free_rule_set(context, rs);
+ rs = NULL;
+ }
+ retval = parse_rule_set(context, rules[x], &rs);
+ if (retval) {
+ if (retval == EINVAL) {
+ pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n",
+ __FUNCTION__, rules[x]);
+ continue;
+ }
+ goto cleanup;
+ }
+
+ /*
+ * Optimize so that we do not get cert info unless we have
+ * valid rules to check. Once obtained, keep it around
+ * until we are done.
+ */
+ if (matchdata == NULL) {
+ retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx,
+ &matchdata);
+ if (retval || matchdata == NULL) {
+ pkiDebug("%s: Error %d obtaining certificate information\n",
+ __FUNCTION__, retval);
+ retval = ENOENT;
+ goto cleanup;
+ }
+ }
+
+ retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, princ, rs, matchdata,
+ &match_found, &the_matching_cert);
+ if (retval) {
+ pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
+ __FUNCTION__, retval, rules[x]);
+ goto cleanup;
+ }
+ if (match_found) {
+ pkiDebug("%s: We have an exact match with rule '%s'\n",
+ __FUNCTION__, rules[x]);
+ break;
+ }
+ }
+
+ if (match_found && the_matching_cert != NULL) {
+ pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
+ retval = crypto_cert_select(context, the_matching_cert);
+ if (retval) {
+ pkiDebug("%s: crypto_cert_select error %d, %s\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto cleanup;
+ }
+ } else {
+ retval = ENOENT; /* XXX */
+ goto cleanup;
+ }
+
+ retval = 0;
+cleanup:
+ if (rules != NULL)
+ profile_free_list(rules);
+ if (rs != NULL)
+ free_rule_set(context, rs);
+ if (matchdata != NULL)
+ free_all_cert_matching_data(context, matchdata);
+ return retval;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_profile.c b/src/plugins/preauth/pkinit/pkinit_profile.c
new file mode 100644
index 0000000..403068a
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_profile.c
@@ -0,0 +1,376 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "k5-int.h"
+#include "pkinit.h"
+
+/*
+ * Routines for handling profile [config file] options
+ */
+
+/* Forward prototypes */
+static int _krb5_conf_boolean(const char *s);
+
+/*
+ * XXX
+ * The following is duplicated verbatim from src/lib/krb5/krb/get_in_tkt.c,
+ * which is duplicated from somewhere else. :-/
+ * XXX
+ */
+static const char *const conf_yes[] = {
+ "y", "yes", "true", "t", "1", "on",
+ 0,
+};
+
+static const char *const conf_no[] = {
+ "n", "no", "false", "nil", "0", "off",
+ 0,
+};
+
+static int
+_krb5_conf_boolean(const char *s)
+{
+ const char *const *p;
+
+ for(p=conf_yes; *p; p++) {
+ if (strcasecmp(*p,s) == 0)
+ return 1;
+ }
+
+ for(p=conf_no; *p; p++) {
+ if (strcasecmp(*p,s) == 0)
+ return 0;
+ }
+
+ /* Default to "no" */
+ return 0;
+}
+
+/*
+ * XXX
+ * End duplicated code from src/lib/krb5/krb/get_in_tkt.c
+ * XXX
+ */
+
+/*
+ * The following are based on krb5_libdefault_* functions in
+ * src/lib/krb5/krb/get_in_tkt.c
+ * N.B. This assumes that context->default_realm has
+ * already been established.
+ */
+krb5_error_code
+pkinit_kdcdefault_strings(krb5_context context, const char *realmname,
+ const char *option, char ***ret_value)
+{
+ profile_t profile = NULL;
+ const char *names[5];
+ char **values = NULL;
+ krb5_error_code retval;
+
+ if (context == NULL)
+ return KV5M_CONTEXT;
+
+ profile = context->profile;
+
+ if (realmname != NULL) {
+ /*
+ * Try number one:
+ *
+ * [realms]
+ * REALM = {
+ * option = <value>
+ * }
+ */
+
+ names[0] = "realms";
+ names[1] = realmname;
+ names[2] = option;
+ names[3] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL)
+ goto goodbye;
+ }
+
+ /*
+ * Try number two:
+ *
+ * [kdcdefaults]
+ * option = <value>
+ */
+
+ names[0] = "kdcdefaults";
+ names[1] = option;
+ names[2] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL)
+ goto goodbye;
+
+goodbye:
+ if (values == NULL)
+ retval = ENOENT;
+
+ *ret_value = values;
+
+ return retval;
+
+}
+
+krb5_error_code
+pkinit_kdcdefault_string(krb5_context context, const char *realmname,
+ const char *option, char **ret_value)
+{
+ krb5_error_code retval;
+ char **values = NULL;
+
+ retval = pkinit_kdcdefault_strings(context, realmname, option, &values);
+ if (retval)
+ return retval;
+
+ if (values[0] == NULL) {
+ retval = ENOENT;
+ } else {
+ *ret_value = malloc(strlen(values[0]) + 1);
+ if (*ret_value == NULL)
+ retval = ENOMEM;
+ else
+ strcpy(*ret_value, values[0]);
+ }
+
+ profile_free_list(values);
+ return retval;
+}
+
+krb5_error_code
+pkinit_kdcdefault_boolean(krb5_context context, const char *realmname,
+ const char *option, int default_value, int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_kdcdefault_string(context, realmname, option, &string);
+
+ if (retval == 0) {
+ *ret_value = _krb5_conf_boolean(string);
+ free(string);
+ } else
+ *ret_value = default_value;
+
+ return 0;
+}
+
+krb5_error_code
+pkinit_kdcdefault_integer(krb5_context context, const char *realmname,
+ const char *option, int default_value, int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_kdcdefault_string(context, realmname, option, &string);
+
+ if (retval == 0) {
+ char *endptr;
+ long l;
+ l = strtol(string, &endptr, 0);
+ if (endptr == string)
+ *ret_value = default_value;
+ else
+ *ret_value = l;
+ free(string);
+ } else
+ *ret_value = default_value;
+
+ return 0;
+}
+
+
+/*
+ * krb5_libdefault_string() is defined as static in
+ * src/lib/krb5/krb/get_in_tkt.c. Create local versions of
+ * krb5_libdefault_* functions here. We need a libdefaults_strings()
+ * function which is not currently supported there anyway. Also,
+ * add the ability to supply a default value for the boolean and
+ * integer functions.
+ */
+
+krb5_error_code
+pkinit_libdefault_strings(krb5_context context, const krb5_data *realm,
+ const char *option, char ***ret_value)
+{
+ profile_t profile;
+ const char *names[5];
+ char **values = NULL;
+ krb5_error_code retval;
+ char realmstr[1024];
+
+ if (realm != NULL && realm->length > sizeof(realmstr)-1)
+ return EINVAL;
+
+ if (realm != NULL) {
+ strncpy(realmstr, realm->data, realm->length);
+ realmstr[realm->length] = '\0';
+ }
+
+ if (!context || (context->magic != KV5M_CONTEXT))
+ return KV5M_CONTEXT;
+
+ profile = context->profile;
+
+
+ if (realm != NULL) {
+ /*
+ * Try number one:
+ *
+ * [libdefaults]
+ * REALM = {
+ * option = <value>
+ * }
+ */
+
+ 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;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL && values[0] != NULL)
+ goto goodbye;
+ }
+
+ /*
+ * Try number three:
+ *
+ * [libdefaults]
+ * option = <value>
+ */
+
+ names[0] = "libdefaults";
+ names[1] = option;
+ names[2] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL && values[0] != NULL)
+ goto goodbye;
+
+goodbye:
+ if (values == NULL)
+ return ENOENT;
+
+ *ret_value = values;
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_libdefault_string(krb5_context context, const krb5_data *realm,
+ const char *option, char **ret_value)
+{
+ krb5_error_code retval;
+ char **values = NULL;
+
+ retval = pkinit_libdefault_strings(context, realm, option, &values);
+ if (retval)
+ return retval;
+
+ if (values[0] == NULL) {
+ retval = ENOENT;
+ } else {
+ *ret_value = malloc(strlen(values[0]) + 1);
+ if (*ret_value == NULL)
+ retval = ENOMEM;
+ else
+ strcpy(*ret_value, values[0]);
+ }
+
+ profile_free_list(values);
+ return retval;
+}
+
+krb5_error_code
+pkinit_libdefault_boolean(krb5_context context, const krb5_data *realm,
+ const char *option, int default_value,
+ int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_libdefault_string(context, realm, option, &string);
+
+ if (retval == 0) {
+ *ret_value = _krb5_conf_boolean(string);
+ free(string);
+ } else
+ *ret_value = default_value;
+
+ return 0;
+}
+
+krb5_error_code
+pkinit_libdefault_integer(krb5_context context, const krb5_data *realm,
+ const char *option, int default_value,
+ int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_libdefault_string(context, realm, option, &string);
+
+ if (retval == 0) {
+ char *endptr;
+ long l;
+ l = strtol(string, &endptr, 0);
+ if (endptr == string)
+ *ret_value = default_value;
+ else
+ *ret_value = l;
+ free(string);
+ }
+
+ return retval;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
new file mode 100644
index 0000000..595a3d0
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -0,0 +1,1399 @@
+/*
+ * COPYRIGHT (C) 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "pkinit.h"
+
+static krb5_error_code
+pkinit_server_get_edata(krb5_context context,
+ krb5_kdc_req * request,
+ struct _krb5_db_entry_new * client,
+ struct _krb5_db_entry_new * server,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ krb5_pa_data * data);
+
+static krb5_error_code
+pkinit_server_verify_padata(krb5_context context,
+ struct _krb5_db_entry_new * client,
+ krb5_data *req_pkt,
+ krb5_kdc_req * request,
+ krb5_enc_tkt_part * enc_tkt_reply,
+ krb5_pa_data * data,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context,
+ krb5_data **e_data,
+ krb5_authdata ***authz_data);
+
+static krb5_error_code
+pkinit_server_return_padata(krb5_context context,
+ krb5_pa_data * padata,
+ struct _krb5_db_entry_new * client,
+ krb5_data *req_pkt,
+ krb5_kdc_req * request,
+ krb5_kdc_rep * reply,
+ struct _krb5_key_data * client_key,
+ krb5_keyblock * encrypting_key,
+ krb5_pa_data ** send_pa,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context);
+
+static int pkinit_server_get_flags
+ (krb5_context kcontext, krb5_preauthtype patype);
+
+static krb5_error_code pkinit_init_kdc_req_context
+ (krb5_context, void **blob);
+
+static void pkinit_fini_kdc_req_context
+ (krb5_context context, void *blob);
+
+static int pkinit_server_plugin_init_realm
+ (krb5_context context, const char *realmname,
+ pkinit_kdc_context *pplgctx);
+
+static void pkinit_server_plugin_fini_realm
+ (krb5_context context, pkinit_kdc_context plgctx);
+
+static int pkinit_server_plugin_init
+ (krb5_context context, void **blob, const char **realmnames);
+
+static void pkinit_server_plugin_fini
+ (krb5_context context, void *blob);
+
+static pkinit_kdc_context pkinit_find_realm_context
+ (krb5_context context, void *pa_plugin_context, krb5_principal princ);
+
+static krb5_error_code
+pkinit_create_edata(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ pkinit_plg_opts *opts,
+ krb5_error_code err_code,
+ krb5_data **e_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+
+ pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
+ err_code, error_message(err_code));
+ switch(err_code) {
+ case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
+ retval = pkinit_create_td_trusted_certifiers(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
+ break;
+ case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
+ retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx, opts, e_data);
+ break;
+ case KRB5KDC_ERR_INVALID_CERTIFICATE:
+ case KRB5KDC_ERR_REVOKED_CERTIFICATE:
+ retval = pkinit_create_td_invalid_certificate(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
+ break;
+ default:
+ pkiDebug("no edata needed for error %d (%s)\n",
+ err_code, error_message(err_code));
+ retval = 0;
+ goto cleanup;
+ }
+
+cleanup:
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_server_get_edata(krb5_context context,
+ krb5_kdc_req * request,
+ struct _krb5_db_entry_new * client,
+ struct _krb5_db_entry_new * server,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ krb5_pa_data * data)
+{
+ krb5_error_code retval = 0;
+ pkinit_kdc_context plgctx = NULL;
+
+ pkiDebug("pkinit_server_get_edata: entered!\n");
+
+ /*
+ * If we don't have a realm context for the given realm,
+ * don't tell the client that we support pkinit!
+ */
+ plgctx = pkinit_find_realm_context(context, pa_plugin_context,
+ request->server);
+ if (plgctx == NULL)
+ retval = EINVAL;
+
+ return retval;
+}
+
+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;
+#ifdef DEBUG_SAN_INFO
+ char *client_string = NULL, *san_string;
+#endif
+
+ 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
+
+#ifdef DEBUG_SAN_INFO
+ krb5_unparse_name(context, client, &client_string);
+#endif
+ pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
+ for (i = 0; princs != NULL && princs[i] != NULL; i++) {
+#ifdef DEBUG_SAN_INFO
+ krb5_unparse_name(context, princs[i], &san_string);
+ pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
+ __FUNCTION__, client_string, san_string);
+ krb5_free_unparsed_name(context, san_string);
+#endif
+ 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++) {
+#ifdef DEBUG_SAN_INFO
+ krb5_unparse_name(context, upns[i], &san_string);
+ pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
+ __FUNCTION__, client_string, san_string);
+ krb5_free_unparsed_name(context, san_string);
+#endif
+ 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);
+ }
+#ifdef DEBUG_SAN_INFO
+ if (client_string != NULL)
+ krb5_free_unparsed_name(context, client_string);
+#endif
+ 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,
+ krb5_kdc_req * request,
+ krb5_enc_tkt_part * enc_tkt_reply,
+ krb5_pa_data * data,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context,
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
+{
+ krb5_error_code retval = 0;
+ krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
+ krb5_data *encoded_pkinit_authz_data = NULL;
+ krb5_pa_pk_as_req *reqp = NULL;
+ krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
+ krb5_auth_pack *auth_pack = NULL;
+ krb5_auth_pack_draft9 *auth_pack9 = NULL;
+ pkinit_kdc_context plgctx = NULL;
+ pkinit_kdc_req_context reqctx;
+ krb5_preauthtype pa_type;
+ krb5_checksum cksum = {0, 0, 0, NULL};
+ krb5_data *der_req = NULL;
+ int valid_eku = 0, valid_san = 0;
+ krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL;
+ krb5_kdc_req *tmp_as_req = NULL;
+ krb5_data k5data;
+
+ pkiDebug("pkinit_verify_padata: entered!\n");
+ if (data == NULL || data->length <= 0 || data->contents == NULL)
+ return 0;
+
+ if (pa_plugin_context == NULL || e_data == NULL)
+ return EINVAL;
+
+ plgctx = pkinit_find_realm_context(context, pa_plugin_context,
+ request->server);
+ if (plgctx == NULL)
+ return 0;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
+#endif
+ /* create a per-request context */
+ retval = pkinit_init_kdc_req_context(context, (void **)&reqctx);
+ if (retval)
+ goto cleanup;
+ reqctx->pa_type = data->pa_type;
+
+ PADATA_TO_KRB5DATA(data, &k5data);
+
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
+ pa_type = (int)data->pa_type;
+ retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(reqp->signedAuthPack.data,
+ reqp->signedAuthPack.length,
+ "/tmp/kdc_signed_data");
+#endif
+ retval = cms_signeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
+ plgctx->opts->require_crl_checking,
+ reqp->signedAuthPack.data, reqp->signedAuthPack.length,
+ &authp_data.data, &authp_data.length, &krb5_authz.data,
+ &krb5_authz.length);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
+ pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
+ retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(reqp9->signedAuthPack.data,
+ reqp9->signedAuthPack.length,
+ "/tmp/kdc_signed_data_draft9");
+#endif
+
+ retval = cms_signeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
+ plgctx->opts->require_crl_checking,
+ reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
+ &authp_data.data, &authp_data.length, &krb5_authz.data,
+ &krb5_authz.length);
+ break;
+ default:
+ pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
+ retval = EINVAL;
+ goto cleanup;
+ }
+ if (retval) {
+ pkiDebug("pkcs7_signeddata_verify failed\n");
+ goto cleanup;
+ }
+
+ retval = verify_client_san(context, plgctx, reqctx, request->client,
+ &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;
+
+ if (!valid_eku) {
+ pkiDebug("%s: did not find an acceptable EKU in user certificate\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
+ goto cleanup;
+ }
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
+#endif
+
+ OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack\n");
+ goto cleanup;
+ }
+
+ /* check dh parameters */
+ if (auth_pack->clientPublicValue != NULL) {
+ retval = server_check_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ &auth_pack->clientPublicValue->algorithm.parameters,
+ plgctx->opts->dh_min_bits);
+
+ if (retval) {
+ pkiDebug("bad dh parameters\n");
+ goto cleanup;
+ }
+ }
+ /*
+ * The KDC may have modified the request after decoding it.
+ * We need to compute the checksum on the data that
+ * came from the client. Therefore, we use the original
+ * packet contents.
+ */
+ 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 = 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;
+ }
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
+ 0, der_req, &cksum);
+ if (retval) {
+ pkiDebug("unable to calculate AS REQ checksum\n");
+ goto cleanup;
+ }
+ if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
+ memcmp(cksum.contents,
+ auth_pack->pkAuthenticator.paChecksum.contents,
+ cksum.length)) {
+ pkiDebug("failed to match the checksum\n");
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size (%d)\n",
+ req_pkt->length);
+ print_buffer(req_pkt->data, req_pkt->length);
+ pkiDebug("received checksum type=%d size=%d ",
+ auth_pack->pkAuthenticator.paChecksum.checksum_type,
+ auth_pack->pkAuthenticator.paChecksum.length);
+ print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
+ auth_pack->pkAuthenticator.paChecksum.length);
+ pkiDebug("expected checksum type=%d size=%d ",
+ cksum.checksum_type, cksum.length);
+ print_buffer(cksum.contents, cksum.length);
+#endif
+
+ retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
+ goto cleanup;
+ }
+
+ /* check if kdcPkId present and match KDC's subjectIdentifier */
+ if (reqp->kdcPkId.data != NULL) {
+ int valid_kdcPkId = 0;
+ retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId);
+ if (retval)
+ goto cleanup;
+ if (!valid_kdcPkId)
+ pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
+ "RFC says to ignore and proceed\n");
+
+ }
+ /* remember the decoded auth_pack for verify_padata routine */
+ reqctx->rcv_auth_pack = auth_pack;
+ auth_pack = NULL;
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack_draft9\n");
+ goto cleanup;
+ }
+ if (auth_pack9->clientPublicValue != NULL) {
+ retval = server_check_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ &auth_pack9->clientPublicValue->algorithm.parameters,
+ plgctx->opts->dh_min_bits);
+
+ if (retval) {
+ pkiDebug("bad dh parameters\n");
+ goto cleanup;
+ }
+ }
+ /* remember the decoded auth_pack for verify_padata routine */
+ reqctx->rcv_auth_pack9 = auth_pack9;
+ auth_pack9 = NULL;
+ break;
+ }
+
+ /* return authorization data to be included in the ticket */
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ 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;
+ }
+ /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */
+ my_authz_data[0]->magic = KV5M_AUTHDATA;
+ my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+
+ /* create an internal AD-INITIAL-VERIFIED-CAS data */
+ pkinit_authz_data = malloc(sizeof(krb5_authdata));
+ if (pkinit_authz_data == NULL) {
+ retval = ENOMEM;
+ pkiDebug("Couldn't allocate krb5_authdata\n");
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ goto cleanup;
+ }
+ pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS;
+ /* content of this ad-type contains the certification
+ path with which the client certificate was validated
+ */
+ pkinit_authz_data->contents = krb5_authz.data;
+ pkinit_authz_data->length = krb5_authz.length;
+ retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data,
+ &encoded_pkinit_authz_data);
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data,
+ encoded_pkinit_authz_data->length,
+ "/tmp/kdc_pkinit_authz_data");
+#endif
+ free(pkinit_authz_data);
+ if (retval) {
+ pkiDebug("k5int_encode_krb5_authdata_elt failed\n");
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ goto cleanup;
+ }
+
+ my_authz_data[0]->contents =
+ (krb5_octet *) encoded_pkinit_authz_data->data;
+ my_authz_data[0]->length = encoded_pkinit_authz_data->length;
+ *authz_data = my_authz_data;
+ pkiDebug("Returning %d bytes of authorization data\n",
+ krb5_authz.length);
+ encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/
+ free(encoded_pkinit_authz_data);
+ break;
+ default:
+ *authz_data = NULL;
+ }
+ /* remember to set the PREAUTH flag in the reply */
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+ *pa_request_context = reqctx;
+ reqctx = NULL;
+
+ cleanup:
+ if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
+ pkiDebug("pkinit_verify_padata failed: creating e-data\n");
+ if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
+ plgctx->idctx, plgctx->opts, retval, e_data))
+ pkiDebug("pkinit_create_edata failed\n");
+ }
+
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_pa_pk_as_req(&reqp);
+ if (cksum.contents != NULL)
+ free(cksum.contents);
+ if (der_req != NULL)
+ krb5_free_data(context, der_req);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ free_krb5_pa_pk_as_req_draft9(&reqp9);
+ }
+ if (tmp_as_req != NULL)
+ k5int_krb5_free_kdc_req(context, tmp_as_req);
+ if (authp_data.data != NULL)
+ free(authp_data.data);
+ if (krb5_authz.data != NULL)
+ free(krb5_authz.data);
+ if (reqctx != NULL)
+ pkinit_fini_kdc_req_context(context, reqctx);
+ if (auth_pack != NULL)
+ free_krb5_auth_pack(&auth_pack);
+ if (auth_pack9 != NULL)
+ free_krb5_auth_pack_draft9(context, &auth_pack9);
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_server_return_padata(krb5_context context,
+ krb5_pa_data * padata,
+ struct _krb5_db_entry_new * client,
+ krb5_data *req_pkt,
+ krb5_kdc_req * request,
+ krb5_kdc_rep * reply,
+ struct _krb5_key_data * client_key,
+ krb5_keyblock * encrypting_key,
+ krb5_pa_data ** send_pa,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context)
+{
+ krb5_error_code retval = 0;
+ krb5_data scratch = {0, 0, NULL};
+ krb5_pa_pk_as_req *reqp = NULL;
+ krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
+ int i = 0;
+
+ unsigned char *subjectPublicKey = NULL;
+ unsigned char *dh_pubkey = NULL, *server_key = NULL;
+ unsigned int subjectPublicKey_len = 0;
+ unsigned int server_key_len = 0, dh_pubkey_len = 0;
+
+ krb5_kdc_dh_key_info dhkey_info;
+ krb5_data *encoded_dhkey_info = NULL;
+ krb5_pa_pk_as_rep *rep = NULL;
+ krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
+ krb5_data *out_data = NULL;
+
+ krb5_enctype enctype = -1;
+
+ krb5_reply_key_pack *key_pack = NULL;
+ krb5_reply_key_pack_draft9 *key_pack9 = NULL;
+ krb5_data *encoded_key_pack = NULL;
+ unsigned int num_types;
+ krb5_cksumtype *cksum_types = NULL;
+
+ pkinit_kdc_context plgctx;
+ pkinit_kdc_req_context reqctx;
+
+ int fixed_keypack = 0;
+
+ *send_pa = NULL;
+ if (padata == NULL || padata->length <= 0 || padata->contents == NULL)
+ return 0;
+
+ if (pa_request_context == NULL || *pa_request_context == NULL) {
+ pkiDebug("missing request context \n");
+ return EINVAL;
+ }
+
+ plgctx = pkinit_find_realm_context(context, pa_plugin_context,
+ request->server);
+ if (plgctx == NULL) {
+ pkiDebug("Unable to locate correct realm context\n");
+ return ENOENT;
+ }
+
+ pkiDebug("pkinit_return_padata: entered!\n");
+ reqctx = (pkinit_kdc_req_context)*pa_request_context;
+
+ if (encrypting_key->contents) {
+ free(encrypting_key->contents);
+ encrypting_key->length = 0;
+ encrypting_key->contents = NULL;
+ }
+
+ for(i = 0; i < request->nktypes; i++) {
+ enctype = request->ktype[i];
+ if (!krb5_c_valid_enctype(enctype))
+ continue;
+ else {
+ pkiDebug("KDC picked etype = %d\n", enctype);
+ break;
+ }
+ }
+
+ if (i == request->nktypes) {
+ retval = KRB5KDC_ERR_ETYPE_NOSUPP;
+ goto cleanup;
+ }
+
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ init_krb5_pa_pk_as_rep(&rep);
+ if (rep == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ /* let's assume it's RSA. we'll reset it to DH if needed */
+ rep->choice = choice_pa_pk_as_rep_encKeyPack;
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ init_krb5_pa_pk_as_rep_draft9(&rep9);
+ if (rep9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+ break;
+ default:
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+
+ if (reqctx->rcv_auth_pack != NULL &&
+ reqctx->rcv_auth_pack->clientPublicValue != NULL) {
+ subjectPublicKey =
+ reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data;
+ subjectPublicKey_len =
+ reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length;
+ rep->choice = choice_pa_pk_as_rep_dhInfo;
+ } else if (reqctx->rcv_auth_pack9 != NULL &&
+ reqctx->rcv_auth_pack9->clientPublicValue != NULL) {
+ subjectPublicKey =
+ reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data;
+ subjectPublicKey_len =
+ reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length;
+ rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
+ }
+
+ /* if this DH, then process finish computing DH key */
+ if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo ||
+ rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) {
+ pkiDebug("received DH key delivery AS REQ\n");
+ retval = server_process_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
+ subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len,
+ &server_key, &server_key_len);
+ if (retval) {
+ pkiDebug("failed to process/create dh paramters\n");
+ goto cleanup;
+ }
+ }
+
+ if ((rep9 != NULL &&
+ rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
+ (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
+ retval = pkinit_octetstring2key(context, enctype, server_key,
+ server_key_len, encrypting_key);
+ if (retval) {
+ pkiDebug("pkinit_octetstring2key failed: %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+
+ dhkey_info.subjectPublicKey.length = dh_pubkey_len;
+ dhkey_info.subjectPublicKey.data = dh_pubkey;
+ dhkey_info.nonce = request->nonce;
+ dhkey_info.dhKeyExpiration = 0;
+
+ retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
+ &encoded_dhkey_info);
+ if (retval) {
+ pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
+ encoded_dhkey_info->length,
+ "/tmp/kdc_dh_key_info");
+#endif
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
+ (unsigned char *)encoded_dhkey_info->data,
+ encoded_dhkey_info->length,
+ &rep->u.dh_Info.dhSignedData.data,
+ &rep->u.dh_Info.dhSignedData.length);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
+ (unsigned char *)encoded_dhkey_info->data,
+ encoded_dhkey_info->length,
+ &rep9->u.dhSignedData.data,
+ &rep9->u.dhSignedData.length);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+ break;
+ }
+ } else {
+ pkiDebug("received RSA key delivery AS REQ\n");
+
+ retval = krb5_c_make_random_key(context, enctype, encrypting_key);
+ if (retval) {
+ pkiDebug("unable to make a session key\n");
+ goto cleanup;
+ }
+
+ /* check if PA_TYPE of 132 is present which means the client is
+ * requesting that a checksum is send back instead of the nonce
+ */
+ for (i = 0; request->padata[i] != NULL; i++) {
+ pkiDebug("%s: Checking pa_type 0x%08x\n",
+ __FUNCTION__, request->padata[i]->pa_type);
+ if (request->padata[i]->pa_type == 132)
+ fixed_keypack = 1;
+ }
+ pkiDebug("%s: return checksum instead of nonce = %d\n",
+ __FUNCTION__, fixed_keypack);
+
+ /* if this is an RFC reply or draft9 client requested a checksum
+ * in the reply instead of the nonce, create an RFC-style keypack
+ */
+ if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
+ init_krb5_reply_key_pack(&key_pack);
+ if (key_pack == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ /* retrieve checksums for a given enctype of the reply key */
+ retval = krb5_c_keyed_checksum_types(context,
+ encrypting_key->enctype, &num_types, &cksum_types);
+ if (retval)
+ goto cleanup;
+
+ /* pick the first of acceptable enctypes for the checksum */
+ retval = krb5_c_make_checksum(context, cksum_types[0],
+ encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ req_pkt, &key_pack->asChecksum);
+ if (retval) {
+ pkiDebug("unable to calculate AS REQ checksum\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
+ print_buffer(req_pkt->data, req_pkt->length);
+ pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
+ print_buffer(key_pack->asChecksum.contents,
+ key_pack->asChecksum.length);
+ pkiDebug("encrypting key (%d)\n", encrypting_key->length);
+ print_buffer(encrypting_key->contents, encrypting_key->length);
+#endif
+
+ krb5_copy_keyblock_contents(context, encrypting_key,
+ &key_pack->replyKey);
+
+ retval = k5int_encode_krb5_reply_key_pack(key_pack,
+ &encoded_key_pack);
+ if (retval) {
+ pkiDebug("failed to encode reply_key_pack\n");
+ goto cleanup;
+ }
+ }
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ rep->choice = choice_pa_pk_as_rep_encKeyPack;
+ retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
+ (unsigned char *)encoded_key_pack->data,
+ encoded_key_pack->length,
+ &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ /* if the request is from the broken draft9 client that
+ * expects back a nonce, create it now
+ */
+ if (!fixed_keypack) {
+ init_krb5_reply_key_pack_draft9(&key_pack9);
+ if (key_pack9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
+ krb5_copy_keyblock_contents(context, encrypting_key,
+ &key_pack9->replyKey);
+
+ retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
+ &encoded_key_pack);
+ if (retval) {
+ pkiDebug("failed to encode reply_key_pack\n");
+ goto cleanup;
+ }
+ }
+
+ rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+ retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
+ (unsigned char *)encoded_key_pack->data,
+ encoded_key_pack->length,
+ &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
+ break;
+ }
+ if (retval) {
+ pkiDebug("failed to create pkcs7 enveloped data: %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_key_pack->data,
+ encoded_key_pack->length,
+ "/tmp/kdc_key_pack");
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ print_buffer_bin(rep->u.encKeyPack.data,
+ rep->u.encKeyPack.length,
+ "/tmp/kdc_enc_key_pack");
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ print_buffer_bin(rep9->u.encKeyPack.data,
+ rep9->u.encKeyPack.length,
+ "/tmp/kdc_enc_key_pack");
+ break;
+ }
+#endif
+ }
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
+ break;
+ }
+ if (retval) {
+ pkiDebug("failed to encode AS_REP\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ if (out_data != NULL)
+ print_buffer_bin((unsigned char *)out_data->data, out_data->length,
+ "/tmp/kdc_as_rep");
+#endif
+
+ *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
+ if (*send_pa == NULL) {
+ retval = ENOMEM;
+ free(out_data->data);
+ free(out_data);
+ out_data = NULL;
+ goto cleanup;
+ }
+ (*send_pa)->magic = KV5M_PA_DATA;
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ break;
+ }
+ (*send_pa)->length = out_data->length;
+ (*send_pa)->contents = (krb5_octet *) out_data->data;
+
+
+ cleanup:
+ pkinit_fini_kdc_req_context(context, reqctx);
+ if (scratch.data != NULL)
+ free(scratch.data);
+ if (out_data != NULL)
+ free(out_data);
+ if (encoded_dhkey_info != NULL)
+ krb5_free_data(context, encoded_dhkey_info);
+ if (encoded_key_pack != NULL)
+ krb5_free_data(context, encoded_key_pack);
+ if (dh_pubkey != NULL)
+ free(dh_pubkey);
+ if (server_key != NULL)
+ free(server_key);
+ if (cksum_types != NULL)
+ free(cksum_types);
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_pa_pk_as_req(&reqp);
+ free_krb5_pa_pk_as_rep(&rep);
+ free_krb5_reply_key_pack(&key_pack);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ free_krb5_pa_pk_as_req_draft9(&reqp9);
+ free_krb5_pa_pk_as_rep_draft9(&rep9);
+ if (!fixed_keypack)
+ free_krb5_reply_key_pack_draft9(&key_pack9);
+ else
+ free_krb5_reply_key_pack(&key_pack);
+ break;
+ }
+
+ if (retval)
+ pkiDebug("pkinit_verify_padata failure");
+
+ return retval;
+}
+
+static int
+pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
+{
+ return PA_SUFFICIENT | PA_REPLACES_KEY;
+}
+
+static krb5_preauthtype supported_server_pa_types[] = {
+ KRB5_PADATA_PK_AS_REQ,
+ KRB5_PADATA_PK_AS_REQ_OLD,
+ KRB5_PADATA_PK_AS_REP_OLD,
+ 0
+};
+
+static void
+pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
+{
+ /*
+ * There is nothing currently allocated by pkinit_init_kdc_profile()
+ * which needs to be freed here.
+ */
+}
+
+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,
+ "pkinit_identity",
+ &plgctx->idopts->identity);
+ if (retval != 0 || NULL == plgctx->idopts->identity) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval,
+ "No pkinit_identity supplied for realm %s",
+ plgctx->realmname);
+ goto errout;
+ }
+
+ retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
+ "pkinit_anchors",
+ &plgctx->idopts->anchors);
+ if (retval != 0 || NULL == plgctx->idopts->anchors) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval,
+ "No pkinit_anchors supplied for realm %s",
+ plgctx->realmname);
+ goto errout;
+ }
+
+ pkinit_kdcdefault_strings(context, plgctx->realmname,
+ "pkinit_pool",
+ &plgctx->idopts->intermediates);
+
+ pkinit_kdcdefault_strings(context, plgctx->realmname,
+ "pkinit_revoke",
+ &plgctx->idopts->crls);
+
+ pkinit_kdcdefault_string(context, plgctx->realmname,
+ "pkinit_kdc_ocsp",
+ &plgctx->idopts->ocsp);
+
+ pkinit_kdcdefault_string(context, plgctx->realmname,
+ "pkinit_mappings_file",
+ &plgctx->idopts->dn_mapping_file);
+
+ pkinit_kdcdefault_integer(context, plgctx->realmname,
+ "pkinit_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);
+ 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);
+ return retval;
+}
+
+static pkinit_kdc_context
+pkinit_find_realm_context(krb5_context context, void *pa_plugin_context,
+ krb5_principal princ)
+{
+ int i;
+ pkinit_kdc_context *realm_contexts = pa_plugin_context;
+
+ if (pa_plugin_context == NULL)
+ return NULL;
+
+ for (i = 0; realm_contexts[i] != NULL; i++) {
+ pkinit_kdc_context p = realm_contexts[i];
+
+ if ((p->realmname_len == princ->realm.length) &&
+ (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
+ pkiDebug("%s: returning context at %p for realm '%s'\n",
+ __FUNCTION__, p, p->realmname);
+ return p;
+ }
+ }
+ pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
+ __FUNCTION__, princ->realm.length, princ->realm.data);
+ return NULL;
+}
+
+static int
+pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
+ pkinit_kdc_context *pplgctx)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_kdc_context plgctx = NULL;
+
+ *pplgctx = NULL;
+
+ plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx));
+ if (plgctx == NULL)
+ goto errout;
+
+ pkiDebug("%s: initializing context at %p for realm '%s'\n",
+ __FUNCTION__, plgctx, realmname);
+ memset(plgctx, 0, sizeof(*plgctx));
+ plgctx->magic = PKINIT_CTX_MAGIC;
+
+ plgctx->realmname = strdup(realmname);
+ if (plgctx->realmname == NULL)
+ goto errout;
+ plgctx->realmname_len = strlen(plgctx->realmname);
+
+ retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_plg_opts(&plgctx->opts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_identity_crypto(&plgctx->idctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_identity_opts(&plgctx->idopts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_kdc_profile(context, plgctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
+ plgctx->idopts, plgctx->idctx, 0, NULL);
+ if (retval)
+ goto errout;
+
+ pkiDebug("%s: returning context at %p for realm '%s'\n",
+ __FUNCTION__, plgctx, realmname);
+ *pplgctx = plgctx;
+ retval = 0;
+
+errout:
+ if (retval)
+ pkinit_server_plugin_fini_realm(context, plgctx);
+
+ return retval;
+}
+
+static int
+pkinit_server_plugin_init(krb5_context context, void **blob,
+ const char **realmnames)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_kdc_context plgctx, *realm_contexts = NULL;
+ int i, j;
+ size_t numrealms;
+
+ retval = pkinit_accessor_init();
+ if (retval)
+ return retval;
+
+ /* Determine how many realms we may need to support */
+ for (i = 0; realmnames[i] != NULL; i++) {};
+ numrealms = i;
+
+ realm_contexts = (pkinit_kdc_context *)
+ calloc(numrealms+1, sizeof(pkinit_kdc_context));
+ if (realm_contexts == NULL)
+ return ENOMEM;
+
+ for (i = 0, j = 0; i < numrealms; i++) {
+ pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]);
+ retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx);
+ if (retval == 0 && plgctx != NULL)
+ realm_contexts[j++] = plgctx;
+ }
+
+ if (j == 0) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval, "No realms configured "
+ "correctly for pkinit support");
+ goto errout;
+ }
+
+ *blob = realm_contexts;
+ retval = 0;
+ pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts);
+
+errout:
+ if (retval)
+ pkinit_server_plugin_fini(context, realm_contexts);
+
+ return retval;
+}
+
+static void
+pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
+{
+ if (plgctx == NULL)
+ return;
+
+ pkinit_fini_kdc_profile(context, plgctx);
+ pkinit_fini_identity_opts(plgctx->idopts);
+ pkinit_fini_identity_crypto(plgctx->idctx);
+ pkinit_fini_plg_crypto(plgctx->cryptoctx);
+ pkinit_fini_plg_opts(plgctx->opts);
+ free(plgctx->realmname);
+ free(plgctx);
+}
+
+static void
+pkinit_server_plugin_fini(krb5_context context, void *blob)
+{
+ pkinit_kdc_context *realm_contexts = blob;
+ int i;
+
+ if (realm_contexts == NULL)
+ return;
+
+ for (i = 0; realm_contexts[i] != NULL; i++) {
+ pkinit_server_plugin_fini_realm(context, realm_contexts[i]);
+ }
+ pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts);
+ free(realm_contexts);
+}
+
+static krb5_error_code
+pkinit_init_kdc_req_context(krb5_context context, void **ctx)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_kdc_req_context reqctx = NULL;
+
+ reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx));
+ if (reqctx == NULL)
+ return retval;
+ memset(reqctx, 0, sizeof(*reqctx));
+ reqctx->magic = PKINIT_CTX_MAGIC;
+
+ retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
+ if (retval)
+ goto cleanup;
+ reqctx->rcv_auth_pack = NULL;
+ reqctx->rcv_auth_pack9 = NULL;
+
+ pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
+ *ctx = reqctx;
+ retval = 0;
+cleanup:
+ if (retval)
+ pkinit_fini_kdc_req_context(context, reqctx);
+
+ return retval;
+}
+
+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("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);
+
+ pkinit_fini_req_crypto(reqctx->cryptoctx);
+ if (reqctx->rcv_auth_pack != NULL)
+ free_krb5_auth_pack(&reqctx->rcv_auth_pack);
+ if (reqctx->rcv_auth_pack9 != NULL)
+ free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9);
+
+ free(reqctx);
+}
+
+struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
+ "pkinit", /* name */
+ supported_server_pa_types, /* pa_type_list */
+ pkinit_server_plugin_init, /* (*init_proc) */
+ pkinit_server_plugin_fini, /* (*fini_proc) */
+ pkinit_server_get_flags, /* (*flags_proc) */
+ pkinit_server_get_edata, /* (*edata_proc) */
+ pkinit_server_verify_padata,/* (*verify_proc) */
+ pkinit_server_return_padata,/* (*return_proc) */
+ NULL, /* (*freepa_reqcontext_proc) */
+};
diff --git a/src/plugins/preauth/wpse/wpse_main.c b/src/plugins/preauth/wpse/wpse_main.c
index fd6ff92..22dfd13 100644
--- a/src/plugins/preauth/wpse/wpse_main.c
+++ b/src/plugins/preauth/wpse/wpse_main.c
@@ -30,7 +30,7 @@
/* Worst. Preauthentication. Scheme. Ever. */
-#ident "$Id$"
+#ident "$Id: wpse_main.c,v 1.3 2007/01/02 22:33:51 kwc Exp $"
#include "autoconf.h"
@@ -101,9 +101,9 @@ client_process(krb5_context kcontext,
void *gak_data,
krb5_data *salt, krb5_data *s2kparams,
krb5_keyblock *as_key,
- krb5_pa_data **out_pa_data)
+ krb5_pa_data ***out_pa_data)
{
- krb5_pa_data *send_pa;
+ krb5_pa_data **send_pa;
krb5_int32 nnonce, enctype;
krb5_keyblock *kb;
krb5_error_code status;
@@ -121,19 +121,26 @@ client_process(krb5_context kcontext,
if (pa_data->length == 0) {
/* Create preauth data. */
- send_pa = malloc(sizeof(krb5_pa_data));
+ send_pa = malloc(2 * sizeof(krb5_pa_data *));
if (send_pa == NULL)
return ENOMEM;
- send_pa->pa_type = KRB5_PADATA_WPSE_REQ;
- send_pa->length = 4;
- send_pa->contents = malloc(4);
- if (send_pa->contents == NULL) {
+ send_pa[1] = NULL; /* Terminate list */
+ send_pa[0] = malloc(sizeof(krb5_pa_data));
+ if (send_pa[0] == NULL) {
+ free(send_pa);
+ return ENOMEM;
+ }
+ send_pa[0]->pa_type = KRB5_PADATA_WPSE_REQ;
+ send_pa[0]->length = 4;
+ send_pa[0]->contents = malloc(4);
+ if (send_pa[0]->contents == NULL) {
+ free(send_pa[0]);
free(send_pa);
return ENOMEM;
}
/* Store the preauth data. */
nnonce = htonl(request->nonce);
- memcpy(send_pa->contents, &nnonce, 4);
+ memcpy(send_pa[0]->contents, &nnonce, 4);
*out_pa_data = send_pa;
} else {
/* A reply from the KDC. Conventionally this would be
@@ -262,11 +269,16 @@ server_verify(krb5_context kcontext,
preauth_get_entry_data_proc server_get_entry_data,
void *pa_module_context,
void **pa_request_context,
- krb5_data **e_data)
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
{
krb5_int32 nnonce;
krb5_data *test_edata;
+ krb5_authdata **my_authz_data;
+#ifdef DEBUG
+ fprintf(stderr, "wpse: server_verify()!\n");
+#endif
/* Verify the preauth data. */
if (data->length != 4)
return KRB5KDC_ERR_PREAUTH_FAILED;
@@ -282,6 +294,54 @@ server_verify(krb5_context kcontext,
if (*pa_request_context == NULL)
*pa_request_context = malloc(4);
+ /*
+ * Return some junk authorization data just to exercise the
+ * code path handling the returned authorization data.
+ *
+ * NOTE that this is NOT VALID authorization data!
+ */
+#ifdef DEBUG
+ fprintf(stderr, "wpse: doing authorization data!\n");
+#endif
+#if 1 /* USE_5000_AD */
+#define AD_ALLOC_SIZE 5000
+ /* ad_header consists of a sequence tag (0x30) and length (0x82 0x1384)
+ * followed by octet string tag (0x04) and length (0x82 0x1380) */
+ krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80};
+#else
+#define AD_ALLOC_SIZE 100
+ /* ad_header consists of a sequence tag (0x30) and length (0x62)
+ * followed by octet string tag (0x04) and length (0x60) */
+ krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60};
+#endif
+ my_authz_data = malloc(2 * sizeof(*my_authz_data));
+ if (my_authz_data != NULL) {
+ my_authz_data[1] = NULL;
+ my_authz_data[0] = malloc(sizeof(krb5_authdata));
+ if (my_authz_data[0] == NULL) {
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
+ if (my_authz_data[0]->contents == NULL) {
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE);
+ my_authz_data[0]->magic = KV5M_AUTHDATA;
+ my_authz_data[0]->ad_type = 1;
+ my_authz_data[0]->length = AD_ALLOC_SIZE;
+ memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header));
+ sprintf(my_authz_data[0]->contents + sizeof(ad_header),
+ "wpse authorization data: %d bytes worth!\n", AD_ALLOC_SIZE);
+ *authz_data = my_authz_data;
+#ifdef DEBUG
+ fprintf(stderr, "Returning %d bytes of authorization data\n",
+ AD_ALLOC_SIZE);
+#endif
+ }
+
/* Return edata to exercise code that handles edata... */
test_edata = malloc(sizeof(*test_edata));
if (test_edata != NULL) {
@@ -366,6 +426,7 @@ server_return(krb5_context kcontext,
krb5_free_keyblock_contents(kcontext, encrypting_key);
krb5_copy_keyblock_contents(kcontext, kb, encrypting_key);
+
/* Clean up. */
krb5_free_keyblock(kcontext, kb);
@@ -375,13 +436,13 @@ server_return(krb5_context kcontext,
static int
server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
- return PA_HARDWARE | PA_REPLACES_KEY;
+ return PA_HARDWARE | PA_REPLACES_KEY | PA_SUFFICIENT;
}
static krb5_preauthtype supported_client_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
static krb5_preauthtype supported_server_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
-struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
+struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
"wpse", /* name */
&supported_client_pa_types[0], /* pa_type_list */
NULL, /* enctype_list */
@@ -395,7 +456,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
client_gic_opt /* get init creds opts function */
};
-struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
+struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
"wpse",
&supported_server_pa_types[0],
NULL,