Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:jberkman
pkinit-nss
pkinit-nss-0.5.0-1-cvs200705021211.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File pkinit-nss-0.5.0-1-cvs200705021211.patch of Package pkinit-nss
? Makefile ? Makefile.in ? aclocal.m4 ? autom4te.cache ? config.guess ? config.h ? config.h.in ? config.log ? config.status ? config.sub ? configure ? depcomp ? install-sh ? libtool ? ltmain.sh ? missing ? mkinstalldirs ? pkinit-nss-0.5.0-1-cvs200705021211.patch ? pkinit-nss.spec ? stamp-h1 ? doc/Makefile ? doc/Makefile.in ? po/Makefile ? po/Makefile.in ? po/POTFILES ? src/.deps ? src/.libs ? src/Makefile ? src/Makefile.in ? src/bcmst.lo ? src/bcmsutil ? src/certs.lo ? src/commont.lo ? src/cscope.out ? src/deroid ? src/make-pkinit-san ? src/map-file.lo ? src/oakley.lo ? src/pkinit-show-cert-guid ? src/pkinit.c.gak ? src/pkinit.la ? src/pkinit.lo ? src/pkinitt.lo ? src/fragment/.deps ? src/fragment/.libs ? src/fragment/Makefile ? src/fragment/Makefile.in ? src/fragment/openmod ? src/fragment/openp12 Index: ChangeLog =================================================================== RCS file: /usr/local/CVS/pkinit-nss/ChangeLog,v retrieving revision 1.76 retrieving revision 1.103 diff -u -r1.76 -r1.103 --- ChangeLog 5 Feb 2007 16:38:16 -0000 1.76 +++ ChangeLog 25 Apr 2007 17:43:50 -0000 1.103 @@ -1,8 +1,144 @@ -2006-02-05 nalin +2007-04-25 nalin + * src/bcmst.c: add definition/template/encoder/decoder for + EncryptedData. + +2007-04-25 nalin + * src/pkinitt.c: remove a few unused variables, remove an unnecessary + typecast. + +2007-04-25 nalin + * configure.ac,src/pkinit.c: handle cases where preauth_plugin.h + requires the krb5_gic_opt_pa_data data type and krb5.h didn't provide + it, even as a stub, yet. + +2007-04-25 nalin + * src/pkinit.c: fix argument order for the client_process() function, + from Jacob Berkman. Fix arguments for client_tryagain(). + +2007-04-25 nalin + * backport-1.6.1: actually commit the new files. + +2007-04-24 nalin + * configure.ac: define PKINIT_CLIENT_MISSING_GIC_OPTS instead of + PKINIT_DONT_USE_GIC_OPTS. + * src/pkinit.c: key off of PKINIT_CLIENT_MISSING_GIC_OPTS instead + of PKINIT_DONT_USE_GIC_OPTS. + +2007-04-24 nalin + * Makefile.am,backport-1.6.1: add headers from 1.6.1. + * configure.ac: check for 1.5/1.6/1.6.1. Provide a --with-krb5-version + option to override autodetection. Define PKINIT_USE_PAL_BACKPORT + and PKINIT_DONT_USE_GIC_OPTS instead of the more not namespaced + USE_PAL_BACKPORT and DONT_USE_GIC_OPTS defines. + * src/pkinit.c(client_gic_opts): add a stub. + * src/pkinit.c(client_process): expect an "opts" argument if + PKINIT_CLIENT_PROCESS_MISSING_OPT isn't defined. + * src/pkinit.c(preauthentication_client_0): point to client_gic_opts + if PKINIT_DONT_USE_GIC_OPTS isn't defined. + +2007-04-24 nalin + * autogen: drop --enable-debugging -- we're always debuggable now. + +2007-04-24 nalin + * src/bcmst.c(compare_issuer_and_sn,find_cert_by_issuer_and_sn): add. + From Jacob Berkman. + * src/bcmst.c(find_cert_from_rid): use find_cert_by_issuer_and_sn() + instead of CERT_FindCertByIssuerAndSN(). From Jacob Berkman. + +2007-04-23 nalin + * ChangeLog: er, 2007, not 2006. + * src/pkinitt.c: add routines specifically for decoding typed_data. + +2007-04-23 nalin + * src/pkinit.c(client_process): accept host names in various places. + Rename authorization_data variables to indicate that they're really + typed_data. + +2007-04-23 nalin + * src/certs.c(cert_get_oid_pkinit_rkey_data_oid, + cert_get_oid_pkinit_auth_data_oid): add. + * src/certs.c(check_item_in_list): factor out a routine for checking + for a value in a list. + * src/certs.c(cert_san_matches_dns_for_realm): add a check to see if + the certificate in question contains a commonName in its subject or a + dnsName subjectAltName with a value which matches the realm's + "trusted_servers" setting. + * src/certs.c(cert_is_preferred): check for a matching DNS name. + * src/certs.c(cert_validate_kdc_certificate): check for a matching + DNS name for the KDC in combination with SSL ServerAuth or KDC key + EKU values. + * doc/CONFIGURATION: note "trusted_servers" + +2007-02-06 nalin + * src/bcmst.c(external_principal_identifier_template): mark the + subject_key_identifier as explicit, even though it's not, so that + NSS will add and strip the octet-string wrapper for us, making it + easier to use and compare values in CERTCertificate structures. + +2007-02-06 nalin + * src/commont.c(common_generate_content_encryption_key_aes): Add. + * src/bcmst.c(bcms_extract_enveloped_data): Learn to parse AES + parameters (an IV). + * src/bcmsutil.c(main): add options for specifying use of + AES128/AES256 keys for bulk encrypting the enveloped data. + * src/bcmsutil.c(usage): document the new AES options, plus the old + DES and RC2 options. + * src/certs.c(cert_is_preferred): debug log when we find a banned cert + or one which chains up to a trusted certifiers. + * src/pkinit.c(client_try_again): debug log how many trusted + certifiers or invalid certificates the KDC told us about. + * src/pkinitt.c,src/bcmst.c: debug log key sizes when handling + enveloped data. + +2007-02-06 nalin + * src/certs.c(bcms_extract_enveloped_data): note what the unsupported + encryption algorithm is, for troubleshooting. + * src/pkinitt.c(pkinit_create_trusted_certifiers_edata): fix encoding + of trusted-certifiers. + +2007-02-06 nalin + * src/bcmst.c(bcms_add_signer_to_signed_data): insert the signing time + into the list so that the encoding comes out with the items sorted, as + required by CMS. + * src/certs.c(cert_validate_{client,kdc}_certificate): add a specific + log for unknown-issuer errors. + * src/certs.c(cert_validate_client_certificate): catch and add e-data + for revoked-certificate errors. Return KDC_ERR_CLIENT_NAME_MISMATCH + instead of KDC_ERR_CERTIFICATE_MISMATCH in the event of name a mismatch. + +2007-02-05 nalin + * src/certs.c,src/pkinit.c: certificate nicknames can be NULL; guard + against that. + * src/pkinitt.c(pkinit_encode_authorization_data): use the right + template, correctly pass the array to the encoder. + * src/pkinitt.c(pkinit_create_typed_datum): return a datum structure, + which the caller will have an easier time processing. + * src/pkinitt.c(pkinit_create_invalid_certificates_edata, + pkinit_create_trusted_certifiers_edata, + pkinit_create_dh_parameters_edata): encode the error data correctly. + * src/pkinitt.c(pkinit_verify_pa_pk_as_req): fix checking of the DH + prime length. + * src/pkinit.c(client_try_again): fix parsing of error data from the + KDC: it's a sequence of typed-data, not just one. + * src/certs.c(cert_validate_kdc_certificate): directly interpret some + of the more expected errors for the log. + * src/certs.c(cert_validate_client_certificate): directly interpret + some of the more expected errors for the log. Report invalid cert + with e_data for revoked certificates. + * src/bcmst.c: use the subjectName, not the possibly-NULL nickname, in + a debug message. + * src/commont.c: take care that integers are encoded so that they come + out unsigned. + +2007-02-05 nalin + * src/pkinit.c: make the location of the client and server + key/cert/token database configurable. + +2007-02-05 nalin * configure.ac: check for 1.7-specific gic_opts callback functions. * configure.ac: bump version to 0.5.0. -2006-02-05 nalin +2007-02-05 nalin * configure.ac: fixup logic so that we can tell the difference between 1.5 without preauth_plugin.h (where the backport forces changes in the symbol names for safety's sake) and 1.6 without preauth_plugin.h. @@ -42,7 +178,7 @@ KDC_ERR_CANT_VERIFY_CERTIFICATE, KDC_ERR_INVALID_CERTIFICATE, and KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. -2006-01-26 nalin +2007-01-26 nalin * src/pkinitt.c(pkinit_time_from_signing_time): try to parse the raw timestamp (without the type and length) as a timestamp. * src/pkinitt.c(pkinit_time_from_utc_time): fix scanning of the time. @@ -54,7 +190,7 @@ CAs as an external_principal_identifier list instead of an array of DER-encoded certificates. -2006-01-26 nalin +2007-01-26 nalin * src/pkinitt.c(pkinit_signing_time_from_time): add function to convert to encoded signing time to time_t, using UTC time or generalized time, as appropriate. @@ -79,11 +215,11 @@ * src/bcmsutil.c: add a -v (verbosity) option to crank up debug logging. -2006-01-25 nalin +2007-01-25 nalin - make pkinit_debug() take a module_context and a debug priority level - thread module_context pointers through many, many functions -2006-01-22 nalin +2007-01-22 nalin * src/bcmst.c(bcms_make_certificate_list): take a deep copy of the certificate, in case the current origin gets pulled out from under us before we go to encode this list. @@ -91,7 +227,7 @@ copy of the parameters field, in case the one we're using gets pulled out from under us before we go to encode this list. -2006-01-19 nalin +2007-01-19 nalin * src/bcmst.c, src/certs.c: use CERT_DestroyCertArray() instead of a home-grown function which actually leaks the array pointer (oops). * src/certs.c(cert_validate_kdc_certificate, @@ -102,39 +238,39 @@ * src/pkinitt.c(pkinit_validate_kdc_certificate): provide a way to pass in the pool of certs which may include intermediate CAs. -2006-01-12 nalin +2007-01-12 nalin * src/bcmst.c, src/certs.c: give destroy_array_of_certs() an upper-bound on the array size. -2006-01-12 nalin +2007-01-12 nalin * backport: update to base off of the final 1.6 sources. -2006-01-12 nalin +2007-01-12 nalin * src/pkinit.c: release slots and certificates when they're no longer going to be used. Note if NSS shutdown fails. -2006-01-12 nalin +2007-01-12 nalin * doc/CONFIGURATION: note which Oakley groups we know about already. -2006-01-12 nalin +2007-01-12 nalin * src/show-cert-guid.c: note if NSS shutdown fails. -2006-01-12 nalin +2007-01-12 nalin * src/pkinitt.c: release keys, certificates, contexts, and slots. -2006-01-12 nalin +2007-01-12 nalin * src/certs.c: release certificates when they're no longer going to be used. -2006-01-12 nalin +2007-01-12 nalin * src/bcmsutil.c: add a -t option to allow forcing a token login. * src/bcmst.c: release keys, certificates, contexts, and slots. -2006-01-12 nalin +2007-01-12 nalin * src/oakley.c, src/prime2sub: add q values for the rest of the DH parameter sets. -2006-01-08 nalin +2007-01-08 nalin * src/bcmst.c(bcms_add_cert_chain_to_signed_data): walk the chain correctly (#221917). Index: Makefile.am =================================================================== RCS file: /usr/local/CVS/pkinit-nss/Makefile.am,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- Makefile.am 5 Feb 2007 16:12:40 -0000 1.10 +++ Makefile.am 24 Apr 2007 20:32:57 -0000 1.11 @@ -5,7 +5,8 @@ $(top_srcdir)/backport/krb5-*pal*.patch \ $(top_srcdir)/backport/krb5/*.h \ $(top_srcdir)/backport/*.h \ - $(top_srcdir)/backport-1.6/krb5/*.h + $(top_srcdir)/backport-1.6/krb5/*.h \ + $(top_srcdir)/backport-1.6.1/krb5/*.h VERSION=$(shell rpm -q --specfile $(top_srcdir)/pkinit-nss.spec --qf '%{version}\n' | head -n1) RELEASE=$(shell rpm -q --specfile $(top_srcdir)/pkinit-nss.spec --qf '%{release}\n' | head -n1) Index: configure.ac =================================================================== RCS file: /usr/local/CVS/pkinit-nss/configure.ac,v retrieving revision 1.23 retrieving revision 1.27 diff -u -r1.23 -r1.27 --- configure.ac 5 Feb 2007 16:38:16 -0000 1.23 +++ configure.ac 25 Apr 2007 16:59:34 -0000 1.27 @@ -1,4 +1,4 @@ -AC_INIT(pkinit-nss,0.5.0) +AC_INIT(pkinit-nss,0.6.0) AM_INIT_AUTOMAKE(foreign) AM_PROG_LIBTOOL AM_GLIB_GNU_GETTEXT @@ -30,27 +30,61 @@ CFLAGS="$CFLAGS $KRB5_CFLAGS" saved_cppflags="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $KRB5_CFLAGS" + AC_CHECK_HEADERS(krb5/krb5.h krb5/preauth_plugin.h) if test x$ac_cv_header_krb5_krb5_h = xno ; then AC_MSG_ERROR([krb5/krb5.h not found -- do you have Kerberos development files?]) fi -if test x$ac_cv_header_krb5_preauth_plugin_h = xno ; then + +AC_CHECK_LIB(krb5,krb5_is_referral_realm,,,$KRB5_LIBS) +AC_CHECK_LIB(krb5,krb5_get_init_creds_opt_alloc,,,$KRB5_LIBS) +AC_CHECK_TYPE(krb5_gic_opt_pa_data,,,[#include <krb5.h>]) +AC_ARG_WITH(krb5-version,[AS_HELP_STRING([--with-krb5-version=AUTO],[Attempt to build for a specified version of MIT Kerberos.])],krb5_version=$withval,krb5_version=AUTO) +if test "x$krb5_version" = xAUTO ; then AC_MSG_RESULT([Using backport preauth plugin header support.]) - AC_MSG_RESULT([Checking whether this is Kerberos 1.5 or 1.6]) - AC_CHECK_LIB(krb5,krb5_is_referral_realm,,,$KRB5_LIBS) - if test x$ac_cv_lib_krb5_krb5_is_referral_realm = xyes ; then - AC_MSG_RESULT([Building for Kerberos 1.6.]) - BACKPORT_CPPFLAGS='-I$(top_srcdir)/backport-1.6' + AC_MSG_CHECKING([whether this is Kerberos 1.5, 1.6, or 1.6.1]) + if test x$ac_cv_lib_krb5_krb5_get_init_creds_opt_alloc = xyes ; then + AC_MSG_RESULT([looks like 1.6.1.]) + krb5_version=1.6.1 + elif test x$ac_cv_lib_krb5_krb5_is_referral_realm = xyes ; then + AC_MSG_RESULT([looks like 1.6.]) + krb5_version=1.6 else - AC_MSG_RESULT([Building for Kerberos 1.5.]) - BACKPORT_CPPFLAGS='-I$(top_srcdir)/backport' - AC_DEFINE(USE_PAL_BACKPORT,1,[Define if you're using the bundled backport patch and error header.]) + AC_MSG_RESULT([can't tell, guessing 1.5.]) + krb5_version=1.5 fi +else + AC_MSG_RESULT([Requested build for $krb5_version.]) fi -AC_CHECK_LIB(krb5,krb5_get_init_creds_opt_set_pkinit,,,$KRB5_LIBS) -if test x$ac_cv_lib_krb5_krb5_get_init_creds_opt_set_pkinit != xyes ; then - AC_DEFINE(DONT_USE_GIC_OPTS,1,[Define if your PAL doesn't provide an option for a preauth_client_supply_gic_opts_proc callback function.]) -fi +case "$krb5_version" in + 1.6.1) + AC_MSG_RESULT([Building for Kerberos 1.6.1.]) + if test x$ac_cv_header_krb5_preauth_plugin_h = xno ; then + BACKPORT_CPPFLAGS='-I$(top_srcdir)/backport-1.6.1' + fi + ;; + 1.6) + AC_MSG_RESULT([Building for Kerberos 1.6.]) + if test x$ac_cv_header_krb5_preauth_plugin_h = xno ; then + BACKPORT_CPPFLAGS='-I$(top_srcdir)/backport-1.6' + fi + AC_DEFINE(PKINIT_CLIENT_PROCESS_MISSING_OPT,1,[Define if your client_process() signature doesn't include an "opt" parameter.]) + AC_DEFINE(PKINIT_CLIENT_MISSING_GIC_OPTS,1,[Define if your PAL doesn't provide an option for a supply_gic_opts_proc callback function.]) + ;; + 1.5) + AC_MSG_RESULT([Building for Kerberos 1.5.]) + if test x$ac_cv_header_krb5_preauth_plugin_h = xno ; then + BACKPORT_CPPFLAGS='-I$(top_srcdir)/backport' + fi + AC_DEFINE(PKINIT_USE_PAL_BACKPORT,1,[Define if you're using the bundled backport patch and error header.]) + AC_DEFINE(PKINIT_CLIENT_PROCESS_MISSING_OPT,1,[Define if your client_process() signature doesn't include an "opt" parameter.]) + AC_DEFINE(PKINIT_CLIENT_MISSING_GIC_OPTS,1,[Define if your PAL doesn't provide an option for a supply_gic_opts_proc callback function.]) + ;; + *) + AC_MSG_ERROR([Don't know how to build for $krb5_version (yet).]) + ;; +esac + CFLAGS="$saved_cflags" CPPFLAGS="$saved_cppflags" Index: pkinit-nss.spec.in =================================================================== RCS file: /usr/local/CVS/pkinit-nss/pkinit-nss.spec.in,v retrieving revision 1.23 retrieving revision 1.24 diff -u -r1.23 -r1.24 --- pkinit-nss.spec.in 5 Feb 2007 16:38:38 -0000 1.23 +++ pkinit-nss.spec.in 6 Feb 2007 02:20:26 -0000 1.24 @@ -36,6 +36,10 @@ %{_libdir}/krb5 %changelog +* Mon Feb 5 2007 Nalin Dahyabhai <nalin@redhat.com> 0.6.0-1 +- fix encoding/interpreting of e_data, encoding of DH parameters, error + reporting for revoked certificates + * Mon Feb 5 2007 Nalin Dahyabhai <nalin@redhat.com> 0.5.0-1 - add protocol-level error reporting and handling Index: backport-1.6.1/krb5/preauth_plugin.h =================================================================== RCS file: backport-1.6.1/krb5/preauth_plugin.h diff -N backport-1.6.1/krb5/preauth_plugin.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ backport-1.6.1/krb5/preauth_plugin.h 25 Apr 2007 15:58:37 -0000 1.1 @@ -0,0 +1,372 @@ +/* + * <krb5/preauth_plugin.h> + * + * Copyright (c) 2006 Red Hat, Inc. + * Portions copyright (c) 2006 Massachusetts Institute of Technology + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Red Hat, Inc., nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Preauthentication plugin definitions for Kerberos 5. + */ + +#ifndef KRB5_PREAUTH_PLUGIN_H_INCLUDED +#define KRB5_PREAUTH_PLUGIN_H_INCLUDED +#include <krb5/krb5.h> + +/* + * While arguments of these types are passed-in, for the most part a preauth + * module can treat them as opaque. If we need keying data, we can ask for + * it directly. + */ +struct _krb5_db_entry_new; +struct _krb5_key_data; +struct _krb5_preauth_client_rock; + +/* + * Preauth mechanism property flags, unified from previous definitions in the + * KDC and libkrb5 sources. + */ + +/* Provides a real answer which we can send back to the KDC (client-only). The + * client assumes that one real answer will be enough. */ +#define PA_REAL 0x00000001 + +/* Doesn't provide a real answer, but must be given a chance to run before any + * REAL mechanism callbacks (client-only). */ +#define PA_INFO 0x00000002 + +/* Causes the KDC to include this mechanism in a list of supported preauth + * types if the user's DB entry flags the user as requiring hardware-based + * preauthentication (server-only). */ +#define PA_HARDWARE 0x00000004 + +/* Causes the KDC to include this mechanism in a list of supported preauth + * types if the user's DB entry flags the user as requiring preauthentication, + * and to fail preauthentication if we can't verify the client data. The + * flipside of PA_SUFFICIENT (server-only). */ +#define PA_REQUIRED 0x00000008 + +/* Causes the KDC to include this mechanism in a list of supported preauth + * types if the user's DB entry flags the user as requiring preauthentication, + * and to mark preauthentication as successful if we can verify the client + * data. The flipside of PA_REQUIRED (server-only). */ +#define PA_SUFFICIENT 0x00000010 + +/* Marks this preauthentication mechanism as one which changes the key which is + * used for encrypting the response to the client. Modules which have this + * flag have their server_return_proc called before modules which do not, and + * are passed over if a previously-called module has modified the encrypting + * key (server-only). */ +#define PA_REPLACES_KEY 0x00000020 + +/* Causes the KDC to check with this preauthentication module even if the + * client has no entry in the realm database. If the module returns a success + * code, continue processing and assume that its return_padata callback will + * supply us with a key for encrypting the AS reply (server-only). */ +/* #define PA_VIRTUAL (0x00000040 | PA_REPLACES_KEY) */ + +/* Not really a padata type, so don't include it in any list of preauth types + * which gets sent over the wire. */ +#define PA_PSEUDO 0x00000080 + +/* + * 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 **); + +/* + * A client module's callback functions are allowed to request various + * information to enable it to process a request. + */ +enum krb5plugin_preauth_client_request_type { + /* The returned krb5_data item holds the enctype used to encrypt the + * encrypted portion of the AS_REP packet. */ + krb5plugin_preauth_client_get_etype = 1, + /* Free the data returned from krb5plugin_preauth_client_req_get_etype */ + krb5plugin_preauth_client_free_etype = 2 +}; +typedef krb5_error_code +(*preauth_get_client_data_proc)(krb5_context, + struct _krb5_preauth_client_rock *, + 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. + */ +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); + +/* + * 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. + */ +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); +/* + * 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 { + /* Not-usually-visible name. */ + char *name; + + /* Pointer to zero-terminated list of pa_types which this module can + * provide services for. */ + krb5_preauthtype *pa_type_list; + + /* Pointer to zero-terminated list of enc_types which this module claims + * to add support for. */ + krb5_enctype *enctype_list; + + /* 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 */ + krb5_error_code (*init)(krb5_context context, void **plugin_context); + void (*fini)(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. */ + int (*flags)(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. */ + void (*request_init)(krb5_context context, void *plugin_context, + void **request_context); + void (*request_fini)(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. */ + 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); + /* 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); + /* + * 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; + +/* + * The function table / structure which a preauth server module must export as + * "preauthentication_server_0". NOTE: replace "0" with "1" for the type and + * variable names if this gets picked up by upstream. 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_server_ftable_v0 { + /* Not-usually-visible name. */ + char *name; + + /* Pointer to zero-terminated list of pa_types which this module can + * provide services for. */ + krb5_preauthtype *pa_type_list; + + /* 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 *); + /* 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. */ + int (*flags_proc)(krb5_context, krb5_preauthtype); + /* 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. */ + 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); + /* 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); + /* 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); + /* 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; + + +/* + * This function allows a preauth plugin to obtain preauth + * options. The preauth_data returned from this function + * should be freed by calling krb5_get_init_creds_opt_free_pa(). + * + * The 'opt' pointer supplied to this function must have been + * obtained using krb5_get_init_creds_opt_alloc() + */ +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_get_pa + (krb5_context context, + krb5_get_init_creds_opt *opt, + int *num_preauth_data, + krb5_gic_opt_pa_data **preauth_data); + +/* + * This function frees the preauth_data that was returned by + * krb5_get_init_creds_opt_get_pa(). + */ +void KRB5_CALLCONV +krb5_get_init_creds_opt_free_pa + (krb5_context context, + int num_preauth_data, + krb5_gic_opt_pa_data *preauth_data); + +#endif /* KRB5_PREAUTH_PLUGIN_H_INCLUDED */ Index: doc/CONFIGURATION =================================================================== RCS file: /usr/local/CVS/pkinit-nss/doc/CONFIGURATION,v retrieving revision 1.9 retrieving revision 1.12 diff -u -r1.9 -r1.12 --- doc/CONFIGURATION 5 Feb 2007 16:27:27 -0000 1.9 +++ doc/CONFIGURATION 23 Apr 2007 21:45:43 -0000 1.12 @@ -1,34 +1,41 @@ In appdefaults: allow_pkinit - Enable or disable the module. Default is "yes". allow_pkinit_server - Enable or disable the module for KDCs. Default is - to take the value of the "allow_pkinit" option. + to take the value of the "allow_pkinit" option. Overrides "allow_pkinit". allow_pkinit_client - Enable or disable the module for clients. Default is - to take the value of the "allow_pkinit" option. + to take the value of the "allow_pkinit" option. Overrides "allow_pkinit". trusted_guid - GUID extension value which the client will trust if the - KDC's cert has no subjectAltName value which can be used. - No default. + KDC's cert has no subjectAltName value which can be used. + No default. ocsp_checking - Enable or disable OCSP checking. Default is "yes" for - KDCs, "no" for clients. - is_hw - Assume that a PKINIT client also satisfies requires_hwauth - requirements. Default is "no". - try_dh - Enable DH instead of enckey-based kinit. Default is "yes". + KDCs, "no" for clients. + is_hw - Assume that a PKINIT client also satisfies requires_hwauth + requirements. Default is "no". + try_dh - Enable DH instead of enckey-based kinit. Default is "yes". minimum_dh_prime_size - Minimum acceptable size for DH primes. Default 1024. preferred_group - Preferred Oakley group when using DH. The default - moduli included with Heimdal correspond to "2". Default + moduli included with Heimdal correspond to 14. Default is "2". Valid values include 1, 2, 5, 14, 15, 16. mappings_file - Name of a principal-name-to-subject-DN mapping file. No - default setting. + default setting. trust_pkinit_san - Whether or not to trust PKINIT-style subjectAltName values - in certificates. Default is "yes". + in certificates. Default is "yes". trust_upn_san - Whether or not to trust userPrincipalName subjectAltName - values in certificates. Default is "yes". + values in certificates. Default is "yes". + client_database - Location of the certificate/key/token database used by the + client. Default is set at compile-time. + server_database - Location of the certificate/key/token database used by the + KDC. Default is set at compile-time. debug_level - Logging level. Default is "0". debug_syslog - Whether or not to send debug messages to syslog. Default - is "yes". + is "yes". debug_stderr - Whether or not to send debug messages to stderr. Default - is "no". + is "no". + trusted_servers - DNS names which, if found in a KDC's certificate, will + make it acceptable as an alternate to having a matching + principal name or GUID. [appdefaults] allow_pkinit = no Index: doc/TODO =================================================================== RCS file: /usr/local/CVS/pkinit-nss/doc/TODO,v retrieving revision 1.10 retrieving revision 1.12 diff -u -r1.10 -r1.12 --- doc/TODO 6 Nov 2006 23:46:23 -0000 1.10 +++ doc/TODO 6 Feb 2007 01:37:18 -0000 1.12 @@ -12,13 +12,17 @@ (well, hopefully not). - Less code to maintain (at some point). - Implement reading of OpenSSL-style PEM-formatted keys and certificates. + - Should be doable. Might be tricky importing keys, if there's no in-memory + database in which to store them. - Implement reading of directories filled with OpenSSL-style PEM-formatted keys and certificates. - Implement reading of client creds directly from a PKCS12 file. + - Can't be done just now. Using a key from a PKCS12 bag requires that it be + imported into a database, and at that point your side-effect is pk12util's + job. - Implement direct loading and use of PKCS11 modules. -- Implement retrying guided by typed-data returned with CANT_VERIFY_CERTIFICATE - and INVALID_CERTIFICIATE errors. - - Report TD-TRUSTED-CERTIFIERS e-data for KDC_ERR_CANT_VERIFY_CERTIFICATE. - - Report TD-INVALID-CERTIFICATES e-data for KDC_ERR_INVALID_CERTIFICATE. - - Report TD-DH-PARAMETERS e-data for KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. + - Having trouble using modules which aren't already in the secmod database. +- Implement RFC4557 "stapling" of OCSP responses into the AS exchange, to + maybe cut down on client lag and OCSP responder load. Requires newer NSS + than exists as of 20070205. - Implement a Windows-compatible server. Index: doc/openssl/make-certs.sh =================================================================== RCS file: /usr/local/CVS/pkinit-nss/doc/openssl/make-certs.sh,v retrieving revision 1.2 retrieving revision 1.4 diff -u -r1.2 -r1.4 --- doc/openssl/make-certs.sh 6 Nov 2006 23:46:05 -0000 1.2 +++ doc/openssl/make-certs.sh 6 Feb 2007 01:37:18 -0000 1.4 @@ -19,8 +19,8 @@ # Choose a user name part for email attributes. GIVENUSER=$2 -test x$GIVENUSER = x && GIVENUSER=$USER -echo $GIVENUSER | grep -q @ || GIVENUSER=$GIVENUSER@$DOMAIN +test x"$GIVENUSER" = x && GIVENUSER=$USER +echo "$GIVENUSER" | grep -q @ || GIVENUSER="$GIVENUSER"@$DOMAIN DOMAIN=`echo "$GIVENUSER" | cut -f2- -d@` # Default to generating a key/cert pair for the current host. @@ -106,14 +106,14 @@ done=echo fi -# Generate another key. -if ! test -f "$commonname.key" ; then +# Generate another key, unless we have a key or CSR. +if ! test -f "$commonname.key" && ! test -f "$commonname.csr" ; then umask=`umask -p` umask 077 openssl genrsa 1024 -nodes > "$commonname.key" 2> /dev/null $umask else -echo "You already have a $commonname.key file; not replacing." +echo "You already have a $commonname.key or $commonname.csr file; not replacing." done=echo fi @@ -175,13 +175,13 @@ eku="$eku,$ekuval" fi ;; - *@*COM) + *@*.COM|*@*.EDU|*@*.NET|*@*.ORG) type="otherName:1.3.6.1.5.2.2;SEQUENCE:$1,otherName:1.3.6.1.4.1.311.20.2.3;UTF8" principals="$principals $1" ;; - *@*com|copy) type=email;; - [0-9]*.[0-9]*.[0-9]*.[0-9]*) type=IP;; - *) type=DNS;; + *@*.com|*@*.edu|*@*.net|*@*.org|copy) type=email;; + [0-9]*.[0-9]*.[0-9]*.[0-9]*) type=IP;; + *) type=DNS;; esac if test -n "$type" ; then newvalue="${type}:$1" @@ -231,7 +231,11 @@ done done -openssl req -config `pwd`/openssl.cnf -new -key "$commonname.key" > host.csr 2> /dev/null +if test -f "$commonname.csr" ; then + cp "$commonname.csr" host.csr +else + openssl req -config `pwd`/openssl.cnf -new -key "$commonname.key" > host.csr 2> /dev/null +fi openssl x509 -req -extfile `pwd`/openssl.cnf -extensions v3_issued -CA ca.crt -CAkey ca.key -CAserial ca.srl -in host.csr -days $DAYS > "$commonname.crt" 2> /dev/null else echo "You already have a $commonname.crt file; not replacing." Index: po/Makefile.in.in =================================================================== RCS file: /usr/local/CVS/pkinit-nss/po/Makefile.in.in,v retrieving revision 1.4 diff -u -r1.4 Makefile.in.in --- po/Makefile.in.in 25 Jan 2007 21:32:03 -0000 1.4 +++ po/Makefile.in.in 2 May 2007 16:06:10 -0000 @@ -25,7 +25,6 @@ prefix = @prefix@ exec_prefix = @exec_prefix@ -datarootdir = @datarootdir@ datadir = @datadir@ libdir = @libdir@ localedir = $(libdir)/locale @@ -35,7 +34,6 @@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ -MKINSTALLDIRS = $(top_srcdir)/@MKINSTALLDIRS@ CC = @CC@ GENCAT = @GENCAT@ @@ -119,11 +117,7 @@ install-data: install-data-@USE_NLS@ install-data-no: all install-data-yes: all - if test -r "$(MKINSTALLDIRS)"; then \ - $(MKINSTALLDIRS) $(DESTDIR)$(datadir); \ - else \ - $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(datadir); \ - fi + @mkdir_p@ $(DESTDIR)$(datadir) @catalogs='$(CATALOGS)'; \ for cat in $$catalogs; do \ cat=`basename $$cat`; \ @@ -133,11 +127,7 @@ esac; \ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \ dir=$(DESTDIR)$$destdir/$$lang/LC_MESSAGES; \ - if test -r "$(MKINSTALLDIRS)"; then \ - $(MKINSTALLDIRS) $$dir; \ - else \ - $(SHELL) $(top_srcdir)/mkinstalldirs $$dir; \ - fi; \ + @mkdir_p@ $$dir; \ if test -r $$cat; then \ $(INSTALL_DATA) $$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \ echo "installing $$cat as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \ @@ -161,11 +151,7 @@ fi; \ done if test "$(PACKAGE)" = "glib"; then \ - if test -r "$(MKINSTALLDIRS)"; then \ - $(MKINSTALLDIRS) $(DESTDIR)$(gettextsrcdir); \ - else \ - $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(gettextsrcdir); \ - fi; \ + @mkdir_p@ $(DESTDIR)$(gettextsrcdir); \ $(INSTALL_DATA) $(srcdir)/Makefile.in.in \ $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \ else \ Index: src/bcmst.c =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/bcmst.c,v retrieving revision 1.16 retrieving revision 1.25 diff -u -r1.16 -r1.25 --- src/bcmst.c 5 Feb 2007 16:14:44 -0000 1.16 +++ src/bcmst.c 25 Apr 2007 17:43:50 -0000 1.25 @@ -20,7 +20,7 @@ * USA. */ -#ident "$Id: bcmst.c,v 1.16 2007/02/05 16:14:44 nalin Exp $" +#ident "$Id: bcmst.c,v 1.25 2007/04/25 17:43:50 nalin Exp $" #include "../config.h" @@ -391,7 +391,9 @@ .size = sizeof(SECItem), }, { - .kind = SEC_ASN1_CONTEXT_SPECIFIC | 2 | + /* This is supposed to be implicit, but we use an explicit tag here to + * avoid having to wrap and unwrap the raw data which NSS gives us. */ + .kind = SEC_ASN1_CONTEXT_SPECIFIC | 2 | SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL, .offset = offsetof(struct external_principal_identifier, subject_key_identifier), @@ -589,6 +591,39 @@ {0, 0, NULL, 0}, }; +/* EncryptedData: RFC 3852, 8. */ +const SEC_ASN1Template +bcms_encrypted_data_template[] = { + { + .kind = SEC_ASN1_SEQUENCE, + .offset = 0, + .sub = NULL, + .size = sizeof(struct encrypted_data), + }, + { + .kind = SEC_ASN1_INTEGER, + .offset = offsetof(struct encrypted_data, version), + .sub = &SEC_IntegerTemplate, + /* CMSVersion == Integer */ + .size = sizeof(SECItem), + }, + { + .kind = SEC_ASN1_INLINE, + .offset = offsetof(struct encrypted_data, encrypted_content_info), + .sub = &bcms_encrypted_content_info_template, + .size = sizeof(struct encrypted_content_info), + }, + { + .kind = SEC_ASN1_CONTEXT_SPECIFIC | 1 | + SEC_ASN1_CONSTRUCTED | + SEC_ASN1_OPTIONAL, + .offset = offsetof(struct encrypted_data, der_unsigned_attrs), + .sub = &SEC_AnyTemplate, + .size = sizeof(SECItem), + }, + {0, 0, NULL, 0}, +}; + struct attribute ** bcms_decode_set_of_attributes(struct module_context *mcontext, PLArenaPool *pool, SECItem *item) @@ -781,6 +816,38 @@ return NULL; } +struct encrypted_data * +bcms_decode_encrypted_data(struct module_context *mcontext, + PLArenaPool *pool, SECItem *item) +{ + struct encrypted_data *ed; + + ed = PORT_ArenaZAlloc(pool, sizeof(*ed)); + if (ed == NULL) { + pkinit_debug(mcontext, 0, "Out of memory.\n"); + return NULL; + } + if (SEC_ASN1DecodeItem(pool, ed, bcms_encrypted_data_template, + item) != SECSuccess) { + pkinit_debug(mcontext, 1, "Error decoding encrypted-data.\n"); + return NULL; + } + return ed; +} + +SECItem * +bcms_encode_encrypted_data(struct module_context *mcontext, + PLArenaPool *pool, struct encrypted_data *ed) +{ + SECItem encoded; + if (SEC_ASN1EncodeItem(pool, &encoded, ed, + bcms_encrypted_data_template) == &encoded) { + return SECITEM_ArenaDupItem(pool, &encoded); + } + pkinit_debug(mcontext, 1, "Error encoding encrypted-data.\n"); + return NULL; +} + struct content_info * bcms_build_content_info(PLArenaPool *pool, SECOidTag content_type_tag, SECItem *content_data) @@ -1321,7 +1388,7 @@ PK11Context *ctx; int signer_count, i; unsigned char digest_buf[64]; - unsigned int digest_length; + unsigned int digest_length, attribute; /* Double-check that we actually have something to sign. */ if (sig->encap_content_info.content.len == 0) { @@ -1449,11 +1516,15 @@ digest_attr.attr_type = oid->oid; digest_attr.attr_values = digest_values; - signing_times[0] = NULL; - signing_times[1] = NULL; if (pkinit_signing_time_from_time(mcontext, pool, NULL, &signing_time_item) == SECSuccess) { signing_times[0] = &signing_time_item; + signing_times[1] = NULL; + pkinit_debug(mcontext, 2, "Including signing time.\n"); + } else { + signing_times[0] = NULL; + pkinit_debug(mcontext, 2, "Unable to including signing " + "time.\n"); } oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_SIGNING_TIME); if (oid == NULL) { @@ -1465,14 +1536,13 @@ signing_time_attr.attr_type = oid->oid; signing_time_attr.attr_values = signing_times; - attributes[0] = &content_type_attr; - attributes[1] = &digest_attr; + attribute = 0; + attributes[attribute++] = &content_type_attr; if (signing_times[0] != NULL) { - attributes[2] = &signing_time_attr; - attributes[3] = NULL; - } else { - attributes[2] = NULL; + attributes[attribute++] = &signing_time_attr; } + attributes[attribute++] = &digest_attr; + attributes[attribute++] = NULL; sign_content = bcms_encode_set_of_attributes(mcontext, pool, attributes); @@ -1501,13 +1571,13 @@ return SECFailure; } else { pkinit_debug(mcontext, 2, - "Generating signature: %s.\n", sigoid->desc); + "Generating %s signature over %d bytes.\n", + sigoid->desc, sign_content->len); } - signer_info->signature_algorithm.algorithm = sigoid->oid; signer_info->signature_algorithm.parameters = null_item; - /* Create a PKCS1 signature using the selected digest. */ + /* Create a PKCS#1 signature using the selected digest. */ status = SEC_SignData(&signer_info->signature, sign_content->data, sign_content->len, @@ -1605,11 +1675,11 @@ issuer = NULL; pkinit_debug(mcontext, 2, "Walking certifying chain for \"%s\".\n", - cert->nickname); + cert->subjectName); issuer = CERT_FindCertIssuer(cert, 0, 0); while ((issuer != NULL) && (!issuer->isRoot)) { pkinit_debug(mcontext, 2, "Adding chain link \"%s\".\n", - issuer->nickname); + issuer->subjectName); if (bcms_add_cert_to_signed_data(mcontext, pool, sig, issuer) != SECSuccess) { CERT_DestroyCertificate(issuer); @@ -1622,12 +1692,12 @@ if (issuer == NULL) { pkinit_debug(mcontext, 2, "No more links in \"%s\"'s cert chain found.\n", - cert->nickname); + cert->subjectName); return SECFailure; } else { if (issuer->isRoot) { pkinit_debug(mcontext, 2, "Stopping at root \"%s\".\n", - issuer->nickname); + issuer->subjectName); } CERT_DestroyCertificate(issuer); } @@ -1829,11 +1899,62 @@ return nlist; } +/* Like CERT_FindCertByIssuerAndSN(), but explicitly search all slots. */ +struct issuer_and_sn_data { + struct module_context *mcontext; + CERTIssuerAndSN *isn; + CERTCertificate *cert; +}; + +/* If the isuer and serial number of the given certificate matches the ones + * passed in with the data, save a copy of the certificate. */ +static SECStatus +compare_issuer_and_sn(CERTCertificate *cert, SECItem *unused_der_cert, + void *cb_data) +{ + struct issuer_and_sn_data *data; + + data = cb_data; + if (data->cert != NULL) { + return SECSuccess; + } + if (SECITEM_ItemsAreEqual(&cert->derIssuer, + &data->isn->derIssuer) && + SECITEM_ItemsAreEqual(&cert->serialNumber, + &data->isn->serialNumber)) { + data->cert = CERT_DupCertificate(cert); + } + return SECSuccess; +} + +/* Search the cache of loaded certs, and then go out to all tokens. */ +static CERTCertificate * +find_cert_by_issuer_and_sn(struct module_context *mcontext, + CERTCertDBHandle *certdb, + CERTIssuerAndSN *isn, + struct pkinit_pwcb_args *pwcb_args) +{ + struct issuer_and_sn_data data; + + data.cert = CERT_FindCertByIssuerAndSN(certdb, isn); + if (data.cert != NULL) { + return data.cert; + } + + data.mcontext = mcontext; + data.isn = isn; + + PK11_TraverseSlotCerts(compare_issuer_and_sn, &data, pwcb_args); + + return data.cert; +} + /* Find the certificate in the database which matches the recipient_identifier. * The returned certificate must be destroyed by the caller. */ static CERTCertificate * -find_cert_from_rid(struct module_context *unused_mcontext, - CERTCertDBHandle *certdb, struct recipient_identifier *rid) +find_cert_from_rid(struct module_context *mcontext, + CERTCertDBHandle *certdb, struct recipient_identifier *rid, + struct pkinit_pwcb_args *pwcb_args) { struct issuer_and_serial_number *is; SECItem *spki; @@ -1846,7 +1967,8 @@ memset(&isn, 0, sizeof(isn)); isn.derIssuer = is->issuer; isn.serialNumber = is->serial; - return CERT_FindCertByIssuerAndSN(certdb, &isn); + return find_cert_by_issuer_and_sn(mcontext, certdb, + &isn, pwcb_args); break; case recipient_identifier_type_subject_key_identifier: /* The recipient is identified by the key identifier. */ @@ -1916,7 +2038,8 @@ break; } /* try to find a matching cert/key pair */ - cert = find_cert_from_rid(mcontext, certdb, &ktri->rid); + cert = find_cert_from_rid(mcontext, certdb, &ktri->rid, + pwcb_args); if (cert == NULL) { pkinit_debug(mcontext, 1, "Couldn't find certificate for " @@ -2037,6 +2160,7 @@ struct algorithm_identifier *enc_alg; struct rc2_cbc_parameters rc2_params; unsigned long rc2_version; + SECItem aes_params; SECItem des3_params; CK_RC2_CBC_PARAMS rc2_cbc_params; CK_MECHANISM mech; @@ -2076,6 +2200,8 @@ "Unrecognized encryption algorithm.\n"); return NULL; } + pkinit_debug(mcontext, 2, "Recovered %d-bit CEK for use with %s.\n", + key->len * 8, oid->desc); memset(&mech, 0, sizeof(mech)); switch (oid->offset) { case SEC_OID_RC2_CBC: @@ -2144,9 +2270,38 @@ mech.pParameter = des3_params.data; mech.ulParameterLen = des3_params.len; break; + case SEC_OID_AES_128_CBC: + case SEC_OID_AES_192_CBC: + case SEC_OID_AES_256_CBC: + block_size = 16; + ublock_size = 16; + if (enc_alg->parameters == NULL) { + pkinit_debug(mcontext, 1, + "Expected AES parameters, got none.\n"); + return NULL; + } + if (SEC_ASN1DecodeItem(pool, &aes_params, + SEC_OctetStringTemplate, + enc_alg->parameters) != SECSuccess) { + pkinit_debug(mcontext, 1, + "Error parsing AES parameters.\n"); + return NULL; + } + if (aes_params.len != ublock_size) { + pkinit_debug(mcontext, 1, + "AES-CBC IV is of unexpected size " + "(expected %d bits, got %d).\n", + ublock_size * 8, aes_params.len * 8); + return NULL; + } + mech.mechanism = CKM_AES_CBC; + mech.pParameter = aes_params.data; + mech.ulParameterLen = aes_params.len; + break; default: pkinit_debug(mcontext, 1, - "Unsupported encryption algorithm.\n"); + "Unsupported encryption algorithm \"%s\".\n", + oid->desc); return NULL; } @@ -2267,7 +2422,7 @@ /* Encrypt the content-encryption key. */ pkinit_debug(mcontext, 2, "Encrypting %d bytes of CEK data for \"%s\".\n", - content_enc_key->len, recipient->nickname); + content_enc_key->len, recipient->subjectName); enc = SECITEM_AllocItem(pool, NULL, key->u.rsa.modulus.len); if (PK11_PubEncryptPKCS1(key, enc->data, content_enc_key->data, content_enc_key->len, Index: src/bcmst.h =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/bcmst.h,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- src/bcmst.h 5 Feb 2007 16:14:44 -0000 1.8 +++ src/bcmst.h 25 Apr 2007 17:43:50 -0000 1.9 @@ -22,7 +22,7 @@ #ifndef bcmst_h #define bcmst_h -#ident "$Id: bcmst.h,v 1.8 2007/02/05 16:14:44 nalin Exp $" +#ident "$Id: bcmst.h,v 1.9 2007/04/25 17:43:50 nalin Exp $" #include <nspr.h> #include <nss.h> @@ -128,11 +128,18 @@ SECItem der_unsigned_attrs; }; +struct encrypted_data { + SECItem version; + struct encrypted_content_info encrypted_content_info; + SECItem der_unsigned_attrs; +}; + /* ASN.1 templates */ extern const SEC_ASN1Template bcms_content_info_template[]; extern const SEC_ASN1Template bcms_primitive_content_info_template[]; extern const SEC_ASN1Template bcms_encrypted_content_info_template[]; extern const SEC_ASN1Template bcms_enveloped_data_template[]; +extern const SEC_ASN1Template bcms_encrypted_data_template[]; extern const SEC_ASN1Template bcms_external_principal_identifiers_template[]; extern const SEC_ASN1Template bcms_key_trans_recipient_info_template[]; extern const SEC_ASN1Template bcms_recipient_identifier_template[]; @@ -165,6 +172,9 @@ SECItem *bcms_encode_enveloped_data(struct module_context *mcontext, PLArenaPool *pool, struct enveloped_data *ed); +SECItem *bcms_encode_encrypted_data(struct module_context *mcontext, + PLArenaPool *pool, + struct encrypted_data *ed); struct attribute **bcms_decode_set_of_attributes(struct module_context *mcontext, PLArenaPool *pool, SECItem *item); @@ -178,6 +188,9 @@ struct external_principal_identifier **bcms_decode_sequence_of_external_principal_identifier(struct module_context *mcontext, PLArenaPool *pool, SECItem *item); struct enveloped_data *bcms_decode_enveloped_data(struct module_context *mcontext, + PLArenaPool *pool, + SECItem *item); +struct encrypted_data *bcms_decode_encrypted_data(struct module_context *mcontext, PLArenaPool *pool, SECItem *item); Index: src/bcmsutil.c =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/bcmsutil.c,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- src/bcmsutil.c 26 Jan 2007 21:13:47 -0000 1.7 +++ src/bcmsutil.c 7 Feb 2007 00:20:12 -0000 1.8 @@ -20,7 +20,7 @@ * USA. */ -#ident "$Id: bcmsutil.c,v 1.7 2007/01/26 21:13:47 nalin Exp $" +#ident "$Id: bcmsutil.c,v 1.8 2007/02/07 00:20:12 nalin Exp $" #include "../config.h" @@ -185,6 +185,10 @@ "\t-t Attempt to log in to named token first\n" "\t-s Sign using named certificate\n" "\t-e Envelope for recipient with named certificate\n" + "\t -R Use RC2 for enveloped data key transport\n" + "\t -D Use DES3 for enveloped data key transport\n" + "\t -a Use AES128 for enveloped data key transport\n" + "\t -A Use AES256 for enveloped data key transport\n" "\t-u Unwrap data (recursively)\n" "\t-n Suppress data output\n" "\t-i Input file\n" @@ -256,7 +260,7 @@ token = NULL; slot = NULL; preferred_cek = SEC_OID_UNKNOWN; - while ((c = getopt(argc, argv, "d:e:i:s:t:vwunRD")) != -1) { + while ((c = getopt(argc, argv, "d:e:i:s:t:vwunRDaA")) != -1) { switch (c) { case 'd': directory = optarg; @@ -290,6 +294,12 @@ break; case 'D': preferred_cek = SEC_OID_DES_EDE3_CBC; + break; + case 'a': + preferred_cek = SEC_OID_AES_128_CBC; + break; + case 'A': + preferred_cek = SEC_OID_AES_256_CBC; break; default: usage(command); Index: src/certs.c =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/certs.c,v retrieving revision 1.26 diff -u -r1.26 certs.c --- src/certs.c 5 Feb 2007 16:19:40 -0000 1.26 +++ src/certs.c 2 May 2007 16:06:11 -0000 @@ -20,7 +20,7 @@ * USA. */ -#ident "$Id: certs.c,v 1.26 2007/02/05 16:19:40 nalin Exp $" +#ident "$Id: certs.c,v 1.32 2007/04/23 21:45:43 nalin Exp $" #include "../config.h" @@ -45,6 +45,8 @@ #include "backport-errors.h" #endif +#define APPDEFAULT_LIST_SEPARATORS " \t," + static unsigned char oid_ms_sc_login_data[] = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x02}; static const SECOidData oid_ms_sc_login = { @@ -324,11 +326,21 @@ { return &oid_pkinit_auth_data.oid; } +const SECOidData * +cert_get_oid_pkinit_auth_data_oid(void) +{ + return &oid_pkinit_auth_data; +} const SECItem * cert_get_oid_pkinit_rkey_data(void) { return &oid_pkinit_rkey_data.oid; } +const SECOidData * +cert_get_oid_pkinit_rkey_data_oid(void) +{ + return &oid_pkinit_rkey_data; +} const SECItem * cert_get_oid_pkinit_dhkey_data(void) { @@ -529,6 +541,122 @@ return SECSuccess; } +static void +tolower_secitem (SECItem *item) +{ + unsigned int i; + for (i = 0; i < item->len; i++) { + item->data[i] = tolower (item->data[i]); + } +} + +static PRBool +check_item_in_list(const SECItem *item, const char *list) +{ + const char *p, *q; + p = list; + p += strspn(p, APPDEFAULT_LIST_SEPARATORS); + while ((p != NULL) && (*p != '\0')) { + q = p + strcspn(p, APPDEFAULT_LIST_SEPARATORS); + if ((item->len == q - p) && + (memcmp(item->data, p, q - p) == 0)) { + return PR_TRUE; + } + p = q + strspn(q, APPDEFAULT_LIST_SEPARATORS); + } + return PR_FALSE; +} + +/* Check if the certificate has a subjectAltName value matching the DNS name. */ +static SECStatus +cert_san_matches_dns_for_realm(struct module_context *mcontext, + CERTCertificate *cert, + krb5_context kcontext, krb5_principal server, + PRBool *matches) +{ + struct subject_alt_name **names; + SECItem san_value, unparsed_name, name; + SECOidData *oid; + CERTName *subject; + CERTRDN *rdn; + CERTAVA *ava; + char *unparsed, *trusted_servers; + int i, j; + + /* Read the list of trusted servers. */ + krb5_appdefault_string(kcontext, "pkinit", + krb5_princ_realm(kcontext, server), + "trusted_servers", "", &trusted_servers); + + /* Pick apart the name. Windows accepts a host name in the CN part of + * the subject name, so we should do the same. */ + subject = &cert->subject; + for (i = 0; + (subject->rdns != NULL) && (subject->rdns[i] != NULL); + i++) { + rdn = subject->rdns[i]; + for (j = 0; + (rdn->avas != NULL) && (rdn->avas[j] != NULL); + j++) { + ava = rdn->avas[j]; + oid = SECOID_FindOID(&ava->type); + if ((oid != NULL) && + (oid->offset == SEC_OID_AVA_COMMON_NAME)) { + if (check_item_in_list(&ava->value, + trusted_servers)) { + pkinit_debug(mcontext, 2, + "Found CN=%.*s in " + "subject name.\n", + ava->value.len, + ava->value.data); + *matches = PR_TRUE; + } + } + } + } + + /* Find the subjectAltName extension. */ + if (CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &san_value) != SECSuccess) { + pkinit_debug(mcontext, 2, + "No subjectAltName extension in cert while " + "checking for matching DNS name.\n"); + return SECFailure; + } + + /* Split up the subjectAltName sequence. */ + names = NULL; + if (SEC_ASN1DecodeItem(cert->arena, &names, + san_template, &san_value) != SECSuccess) { + pkinit_debug(mcontext, 2, + "Error parsing subjectAltName extension.\n"); + return SECFailure; + } + + /* Iterate over all of the values. */ + *matches = PR_FALSE; + for (i = 0; (names != NULL) && (names[i] != NULL) && !(*matches); i++) { + switch (names[i]->subject_alt_name_type) { + case subject_alt_name_dnsname: + name = names[i]->subject_alt_name_value.dnsname; + pkinit_debug(mcontext, 2, "SAN %d is DNS: \"%.*s\".\n", + i + 1, name.len, name.data); + if (check_item_in_list(&name, trusted_servers)) { + *matches = PR_TRUE; + } + break; + default: + pkinit_debug(mcontext, 2, + "SAN %d is type %d, not an otherName.\n", + i + 1, names[i]->subject_alt_name_type); + break; + } + } + + pkinit_debug(mcontext, 2, "%d subjectAltName values found\n", i); + return SECSuccess; +} + /* Check if the certificate subjectAltName UPN value matches the principal. */ static SECStatus cert_san_matches_upn(struct module_context *mcontext, CERTCertificate *cert, @@ -536,7 +664,7 @@ PRBool *matches) { struct subject_alt_name **names; - SECItem san_value, unparsed_name, ms_upn_name; + SECItem san_value, unparsed_name, ms_upn_name, unparsed_name_lower; char *unparsed; int i; @@ -567,7 +695,19 @@ return SECFailure; } unparsed_name.data = (unsigned char *) unparsed; - unparsed_name.len = strlen(unparsed); + unparsed_name.len = strlen(unparsed) + 1; + + if (SECITEM_CopyItem (cert->arena, &unparsed_name_lower, + &unparsed_name) != SECSuccess) { + pkinit_debug(mcontext, 2, + "Error copying unparsed_name SECItem.\n"); + krb5_free_unparsed_name(kcontext, unparsed); + return SECFailure; + } + + tolower_secitem (&unparsed_name_lower); + --unparsed_name.len; + --unparsed_name_lower.len; /* Iterate over all of the values. */ *matches = PR_FALSE; @@ -590,6 +730,11 @@ pkinit_debug(mcontext, 2, "UPN Matched.\n"); *matches = PR_TRUE; + } else if (SECITEM_ItemsAreEqual (&ms_upn_name, + &unparsed_name_lower)) { + pkinit_debug(mcontext, 2, + "UPN Matched (lower).\n"); + *matches = PR_TRUE; } else { pkinit_debug(mcontext, 2, "\"%.*s\" != " @@ -766,7 +911,11 @@ i++) { if (cert_matches_epi(mcontext, pool, candidate, banned_cert_list[i])) { + pkinit_debug(mcontext, 2, + "Certificate \"%s\" is not to be used.\n", + issuer->subjectName); saw_banned_cert = PR_TRUE; + break; } } /* Now walk the certifying chain, checking for restricted and banned CA @@ -803,6 +952,10 @@ i++) { if (cert_matches_epi(mcontext, pool, issuer, banned_cert_list[i])) { + pkinit_debug(mcontext, 2, + "Issuer \"%s\" is not to " + "be used.\n", + issuer->subjectName); saw_banned_cert = PR_TRUE; } } @@ -821,10 +974,13 @@ } /* Banned cert somewhere = don't use. */ if (saw_banned_cert) { + pkinit_debug(mcontext, 2, "Marking certificate as not-okay.\n"); return PR_FALSE; } /* If we had a list of valid CAs, and we didn't see one, don't use. */ if ((restricted_ca_list != NULL) && !saw_restricted_ca) { + pkinit_debug(mcontext, 2, + "Certificate was not issued by a given CA.\n"); return PR_FALSE; } /* The PKINIT certificate will have our principal name encoded as a @@ -867,6 +1023,25 @@ cert_flags &= ~CERT_NAME_MASK; } } + /* A Domain Controller certificate may only have a trusted server's DNS + * name encoded in it. */ + if (cert_flags & CERT_MATCHES_TRUSTED_SERVERS) { + cert_flags &= ~CERT_MATCHES_TRUSTED_SERVERS; + if ((cert_san_matches_dns_for_realm(mcontext, candidate, + kcontext, principal, + &match) != SECSuccess) || + !match) { + if (!(cert_flags & CERT_NAME_MASK)) { + /* Return the error if we have no more name + * types to try matching. */ + return PR_FALSE; + } + } else { + /* No need to check other name types. */ + pkinit_debug(mcontext, 2, "DNS name matched.\n"); + cert_flags &= ~CERT_NAME_MASK; + } + } /* The certificate may not have a principal name included as a SAN * value, so check the (potentially very long) list for a match. */ if (cert_flags & CERT_MATCHES_DN_LIST) { @@ -984,13 +1159,19 @@ continue; } - pkinit_debug(mcontext, 2, "Checking \"%s\".\n", cert->nickname); + pkinit_debug(mcontext, 2, "Checking \"%s\" (\"%s\").\n", + cert->nickname ? cert->nickname : "(NULL)", + cert->subjectName); if (cert_is_preferred(mcontext, pool, cert, restricted_ca_list, banned_cert_list, additional_dn_list, kcontext, principal, cert_flags)) { pkinit_debug(mcontext, 2, - "Using \"%s\"\n", cert->nickname); + "Certificate \"%s\" (\"%s\") " + "looks okay.\n", + cert->nickname ? + cert->nickname : "(NULL)", + cert->subjectName); /* CERT_DestroyCertList will free 'cert', so we need * to make a copy of it. */ ret = CERT_DupCertificate(cert); @@ -1386,11 +1567,46 @@ verify_node != NULL; verify_node = verify_node->next) { if (verify_node->error != 0) { - pkinit_debug(mcontext, 1, - "Error at \"%s\": %s\n", - verify_node->cert->subjectName, - PR_ErrorToString(verify_node->error, - PR_LANGUAGE_I_DEFAULT)); + switch (verify_node->error) { + case SEC_ERROR_EXPIRED_CERTIFICATE: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + verify_node->cert->subjectName, + "certificate expired"); + break; + case SEC_ERROR_UNKNOWN_SIGNER: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + verify_node->cert->subjectName, + "unknown signer"); + break; + case SEC_ERROR_UNTRUSTED_ISSUER: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + verify_node->cert->subjectName, + "untrusted issuer"); + case SEC_ERROR_INADEQUATE_KEY_USAGE: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + verify_node->cert->subjectName, + "inadequate key usage"); + break; + case SEC_ERROR_REVOKED_CERTIFICATE: + case SEC_ERROR_REVOKED_CERTIFICATE_CRL: + case SEC_ERROR_REVOKED_CERTIFICATE_OCSP: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + verify_node->cert->subjectName, + "certificate revoked"); + break; + default: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + verify_node->cert->subjectName, + PR_ErrorToString(verify_node->error, + PR_LANGUAGE_I_DEFAULT)); + break; + } } } CERT_DestroyCertArray(other_certs, n_other_certs); @@ -1493,6 +1709,27 @@ return 0; } + /* Try it the new way. */ + if ((cert_san_matches_dns_for_realm(mcontext, kdc, + kcontext, server, + &matches) == SECSuccess) && + matches == PR_TRUE) { + if (((cert_eku_matches_tag(mcontext, kdc, + SEC_OID_EXT_KEY_USAGE_SERVER_AUTH, + &matches) == SECSuccess) && + matches) || + ((cert_eku_matches_oid(mcontext, kdc, + &oid_pkinit_key_purpose_kdc.oid, + &matches) == SECSuccess) && + matches)) { + pkinit_debug(mcontext, 2, + "KDC certificate has DNS name which is " + "trusted for realm, and has correct EKU " + "value.\n"); + return 0; + } + } + /* Otherwise do the best we can to see if it's a valid Windows * domain controller certificate. First, check if the * certificateTemplateName has a good value. */ @@ -1613,11 +1850,47 @@ vnode != NULL; vnode = vnode->next) { if (vnode->error != 0) { - pkinit_debug(mcontext, 1, - "Error at \"%s\": %s\n", - vnode->cert->subjectName, - PR_ErrorToString(vnode->error, - PR_LANGUAGE_I_DEFAULT)); + switch (vnode->error) { + case SEC_ERROR_EXPIRED_CERTIFICATE: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + vnode->cert->subjectName, + "certificate expired"); + break; + case SEC_ERROR_UNKNOWN_SIGNER: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + vnode->cert->subjectName, + "unknown signer"); + break; + case SEC_ERROR_UNTRUSTED_ISSUER: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + vnode->cert->subjectName, + "untrusted issuer"); + break; + case SEC_ERROR_INADEQUATE_KEY_USAGE: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + vnode->cert->subjectName, + "inadequate key usage"); + break; + case SEC_ERROR_REVOKED_CERTIFICATE: + case SEC_ERROR_REVOKED_CERTIFICATE_CRL: + case SEC_ERROR_REVOKED_CERTIFICATE_OCSP: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + vnode->cert->subjectName, + "certificate revoked"); + break; + default: + pkinit_debug(mcontext, 1, + "Error at \"%s\": %s\n", + vnode->cert->subjectName, + PR_ErrorToString(vnode->error, + PR_LANGUAGE_I_DEFAULT)); + break; + } } } CERT_DestroyCertArray(other_certs, n_other_certs); @@ -1639,6 +1912,76 @@ *error_epis = epi; return KRB5KDC_ERR_INVALID_CERTIFICATE; break; + case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE: + case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE: + case SEC_ERROR_OCSP_MALFORMED_REQUEST: + case SEC_ERROR_OCSP_SERVER_ERROR: + case SEC_ERROR_OCSP_TRY_SERVER_LATER: + case SEC_ERROR_OCSP_REQUEST_NEEDS_SIG: + case SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST: + case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS: + case SEC_ERROR_OCSP_UNKNOWN_CERT: + case SEC_ERROR_OCSP_NOT_ENABLED: + case SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER: + case SEC_ERROR_OCSP_MALFORMED_RESPONSE: + case SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE: + case SEC_ERROR_OCSP_FUTURE_RESPONSE: + case SEC_ERROR_OCSP_OLD_RESPONSE: + case SEC_ERROR_OCSP_INVALID_SIGNING_CERT: + /* encode the invalid certificate list */ + epi = NULL; + for (vnode = verify_log.head; + vnode != NULL; + vnode = vnode->next) { + switch (vnode->error) { + case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE: + case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE: + case SEC_ERROR_OCSP_MALFORMED_REQUEST: + case SEC_ERROR_OCSP_SERVER_ERROR: + case SEC_ERROR_OCSP_TRY_SERVER_LATER: + case SEC_ERROR_OCSP_REQUEST_NEEDS_SIG: + case SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST: + case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS: + case SEC_ERROR_OCSP_UNKNOWN_CERT: + case SEC_ERROR_OCSP_NOT_ENABLED: + case SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER: + case SEC_ERROR_OCSP_MALFORMED_RESPONSE: + case SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE: + case SEC_ERROR_OCSP_FUTURE_RESPONSE: + case SEC_ERROR_OCSP_OLD_RESPONSE: + case SEC_ERROR_OCSP_INVALID_SIGNING_CERT: + epi = bcms_make_external_principal_identifier_list(mcontext, pool, epi, vnode->cert); + break; + default: + break; + } + } + *error_epis = epi; + return KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN; + break; + case SEC_ERROR_REVOKED_CERTIFICATE: + case SEC_ERROR_REVOKED_CERTIFICATE_CRL: + case SEC_ERROR_REVOKED_CERTIFICATE_OCSP: + /* encode the invalid certificate list */ + epi = NULL; + for (vnode = verify_log.head; + vnode != NULL; + vnode = vnode->next) { + switch (vnode->error) { + case SEC_ERROR_REVOKED_CERTIFICATE: + case SEC_ERROR_REVOKED_CERTIFICATE_CRL: + case SEC_ERROR_REVOKED_CERTIFICATE_OCSP: + epi = bcms_make_external_principal_identifier_list(mcontext, pool, epi, vnode->cert); + break; + default: + break; + } + } + *error_epis = epi; + return KRB5KDC_ERR_REVOKED_CERTIFICATE; + break; + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_UNKNOWN_SIGNER: case SEC_ERROR_UNTRUSTED_ISSUER: case SEC_ERROR_UNTRUSTED_CERT: default: @@ -1662,8 +2005,9 @@ NULL, NULL, additional_dn_list, kcontext, client, cert_flags)) { pkinit_debug(mcontext, 1, - "Client certificate didn't match client name.\n"); - return KRB5KDC_ERR_CERTIFICATE_MISMATCH; + "Client certificate for \"%s\" didn't match " + "client name.\n", client_cert->subjectName); + return KRB5KDC_ERR_CLIENT_NAME_MISMATCH; } /* Check that it matches the desired EKU. */ Index: src/certs.h =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/certs.h,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- src/certs.h 5 Feb 2007 16:19:40 -0000 1.16 +++ src/certs.h 23 Apr 2007 21:45:43 -0000 1.17 @@ -23,7 +23,7 @@ #ifndef certs_h #define certs_h -#ident "$Id: certs.h,v 1.16 2007/02/05 16:19:40 nalin Exp $" +#ident "$Id: certs.h,v 1.17 2007/04/23 21:45:43 nalin Exp $" #include "bcmst.h" #include "pkinit.h" @@ -60,21 +60,23 @@ #define CERT_MATCHES_UPN_SAN (1 << 0) #define CERT_MATCHES_KRB_SAN (1 << 1) #define CERT_MATCHES_DN_LIST (1 << 2) +#define CERT_MATCHES_TRUSTED_SERVERS (1 << 3) #define CERT_NAME_MASK (CERT_MATCHES_UPN_SAN |\ CERT_MATCHES_KRB_SAN |\ - CERT_MATCHES_DN_LIST) -#define CERT_MATCHES_CLIENT_AUTH_EKU (1 << 3) -#define CERT_MATCHES_SERVER_AUTH_EKU (1 << 4) -#define CERT_MATCHES_PKINIT_CLIENT_EKU (1 << 5) -#define CERT_MATCHES_PKINIT_KDC_EKU (1 << 6) -#define CERT_MATCHES_MS_SMARTCARD_EKU (1 << 7) + CERT_MATCHES_DN_LIST |\ + CERT_MATCHES_TRUSTED_SERVERS) +#define CERT_MATCHES_CLIENT_AUTH_EKU (1 << 4) +#define CERT_MATCHES_SERVER_AUTH_EKU (1 << 5) +#define CERT_MATCHES_PKINIT_CLIENT_EKU (1 << 6) +#define CERT_MATCHES_PKINIT_KDC_EKU (1 << 7) +#define CERT_MATCHES_MS_SMARTCARD_EKU (1 << 8) #define CERT_EKU_MASK (CERT_MATCHES_CLIENT_AUTH_EKU |\ CERT_MATCHES_PKINIT_CLIENT_EKU |\ CERT_MATCHES_PKINIT_KDC_EKU |\ CERT_MATCHES_MS_SMARTCARD_EKU) -#define CERT_MATCHES_DC_CERT_TEMPLATE (1 << 8) -#define CERT_MATCHES_TRUSTED_GUID (1 << 9) +#define CERT_MATCHES_DC_CERT_TEMPLATE (1 << 9) +#define CERT_MATCHES_TRUSTED_GUID (1 << 10) CERTCertificate *cert_find_preferred_cert(struct module_context *mcontext, PLArenaPool *pool, @@ -111,7 +113,9 @@ const SECItem *cert_get_oid_dh_public_number(void); const SECItem *cert_get_oid_dh_key_agreement(void); const SECItem *cert_get_oid_pkinit_auth_data(void); +const SECOidData *cert_get_oid_pkinit_auth_data_oid(void); const SECItem *cert_get_oid_pkinit_rkey_data(void); +const SECOidData *cert_get_oid_pkinit_rkey_data_oid(void); const SECItem *cert_get_oid_pkinit_dhkey_data(void); SECItem **cert_get_guids(struct module_context *mcontext, Index: src/commont.c =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/commont.c,v retrieving revision 1.16 retrieving revision 1.18 diff -u -r1.16 -r1.18 --- src/commont.c 5 Feb 2007 16:20:29 -0000 1.16 +++ src/commont.c 7 Feb 2007 00:17:26 -0000 1.18 @@ -20,7 +20,7 @@ * USA. */ -#ident "$Id: commont.c,v 1.16 2007/02/05 16:20:29 nalin Exp $" +#ident "$Id: commont.c,v 1.18 2007/02/07 00:17:26 nalin Exp $" #include "../config.h" @@ -314,6 +314,16 @@ }; const SEC_ASN1Template +common_aes_cbc_parameters_template[] = { + { + .kind = SEC_ASN1_OCTET_STRING, + .offset = offsetof(struct aes_cbc_parameters, iv), + .sub = &SEC_OctetStringTemplate, + .size = sizeof(SECItem), + }, +}; + +const SEC_ASN1Template common_sequence_of_any_template[] = { { .kind = SEC_ASN1_SEQUENCE_OF, @@ -430,11 +440,24 @@ } SECItem * -common_encode_integer(struct module_context *mcontext, - PLArenaPool *pool, SECItem *bits) +common_encode_unsigned_integer(struct module_context *mcontext, + PLArenaPool *pool, SECItem *bits) { - SECItem encoded; - if (SEC_ASN1EncodeItem(pool, &encoded, bits, + SECItem encoded, *unsigned_raw; + if (bits->data[0] & 0x80) { + /* We want an unsigned number, but the high bit is set, so if + * we just treat it as an integer, it'll appear to be signed + * and negative. */ + unsigned_raw = SECITEM_AllocItem(pool, NULL, bits->len + 1); + if (unsigned_raw == NULL) { + pkinit_debug(mcontext, 0, "Out of memory.\n"); + } + unsigned_raw->data[0] = 0; + memcpy(unsigned_raw->data + 1, bits->data, bits->len); + } else { + unsigned_raw = bits; + } + if (SEC_ASN1EncodeItem(pool, &encoded, unsigned_raw, SEC_IntegerTemplate) == &encoded) { return SECITEM_ArenaDupItem(pool, &encoded); } @@ -560,7 +583,7 @@ common_decode_sequence_of_algorithm_identifier(struct module_context *mcontext, PLArenaPool *pool, SECItem *item) { - struct algorthm_identifier **ai; + struct algorithm_identifier **ai; ai = NULL; if (SEC_ASN1DecodeItem(pool, &ai, @@ -974,6 +997,153 @@ return SECSuccess; } +static SECStatus +common_generate_content_encryption_key_aes(struct module_context *mcontext, + PLArenaPool *pool, + int key_size_bits, + krb5_context kcontext, + struct algorithm_identifier *alg, + SECItem *content_enc_key, + CK_MECHANISM *mech, + int *block_size) +{ + SECOidData *oid; + SECItem encoded; + krb5_data octets; + struct aes_cbc_parameters params; + char *pk_params; + unsigned int i, parity; + + /* Generate a key. */ + content_enc_key->len = key_size_bits / 8; + content_enc_key->data = PORT_ArenaZAlloc(pool, content_enc_key->len); + if (content_enc_key->data == NULL) { + pkinit_debug(mcontext, 0, "Out of memory.\n"); + return SECFailure; + } + octets.length = content_enc_key->len; + octets.data = (char *) content_enc_key->data; + if (krb5_c_random_make_octets(kcontext, &octets) != 0) { + pkinit_debug(mcontext, 0, "Out of entropy.\n"); + return SECFailure; + } + + /* Set up some parameters: first, the OID of the algorithm. */ + switch (key_size_bits) { + case 128: + oid = SECOID_FindOIDByTag(SEC_OID_AES_128_CBC); + break; + case 192: + oid = SECOID_FindOIDByTag(SEC_OID_AES_192_CBC); + break; + case 256: + oid = SECOID_FindOIDByTag(SEC_OID_AES_256_CBC); + break; + default: + pkinit_debug(mcontext, 0, "Internal error: no knowledge of the " + "AES encryption algorithm (%d bits).\n", + key_size_bits); + return SECFailure; + } + if (oid == NULL) { + pkinit_debug(mcontext, 0, "Internal error: no knowledge of the " + "AES-%d encryption algorithm.\n", key_size_bits); + return SECFailure; + } + alg->algorithm = oid->oid; + + /* Generate an IV. */ + params.iv.len = 16; + params.iv.data = PORT_ArenaZAlloc(pool, params.iv.len); + if (params.iv.data == NULL) { + pkinit_debug(mcontext, 0, "Out of memory.\n"); + return SECFailure; + } + octets.length = params.iv.len; + octets.data = (char *) params.iv.data; + if (krb5_c_random_make_octets(kcontext, &octets) != 0) { + pkinit_debug(mcontext, 0, "Out of entropy.\n"); + return SECFailure; + } + + /* Encode the parameters structure. */ + if (SEC_ASN1EncodeItem(pool, &encoded, ¶ms, + common_aes_cbc_parameters_template) != &encoded) { + pkinit_debug(mcontext, 0, "Error encoding parameters.\n"); + return SECFailure; + } + alg->parameters = SECITEM_ArenaDupItem(pool, &encoded); + if (alg->parameters == NULL) { + pkinit_debug(mcontext, 0, "Out of memory.\n"); + return SECFailure; + } + + /* Build the parameter block for the PKCS11 layer. */ + mech->mechanism = CKM_AES_CBC; + pk_params = PORT_ArenaZAlloc(pool, 16); + if (pk_params == NULL) { + pkinit_debug(mcontext, 0, "Out of memory.\n"); + return SECFailure; + } + memcpy(&pk_params[0], params.iv.data, 16); + mech->pParameter = pk_params; + mech->ulParameterLen = 16; + + /* The consumer of the key is going to need to know how to pad the + * data for encryption. */ + *block_size = 16; + + return SECSuccess; +} + +SECStatus +common_generate_content_encryption_key_aes128(struct module_context *mcontext, + PLArenaPool *pool, + krb5_context kcontext, + struct algorithm_identifier *alg, + SECItem *content_enc_key, + CK_MECHANISM *mech, + int *block_size) +{ + return common_generate_content_encryption_key_aes(mcontext, pool, 128, + kcontext, + alg, + content_enc_key, + mech, block_size); +} + +SECStatus +common_generate_content_encryption_key_aes192(struct module_context *mcontext, + PLArenaPool *pool, + krb5_context kcontext, + struct algorithm_identifier *alg, + SECItem *content_enc_key, + CK_MECHANISM *mech, + int *block_size) +{ + return common_generate_content_encryption_key_aes(mcontext, pool, 192, + kcontext, + alg, + content_enc_key, + mech, block_size); +} + +SECStatus +common_generate_content_encryption_key_aes256(struct module_context *mcontext, + PLArenaPool *pool, + krb5_context kcontext, + struct algorithm_identifier *alg, + SECItem *content_enc_key, + CK_MECHANISM *mech, + int *block_size) +{ + return common_generate_content_encryption_key_aes(mcontext, pool, 256, + kcontext, + alg, + content_enc_key, + mech, block_size); +} + SECStatus common_generate_content_encryption_key(struct module_context *mcontext, PLArenaPool *pool, @@ -1011,6 +1181,42 @@ return status; } break; + case SEC_OID_AES_128_CBC: + status = common_generate_content_encryption_key_aes128(mcontext, + pool, + kcontext, + algorithm, + content_enc_key, + mech, + block_size); + if (status == SECSuccess) { + return status; + } + break; + case SEC_OID_AES_192_CBC: + status = common_generate_content_encryption_key_aes192(mcontext, + pool, + kcontext, + algorithm, + content_enc_key, + mech, + block_size); + if (status == SECSuccess) { + return status; + } + break; + case SEC_OID_AES_256_CBC: + status = common_generate_content_encryption_key_aes256(mcontext, + pool, + kcontext, + algorithm, + content_enc_key, + mech, + block_size); + if (status == SECSuccess) { + return status; + } + break; default: break; } @@ -1032,6 +1238,16 @@ content_enc_key, mech, block_size); + if (status == SECSuccess) { + return status; + } + status = common_generate_content_encryption_key_aes256(mcontext, + pool, + kcontext, + algorithm, + content_enc_key, + mech, + block_size); if (status == SECSuccess) { return status; } Index: src/commont.h =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/commont.h,v retrieving revision 1.13 retrieving revision 1.15 diff -u -r1.13 -r1.15 --- src/commont.h 5 Feb 2007 16:20:29 -0000 1.13 +++ src/commont.h 7 Feb 2007 00:17:26 -0000 1.15 @@ -23,7 +23,7 @@ #ifndef commont_h #define commont_h -#ident "$Id: commont.h,v 1.13 2007/02/05 16:20:29 nalin Exp $" +#ident "$Id: commont.h,v 1.15 2007/02/07 00:17:26 nalin Exp $" #include <krb5.h> #include <nspr.h> @@ -68,6 +68,10 @@ SECItem iv; }; +struct aes_cbc_parameters { + SECItem iv; +}; + extern const SEC_ASN1Template common_general_string_template[]; extern const SEC_ASN1Template common_sequence_of_any_template[]; @@ -93,8 +97,8 @@ struct domain_parameters *info); SECItem *common_encode_bitstring(struct module_context *mcontext, PLArenaPool *pool, SECItem *bits); -SECItem *common_encode_integer(struct module_context *mcontext, - PLArenaPool *pool, SECItem *bits); +SECItem *common_encode_unsigned_integer(struct module_context *mcontext, + PLArenaPool *pool, SECItem *bits); SECItem *common_encode_octetstring(struct module_context *mcontext, PLArenaPool *pool, SECItem *bits); SECItem *common_encode_objectid(struct module_context *mcontext, Index: src/oakley.c =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/oakley.c,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- src/oakley.c 5 Feb 2007 16:23:16 -0000 1.6 +++ src/oakley.c 6 Feb 2007 01:58:10 -0000 1.7 @@ -20,13 +20,34 @@ * USA. */ -#ident "$Id: oakley.c,v 1.6 2007/02/05 16:23:16 nalin Exp $" +#ident "$Id: oakley.c,v 1.7 2007/02/06 01:58:10 nalin Exp $" #include "../config.h" #include <keythi.h> #include "oakley.h" #include "pkinit.h" +static void +derdump(const char *text, SECItem *item) +{ +#if DEBUG + FILE *pp; + unsigned int i; + pp = popen("derdump", "w"); + if (pp) { + if (text) { + fprintf(stderr, "%s: dumping %d bytes.\n", text, + item->len); + } else { + fprintf(stderr, "Dumping %d bytes.\n", item->len); + } + for (i = 0; i < item->len; i++) { + fputc(item->data[i] & 0xff, pp); + } + pclose(pp); + } +#endif +} struct oakley_group { int identifier; unsigned int bits; @@ -365,6 +386,7 @@ encoded_params = common_encode_domain_parameters(mcontext, pool, params); + derdump("DH Parameters", encoded_params); ident.algorithm = oid->oid; ident.parameters = encoded_params; idents = common_make_algorithm_identifier_list(mcontext, Index: src/pkinit.c =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/pkinit.c,v retrieving revision 1.43 diff -u -r1.43 pkinit.c --- src/pkinit.c 5 Feb 2007 16:27:27 -0000 1.43 +++ src/pkinit.c 2 May 2007 16:06:11 -0000 @@ -20,7 +20,7 @@ * USA. */ -#ident "$Id: pkinit.c,v 1.43 2007/02/05 16:27:27 nalin Exp $" +#ident "$Id: pkinit.c,v 1.52 2007/04/25 16:59:34 nalin Exp $" #include "../config.h" @@ -31,6 +31,9 @@ #include <unistd.h> #include <krb5/krb5.h> +#ifndef HAVE_KRB5_GIC_OPT_PA_DATA +typedef struct _krb5_gic_opt_pa_data krb5_gic_opt_pa_data; +#endif #include <krb5/preauth_plugin.h> #include <nss.h> @@ -46,7 +49,7 @@ #include "map-file.h" #include "pkinit.h" -#ifdef USE_PAL_BACKPORT +#ifdef PKINIT_USE_PAL_BACKPORT #include "backport-errors.h" #define preauthentication_client_0 preauthentication_client_0_backport_1_6 #define preauthentication_server_0 preauthentication_server_0_backport_1_6 @@ -70,6 +73,48 @@ #define _(_x) _x #endif +static void +derdump(const char *text, SECItem *item) +{ +#if DEBUG + FILE *pp; + unsigned int i; + pp = popen("derdump", "w"); + if (pp) { + if (text) { + fprintf(stderr, "%s: dumping %d bytes.\n", text, + item->len); + } else { + fprintf(stderr, "Dumping %d bytes.\n", item->len); + } + for (i = 0; i < item->len; i++) { + fputc(item->data[i] & 0xff, pp); + } + pclose(pp); + } +#endif +} +static void +dump(const char *text, SECItem *item) +{ +#if DEBUG + FILE *pp; + unsigned int i; + pp = popen("od -t x1c", "w"); + if (pp) { + if (text) { + fprintf(stderr, "%s: dumping %d bytes.\n", text, + item->len); + } else { + fprintf(stderr, "Dumping %d bytes.\n", item->len); + } + for (i = 0; i < item->len; i++) { + fputc(item->data[i] & 0xff, pp); + } + pclose(pp); + } +#endif +} static krb5_preauthtype supported_client_pa_types[] = { KRB5_PADATA_PK_AS_REP_OLD, KRB5_PADATA_PK_AS_REQ, KRB5_PADATA_PK_AS_REP, @@ -134,7 +179,7 @@ int pkinit, ocsp, try_dh, is_hw, preferred_group, minimum_dh_prime_size; int trust_pkinit_san, trust_upn_san; int debug_level, debug_syslog, debug_stderr; - char *p, *mappings_file; + char *p, *mappings_file, *dbdir; PRBool owns_nss; struct module_context *context; krb5_data *default_realm = NULL; @@ -167,10 +212,22 @@ owns_nss = !NSS_IsInitialized(); if (owns_nss) { if (server) { - ret = NSS_Init(DEFAULT_PKINIT_SERVER_DBDIR); + dbdir = DEFAULT_PKINIT_SERVER_DBDIR; + krb5_appdefault_string(kcontext, "pkinit", + default_realm, + "server_database", + DEFAULT_PKINIT_SERVER_DBDIR, + &dbdir); } else { - ret = NSS_Init(DEFAULT_PKINIT_CLIENT_DBDIR); + dbdir = DEFAULT_PKINIT_CLIENT_DBDIR; + krb5_appdefault_string(kcontext, "pkinit", + default_realm, + "client_database", + DEFAULT_PKINIT_CLIENT_DBDIR, + &dbdir); } + pkinit_debug(NULL, 2, "Using database in \"%s\".\n", dbdir); + ret = NSS_Init(dbdir); if (ret != SECSuccess) { char *err = NULL; PRInt32 errlen = PR_GetErrorTextLength(); @@ -377,7 +434,21 @@ "Prompter callback called with no pool.\n"); return NULL; } - +#if 0 + if (args->gak_data != NULL && + args->gak_data->data != NULL && + args->gak_data->data[0] != '\0') { + int answer_len; + pkinit_debug(args->mcontext, 2, "We have some gak_data.\n"); + answer_len = strnlen (args->gak_data->data, args->gak_data->length) + 1; + answer = PR_Malloc(answer_len); + if (answer != NULL) { + memcpy(answer, args->gak_data->data, answer_len); + answer[answer_len - 1] = '\0'; + } + return answer; + } +#endif /* Get the name of the token. */ name = PK11_GetTokenName(slot); if (name == NULL) { @@ -449,9 +520,24 @@ } static krb5_error_code +client_gic_opts(krb5_context kcontext, void *plugin_context, + krb5_get_init_creds_opt *opt, + const char *attr, const char *value) +{ + struct module_context *context; + context = plugin_context; + pkinit_debug(context, 2, "get_init_creds_opt(\"%s\") = \"%s\"\n", + attr, value); + return 0; +} + +static krb5_error_code client_process(krb5_context kcontext, void *plugin_context, void *unused_request_context, +#ifndef PKINIT_CLIENT_PROCESS_MISSING_OPT + krb5_get_init_creds_opt *opt, +#endif preauth_get_client_data_proc unused_get_data_proc, struct _krb5_preauth_client_rock *unused_rock, krb5_kdc_req *request, @@ -461,11 +547,14 @@ krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc unused_gak_fct, - void *unused_gak_data, + void *gak_data, krb5_data *unused_salt, krb5_data *unused_s2kparams, krb5_keyblock *as_key, krb5_pa_data **out_pa_data) { +#ifdef PKINIT_CLIENT_PROCESS_MISSING_OPT + krb5_get_init_creds_opt *opt = NULL; +#endif krb5_data *req, rep; krb5_pa_data *pa_req; krb5_error_code ret; @@ -494,6 +583,7 @@ pwcb_args.fct = prompter; pwcb_args.fct_data = prompter_data; pwcb_args.pool = context->pool; + pwcb_args.gak_data = gak_data; /* If we have a preferred slot, try to get a handle for it. */ preferred_slot_name = getenv("PKCS11_LOGIN_TOKEN_NAME"); @@ -517,7 +607,7 @@ kcontext, request->client, CERT_MATCHES_DN_LIST | (context->trust_pkinit_san ? - CERT_MATCHES_KRB_SAN : 0 ) | + CERT_MATCHES_KRB_SAN : 0) | (context->trust_upn_san ? CERT_MATCHES_UPN_SAN : 0)); if (cert == NULL) { @@ -527,7 +617,9 @@ } return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; } - pkinit_debug(context, 1, "Using \"%s\".\n", cert->nickname); + pkinit_debug(context, 1, "Using \"%s\" (\"%s\").\n", + cert->nickname ? cert->nickname : "(NULL)", + cert->subjectName); switch (pa_data->pa_type) { case KRB5_PADATA_PK_AS_REP_OLD: @@ -594,7 +686,8 @@ (context->trust_pkinit_san ? CERT_MATCHES_KRB_SAN : 0) | (context->trust_upn_san ? - CERT_MATCHES_UPN_SAN : 0), + CERT_MATCHES_UPN_SAN : 0) | + CERT_MATCHES_TRUSTED_SERVERS, kcontext, request, &rep, @@ -667,7 +760,8 @@ (context->trust_pkinit_san ? CERT_MATCHES_KRB_SAN : 0) | (context->trust_upn_san ? - CERT_MATCHES_UPN_SAN : 0), + CERT_MATCHES_UPN_SAN : 0) | + CERT_MATCHES_TRUSTED_SERVERS, kcontext, request, encoded_previous_request, @@ -699,6 +793,9 @@ client_try_again(krb5_context kcontext, void *plugin_context, void *unused_request_context, +#ifndef PKINIT_CLIENT_PROCESS_MISSING_OPT + krb5_get_init_creds_opt *opt, +#endif preauth_get_client_data_proc unused_get_data_proc, struct _krb5_preauth_client_rock *unused_rock, krb5_kdc_req *request, @@ -709,15 +806,18 @@ krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc unused_gak_fct, - void *unused_gak_data, + void *gak_data, krb5_data *unused_salt, krb5_data *unused_s2kparams, krb5_keyblock *as_key, krb5_pa_data **out_pa_data) { +#ifdef PKINIT_CLIENT_PROCESS_MISSING_OPT + krb5_get_init_creds_opt *opt = NULL; +#endif krb5_data *req, rep; krb5_pa_data *pa_req; krb5_error_code ret; - SECItem e_data; + SECItem e_data, *td_data; SECOidData *oid; SECStatus status; CERTCertificate *cert = NULL; @@ -728,11 +828,11 @@ struct external_principal_identifier **restricted_cas, **banned_certs; struct algorithm_identifier **allowed_algorithms; struct domain_parameters **allowed_dh_params, *dh_params; - struct authorization_datum *typed_error; + struct typed_datum **typed_errors; char *unparsed_name; struct module_context *context; unsigned long td_type; - int i, j; + int i, j, k; *out_pa_data = NULL; preferred_slot = NULL; @@ -746,6 +846,7 @@ pwcb_args.fct = prompter; pwcb_args.fct_data = prompter_data; pwcb_args.pool = context->pool; + pwcb_args.gak_data = gak_data; /* Parse the error data. */ restricted_cas = NULL; @@ -754,103 +855,95 @@ memset(&e_data, 0, sizeof(e_data)); e_data.data = (unsigned char *) error->e_data.data; e_data.len = error->e_data.length; - typed_error = pkinit_decode_authorization_datum(context, - context->pool, - &e_data); - if (typed_error != NULL) { - status = SEC_ASN1DecodeInteger(&typed_error->ad_type, + typed_errors = pkinit_decode_typed_data(context, + context->pool, + &e_data); + if (typed_errors == NULL) { + pkinit_debug(context, 1, "Error parsing typed-data from the " + "KDC, not retrying.\n"); + derdump("e-data", &e_data); + return error->error; + } + for (i = 0; (typed_errors[i] != NULL); i++) { + status = SEC_ASN1DecodeInteger(&typed_errors[i]->td_type, &td_type); if (status != SECSuccess) { pkinit_debug(context, 1, - "Error parsing typed data.\n"); + "Error parsing typed data type.\n"); return error->error; } - } - switch (error->error + KRB5KDC_ERR_NONE) { - case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE: - if (typed_error != NULL) { - if (td_type == 104) { - restricted_cas = bcms_decode_sequence_of_external_principal_identifier(context, context->pool, &typed_error->ad_data); - } else { - pkinit_debug(context, 1, - "Error data type mismatch.\n"); - return error->error; + pkinit_debug(context, 2, "Received typed-data %d.\n", td_type); + td_data = &typed_errors[i]->td_data; + switch (td_type) { + case 104: + restricted_cas = bcms_decode_sequence_of_external_principal_identifier(context, context->pool, td_data); + derdump("Trusted certifiers", td_data); + for (j = 0; + (restricted_cas != NULL) && + (restricted_cas[j] != NULL); + j++) { + continue; } - } else { - pkinit_debug(context, 1, "Error data missing.\n"); - return error->error; - } - break; - case KRB5KDC_ERR_INVALID_CERTIFICATE: - if (typed_error != NULL) { - if (td_type == 105) { - banned_certs = bcms_decode_sequence_of_external_principal_identifier(context, context->pool, &typed_error->ad_data); - } else { - pkinit_debug(context, 1, - "Error data type mismatch.\n"); - return error->error; + pkinit_debug(context, 2, + "KDC sent list of %d trusted " + "certifiers.\n", j); + break; + case 105: + banned_certs = bcms_decode_sequence_of_external_principal_identifier(context, context->pool, td_data); + derdump("Invalid certificates", td_data); + for (j = 0; + (banned_certs != NULL) && + (banned_certs[j] != NULL); + j++) { + continue; } - } else { - pkinit_debug(context, 1, "Error data missing.\n"); - return error->error; - } - break; - case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED: - if (typed_error != NULL) { - if (td_type == 109) { - allowed_algorithms = common_decode_sequence_of_algorithm_identifier(context, context->pool, &typed_error->ad_data); - if (allowed_algorithms != NULL) { - for (i = 0; - allowed_algorithms[i] != NULL; - i++) { + pkinit_debug(context, 2, + "KDC sent list of %d invalid " + "certificates.\n", j); + break; + case 109: + allowed_algorithms = common_decode_sequence_of_algorithm_identifier(context, context->pool, td_data); + derdump("Sequence of allowed algorithms", td_data); + if (allowed_algorithms != NULL) { + for (j = 0; allowed_algorithms[j] != NULL; j++) { + continue; + } + allowed_dh_params = PORT_ArenaZAlloc(context->pool, + sizeof(struct domain_parameters*) * (i + 1)); + if (allowed_dh_params == NULL) { + pkinit_debug(context, 0, + "Out of memory.\n"); + break; + } + for (j = k = 0; + allowed_algorithms[j] != NULL; + j++) { + oid = SECOID_FindOID(&allowed_algorithms[j]->algorithm); + if (oid == NULL) { + pkinit_debug(context, 1, + "DH parameter is not a DH parameter?\n"); continue; } - allowed_dh_params = PORT_ArenaZAlloc(context->pool, - sizeof(struct domain_parameters*) * (i + 1)); - if (allowed_dh_params == NULL) { - pkinit_debug(context, 0, - "Out of memory.\n"); - break; + if (oid->offset != SEC_OID_X942_DIFFIE_HELMAN_KEY) { + pkinit_debug(context, 1, + "Expected DH parameters, got %s.\n", oid->desc); + continue; } - for (i = j = 0; - allowed_algorithms[i] != NULL; - i++) { - oid = SECOID_FindOID(&allowed_algorithms[i]->algorithm); - if (oid == NULL) { - pkinit_debug(context, 1, - "DH parameter is not a DH parameter?\n"); - continue; - } - if (oid->offset != SEC_OID_X942_DIFFIE_HELMAN_KEY) { - pkinit_debug(context, 1, - "Expected DH parameters, got %s.\n", oid->desc); - continue; - } - dh_params = common_decode_domain_parameters(context, context->pool, allowed_algorithms[i]->parameters); - if (dh_params == NULL) { - pkinit_debug(context, 1, - "Error parsing domain parameters.\n"); - continue; - } - allowed_dh_params[j++] = dh_params; + dh_params = common_decode_domain_parameters(context, context->pool, allowed_algorithms[j]->parameters); + if (dh_params == NULL) { + pkinit_debug(context, 1, + "Error parsing domain parameters.\n"); + continue; } + allowed_dh_params[k++] = dh_params; + dump("Allowed DH prime", &dh_params->q); + dump("Allowed DH base", &dh_params->g); } - } else { - pkinit_debug(context, 1, - "Error data type mismatch.\n"); - return error->error; } - } else { - pkinit_debug(context, 1, "Error data missing.\n"); - return error->error; + break; + default: + break; } - break; - default: - pkinit_debug(context, 1, "Can't recover from error %d (%s).\n", - error->error, - error_message(error->error + KRB5KDC_ERR_NONE)); - return error->error; - break; } /* If we have a preferred slot, try to get a handle for it. */ @@ -886,7 +979,9 @@ } return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; } - pkinit_debug(context, 1, "Using \"%s\".\n", cert->nickname); + pkinit_debug(context, 1, "Using \"%s\" (\"%s\").\n", + cert->nickname ? cert->nickname : "(NULL)", + cert->subjectName); switch (pa_data->pa_type) { case KRB5_PADATA_PK_AS_REP_OLD: @@ -1023,7 +1118,7 @@ if (context->client_nonce != NULL) { SECITEM_FreeItem(context->client_nonce, PR_TRUE); - context->client_cert = NULL; + context->client_nonce = NULL; } if (context->client_dh_nonce != NULL) { SECITEM_FreeItem(context->client_dh_nonce, @@ -1122,7 +1217,8 @@ (context->trust_pkinit_san ? CERT_MATCHES_KRB_SAN : 0 ) | (context->trust_upn_san ? - CERT_MATCHES_UPN_SAN : 0)); + CERT_MATCHES_UPN_SAN : 0) | + CERT_MATCHES_TRUSTED_SERVERS); /* Clean up. */ krb5_free_principal(kcontext, tgs_principal); @@ -1202,6 +1298,7 @@ char *unparsed_name; context = pa_plugin_context; + pkinit_debug(context, 2, "Called to handle a client request.\n"); /* If we can't create a new memory arena, bail. */ pool = PORT_NewArena(sizeof(double)); @@ -1273,7 +1370,8 @@ (context->trust_pkinit_san ? CERT_MATCHES_KRB_SAN : 0) | (context->trust_upn_san ? - CERT_MATCHES_UPN_SAN : 0), + CERT_MATCHES_UPN_SAN : 0) | + CERT_MATCHES_TRUSTED_SERVERS, &signer, &client_public_info, &client_nonce, @@ -1507,6 +1605,9 @@ .fini = client_fini, .process = client_process, .tryagain = client_try_again, +#ifndef PKINIT_CLIENT_MISSING_GIC_OPTS + .gic_opts = client_gic_opts, +#endif .flags = client_get_flags, }; struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = { Index: src/pkinit.h =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/pkinit.h,v retrieving revision 1.5 diff -u -r1.5 pkinit.h --- src/pkinit.h 25 Jan 2007 21:32:03 -0000 1.5 +++ src/pkinit.h 2 May 2007 16:06:11 -0000 @@ -39,6 +39,7 @@ krb5_prompter_fct fct; void *fct_data; PLArenaPool *pool; + krb5_data *gak_data; }; char *pkinit_pwcb(PK11SlotInfo *slot, PRBool retry, void *arg); Index: src/pkinitt.c =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/pkinitt.c,v retrieving revision 1.31 retrieving revision 1.37 diff -u -r1.31 -r1.37 --- src/pkinitt.c 5 Feb 2007 16:32:24 -0000 1.31 +++ src/pkinitt.c 25 Apr 2007 17:01:16 -0000 1.37 @@ -20,7 +20,7 @@ * USA. */ -#ident "$Id: pkinitt.c,v 1.31 2007/02/05 16:32:24 nalin Exp $" +#ident "$Id: pkinitt.c,v 1.37 2007/04/25 17:01:16 nalin Exp $" #include "../config.h" @@ -859,6 +859,42 @@ }, }; +static const SEC_ASN1Template +pkinit_typed_datum_template[] = { + { + .kind = SEC_ASN1_SEQUENCE, + .offset = 0, + .sub = NULL, + .size = sizeof(struct typed_datum), + }, + { + .kind = SEC_ASN1_CONTEXT_SPECIFIC | 0 | + SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED, + .offset = offsetof(struct typed_datum, td_type), + .sub = SEC_IntegerTemplate, + .size = sizeof(SECItem), + }, + { + .kind = SEC_ASN1_CONTEXT_SPECIFIC | 1 | + SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED, + .offset = offsetof(struct typed_datum, td_data), + .sub = SEC_OctetStringTemplate, + .size = sizeof(SECItem), + }, + {0, 0, NULL, 0}, +}; +static const SEC_ASN1Template +pkinit_typed_data_template[] = { + { + .kind = SEC_ASN1_SEQUENCE_OF, + .offset = 0, + .sub = &pkinit_typed_datum_template, + .size = 0, + }, +}; + struct draft_auth_pack * pkinit_decode_draft_auth_pack(struct module_context *mcontext, PLArenaPool *pool, SECItem *item) @@ -1099,6 +1135,40 @@ return adata; } +struct typed_datum * +pkinit_decode_typed_datum(struct module_context *mcontext, + PLArenaPool *pool, SECItem *item) +{ + struct typed_datum *adata; + + adata = PORT_ArenaZAlloc(pool, sizeof(*adata)); + if (adata == NULL) { + pkinit_debug(mcontext, 0, "Out of memory.\n"); + return NULL; + } + if (SEC_ASN1DecodeItem(pool, adata, pkinit_typed_datum_template, + item) != SECSuccess) { + pkinit_debug(mcontext, 1, "Error decoding typed datum.\n"); + return NULL; + } + return adata; +} + +struct typed_datum ** +pkinit_decode_typed_data(struct module_context *mcontext, + PLArenaPool *pool, SECItem *item) +{ + struct typed_datum **adata; + + adata = NULL; + if (SEC_ASN1DecodeItem(pool, &adata, pkinit_typed_data_template, + item) != SECSuccess) { + pkinit_debug(mcontext, 1, "Error decoding typed data.\n"); + return NULL; + } + return adata; +} + SECItem * pkinit_encode_draft_pa_pk_as_req(struct module_context *mcontext, PLArenaPool *pool, @@ -1256,8 +1326,8 @@ struct authorization_datum **adata) { SECItem encoded; - if (SEC_ASN1EncodeItem(pool, &encoded, adata, - pkinit_authorization_datum_template) == &encoded) { + if (SEC_ASN1EncodeItem(pool, &encoded, &adata, + pkinit_authorization_data_template) == &encoded) { return SECITEM_ArenaDupItem(pool, &encoded); } pkinit_debug(mcontext, 1, "Error encoding authorization_data.\n"); @@ -1285,15 +1355,36 @@ return pkinit_encode_authorization_datum(mcontext, pool, &adata); } -static SECItem * +static struct typed_datum * pkinit_create_typed_datum(struct module_context *mcontext, PLArenaPool *pool, - long d_type, - SECItem *d_data) + long td_type, + SECItem *td_data) { - /* The coding is the same, so just pretend it's authorization data. */ - return pkinit_create_authorization_datum(mcontext, pool, - d_type, d_data); + struct typed_datum *tdata; + tdata = PORT_ArenaZAlloc(pool, sizeof(*tdata)); + if (SEC_ASN1EncodeInteger(pool, + &tdata->td_type, + td_type) != &tdata->td_type) { + pkinit_debug(mcontext, 1, "Error encoding typed data type.\n"); + return NULL; + } + tdata->td_data = *td_data; + return tdata; +} + +SECItem * +pkinit_encode_typed_data(struct module_context *mcontext, + PLArenaPool *pool, + struct typed_datum **tdata) +{ + SECItem encoded; + if (SEC_ASN1EncodeItem(pool, &encoded, &tdata, + pkinit_typed_data_template) == &encoded) { + return SECITEM_ArenaDupItem(pool, &encoded); + } + pkinit_debug(mcontext, 1, "Error encoding typed_data.\n"); + return NULL; } static time_t @@ -1573,16 +1664,20 @@ krb5_context kcontext, struct external_principal_identifier **epi) { - SECItem encoded_cert_list, *td_payload; + SECItem encoded_cert_list, *td_payload, *encoded; + struct typed_datum *data[2]; td_payload = NULL; if (SEC_ASN1EncodeItem(pool, &encoded_cert_list, epi, bcms_sequence_of_external_principal_identifier_template) == &encoded_cert_list) { - td_payload = pkinit_create_typed_datum(mcontext, - pool, - 105, - &encoded_cert_list); + td_payload = &encoded_cert_list; } - return common_krb5_data_from_secitem(mcontext, kcontext, td_payload); + data[0] = pkinit_create_typed_datum(mcontext, pool, 105, td_payload); + data[1] = NULL; + encoded = pkinit_encode_typed_data(mcontext, pool, data); +#if DEBUG + derdump("e_data typed data", encoded); +#endif + return common_krb5_data_from_secitem(mcontext, kcontext, encoded); } static krb5_data * @@ -1591,16 +1686,20 @@ krb5_context kcontext, struct external_principal_identifier **epi) { - SECItem encoded_ca_list, *td_payload; + SECItem encoded_ca_list, *td_payload, *encoded; + struct typed_datum *data[2]; td_payload = NULL; - if (SEC_ASN1EncodeItem(pool, &encoded_ca_list, epi, + if (SEC_ASN1EncodeItem(pool, &encoded_ca_list, &epi, bcms_sequence_of_external_principal_identifier_template) == &encoded_ca_list) { - td_payload = pkinit_create_typed_datum(mcontext, - pool, - 104, - &encoded_ca_list); + td_payload = &encoded_ca_list; } - return common_krb5_data_from_secitem(mcontext, kcontext, td_payload); + data[0] = pkinit_create_typed_datum(mcontext, pool, 104, td_payload); + data[1] = NULL; + encoded = pkinit_encode_typed_data(mcontext, pool, data); +#if DEBUG + derdump("e_data typed data", encoded); +#endif + return common_krb5_data_from_secitem(mcontext, kcontext, encoded); } static krb5_data * @@ -1609,18 +1708,23 @@ krb5_context kcontext, unsigned int minimum_prime_size) { - SECItem encoded_dh_param_list, *td_payload; + SECItem encoded_dh_param_list, *td_payload, *encoded; struct algorithm_identifier **algorithm_ids; + struct typed_datum *data[2]; td_payload = NULL; algorithm_ids = oakley_get_groups(mcontext, pool, minimum_prime_size); - if (SEC_ASN1EncodeItem(pool, &encoded_dh_param_list, algorithm_ids, + if (SEC_ASN1EncodeItem(pool, &encoded_dh_param_list, &algorithm_ids, common_sequence_of_algorithm_identifier_template) == &encoded_dh_param_list) { - td_payload = pkinit_create_typed_datum(mcontext, - pool, - 109, - &encoded_dh_param_list); + td_payload = &encoded_dh_param_list; } - return common_krb5_data_from_secitem(mcontext, kcontext, td_payload); +#if DEBUG + derdump("allowed dh params", td_payload); +#endif + data[0] = pkinit_create_typed_datum(mcontext, pool, 109, td_payload); + data[1] = NULL; + encoded = pkinit_encode_typed_data(mcontext, pool, data); + derdump("e_data typed data", encoded); + return common_krb5_data_from_secitem(mcontext, kcontext, encoded); } /* Verify that the contents of the auth_pack are acceptible. */ @@ -1628,7 +1732,7 @@ pkinit_verify_auth_pack(struct module_context *mcontext, PLArenaPool *pool, krb5_context kcontext, - krb5_kdc_req *req_body, + krb5_kdc_req *unused_req_body, krb5_data *request_packet, krb5_deltat max_time_skew, struct auth_pack *pack) @@ -1654,10 +1758,14 @@ } } +#if 0 + RFC 4556 explicitly calls out that this test is bogus, though Windows + enforced it for draft 9. if (nonce != (unsigned long) req_body->nonce) { pkinit_debug(mcontext, 1, "Nonce mismatch.\n"); return KRB5KRB_ERR_GENERIC; } +#endif /* Check that the time is within the acceptible window. */ if ((pkauth->ctime.len != 15) || (pkauth->ctime.data[14] != 'Z')) { @@ -1710,7 +1818,7 @@ return KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED; } - pkinit_debug(mcontext, 1, "Checksum matches.\n"); + pkinit_debug(mcontext, 2, "Checksum matches.\n"); return 0; } @@ -1829,8 +1937,8 @@ PK11_FreeSlot(slot); return NULL; } - encoded_pubval = common_encode_integer(mcontext, pool, - &pub->u.dh.publicValue); + encoded_pubval = common_encode_unsigned_integer(mcontext, pool, + &pub->u.dh.publicValue); if (encoded_pubval == NULL) { pkinit_debug(mcontext, 1, "Error encoding DH public value.\n"); @@ -2017,6 +2125,9 @@ SECOidTag tag; SECItem **pparams; } supported_cms_types[] = { + {SEC_OID_AES_256_KEY_WRAP, NULL}, + {SEC_OID_AES_192_KEY_WRAP, NULL}, + {SEC_OID_AES_128_KEY_WRAP, NULL}, {SEC_OID_CMS_3DES_KEY_WRAP, NULL}, {SEC_OID_CMS_RC2_KEY_WRAP, &rc2_params_ptr}, {SEC_OID_SHA1, NULL}, @@ -2396,6 +2507,7 @@ krb5_enctype enctype, krb5_keyblock *keyblock) { PK11Context *ctx; + SECItem key_item; size_t left; unsigned int length; unsigned char buf[512 / 8]; @@ -2434,6 +2546,9 @@ key.length = left; key.enctype = enctype; +#if DEBUG + dump("Shared secret", input); +#endif while (left > 0) { ctx = PK11_CreateDigestContext(hash_alg); if (ctx == NULL) { @@ -2496,6 +2611,11 @@ left -= length; c++; } + key_item.data = key.contents; + key_item.len = key.length; +#if DEBUG + dump("Derived key", &key_item); +#endif if (krb5_copy_keyblock_contents(kcontext, &key, keyblock) != 0) { pkinit_debug(mcontext, 1, "Error copying keyblock.\n"); @@ -2604,7 +2724,7 @@ SECItem **authorized_certs, int n_authorized_certs, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 client_cert_flags, CERTCertificate **signing_cert, struct subject_public_key_info **client_dh_key, SECItem **nonce, @@ -2619,6 +2739,7 @@ struct external_principal_identifier **error_epis; struct auth_pack *auth_pack; struct content_info *content_info; + struct domain_parameters *domain_params; SECItem *padata_item, *auth_data, encoded_cas; const SECItem *roid_data; SECOidData *oid; @@ -2685,7 +2806,7 @@ "Error determining current time.\n"); return KRB5KRB_ERR_GENERIC; } - pkinit_debug(mcontext, 1, "Current time is %d, " + pkinit_debug(mcontext, 2, "Current time is %d, " "client signed request at %d.\n", now, signing_time); if (labs(now - signing_time) > max_time_skew) { @@ -2725,7 +2846,7 @@ signed_data->certificates, NULL, additional_authzdn_list, - cert_flags, + client_cert_flags, kcontext, request->client, &error_epis); @@ -2798,18 +2919,6 @@ /* Save the client's DH keying information, if some was provided. */ if (client_dh_key != NULL) { if (auth_pack->client_public_value.len > 0) { - if ((auth_pack->client_public_value.len * 8) < - minimum_dh_prime_size) { - pkinit_debug(mcontext, 1, - "Client's DH prime is less than " - "%d bits in size.\n", - minimum_dh_prime_size); - *e_data = pkinit_create_dh_parameters_edata(mcontext, - pool, - kcontext, - minimum_dh_prime_size); - return KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; - } spki = common_decode_subject_public_key_info(mcontext, pool, &auth_pack->client_public_value); @@ -2820,6 +2929,28 @@ *client_dh_key = spki; pkinit_debug(mcontext, 1, "Saved subject public key info.\n"); + domain_params = common_decode_domain_parameters(mcontext, pool, spki->algorithm.parameters); + if (domain_params != NULL) { + pkinit_debug(mcontext, 2, + "Client's DH prime value " + "is %d bits in size.\n", + domain_params->q.len * 8); + pkinit_debug(mcontext, 2, + "Client's DH public value " + "is %d bits in size.\n", + spki->subject_public_key.len); + if (domain_params->q.len < + minimum_dh_prime_size / 8) { + pkinit_debug(mcontext, 1, + "Client's DH prime " + "is too small.\n"); + *e_data = pkinit_create_dh_parameters_edata(mcontext, + pool, + kcontext, + minimum_dh_prime_size); + return KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; + } + } } } } @@ -3067,6 +3198,12 @@ /* Derive the shared key. */ client_pub = *pub; client_pub.u.dh.publicValue = *client_pubval; +#if DEBUG + dump("KDC pubkey", &pub->u.dh.publicValue); + dump("Parameter Q", &pub->u.dh.prime); + dump("Parameter G", &pub->u.dh.base); + dump("Client pubkey", client_pubval); +#endif sym = PK11_PubDerive(priv, &client_pub, PR_FALSE, NULL, NULL, @@ -3145,8 +3282,8 @@ /* Encode the kdc_dh_key_info. */ memset(&kdc_dh_key_info, 0, sizeof(kdc_dh_key_info)); - encoded_pubval = common_encode_integer(mcontext, pool, - &pub->u.dh.publicValue); + encoded_pubval = common_encode_unsigned_integer(mcontext, pool, + &pub->u.dh.publicValue); if (encoded_pubval == NULL) { pkinit_debug(mcontext, 1, "Error encoding DH public value.\n"); SECKEY_DestroyPublicKey(pub); @@ -3283,7 +3420,7 @@ CERTCertificate *signer, SECItem **additional_certificates, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request) { @@ -3308,7 +3445,7 @@ additional_certificates, NULL, additional_authzdn_list, - cert_flags, + kdc_cert_flags, kcontext, tgt_princ); if (ret != 0) { @@ -3330,11 +3467,11 @@ PK11SlotInfo *preferred_slot, struct pkinit_pwcb_args *pwcb_args, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request, SECItem *envelope, - const SECItem *expected_inner_type, + const SECOidData *expected_inner_type, SECItem **inner_data) { struct signed_data *signed_data; @@ -3374,6 +3511,7 @@ if (SECITEM_ItemsAreEqual(&oid->oid, &enveloped_data->encrypted_content_info.content_type)) { /* Try to parse the data as a content-info structure. */ + pkinit_debug(mcontext, 3, "Enveloped data contains data.\n"); content_info = bcms_decode_content_info(mcontext, pool, data); if (content_info == NULL) { /* Just hope the server's messing with us, and it's @@ -3382,8 +3520,19 @@ content_info = PORT_ArenaZAlloc(pool, sizeof(*content_info)); content_info->content_type = oid->oid; content_info->content = *data; + pkinit_debug(mcontext, 3, + "Enveloped data is not content-info, " + "assuming it's signed-data.\n"); } } else { + oid = SECOID_FindOID(&enveloped_data->encrypted_content_info.content_type); + if (oid) { + pkinit_debug(mcontext, 3, "Enveloped data is \"%s\".\n", + oid->desc); + } else { + pkinit_debug(mcontext, 3, + "Enveloped data has unknown type.\n"); + } /* Assemble a content info with the encap type and data. */ content_info = PORT_ArenaZAlloc(pool, sizeof(*content_info)); content_info->content_type = enveloped_data->encrypted_content_info.content_type; @@ -3398,11 +3547,6 @@ } if (!SECITEM_ItemsAreEqual(&oid->oid, &content_info->content_type)) { - oid = SECOID_FindOIDByTag(SEC_OID_PKCS7_SIGNED_DATA); - if (oid == NULL) { - pkinit_debug(mcontext, 1, "Internal error.\n"); - return KRB5KRB_ERR_GENERIC; - } oid = SECOID_FindOID(&content_info->content_type); if (oid != NULL) { pkinit_debug(mcontext, 1, @@ -3417,9 +3561,21 @@ return KRB5KRB_ERR_GENERIC; } + /* In case the signed data payload is yet another content_info, try to + * unrap it again. */ + data = &content_info->content; + content_info = bcms_decode_content_info(mcontext, pool, data); + if (content_info != NULL) { + oid = SECOID_FindOIDByTag(SEC_OID_PKCS7_SIGNED_DATA); + if ((oid != NULL) && + SECITEM_ItemsAreEqual(&oid->oid, + &content_info->content_type)) { + data = &content_info->content; + } + } + /* Decode the signed data. */ - signed_data = bcms_decode_signed_data(mcontext, pool, - &content_info->content); + signed_data = bcms_decode_signed_data(mcontext, pool, data); if (signed_data == NULL) { pkinit_debug(mcontext, 1, "Error parsing signed data part of KDC response.\n"); @@ -3439,7 +3595,7 @@ ret = pkinit_validate_kdc_certificate(mcontext, pool, certdb, signer, signed_data->certificates, additional_authzdn_list, - cert_flags, + kdc_cert_flags, kcontext, request); if (ret != 0) { pkinit_debug(mcontext, 1, @@ -3449,10 +3605,10 @@ } /* Check that the signed data is what we're expecting. */ - if (!SECITEM_ItemsAreEqual(expected_inner_type, + if (!SECITEM_ItemsAreEqual(&expected_inner_type->oid, &signed_data->encap_content_info.content_type)) { pkinit_debug(mcontext, 1, - "Signed data is not PKINIT key data.\n"); + "Signed data is not expected \"%s\".\n"); CERT_DestroyCertificate(signer); return KRB5KRB_ERR_GENERIC; } @@ -3471,7 +3627,7 @@ PK11SlotInfo *preferred_slot, struct pkinit_pwcb_args *pwcb_args, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request, krb5_data *padata, @@ -3522,11 +3678,11 @@ preferred_slot, pwcb_args, additional_authzdn_list, - cert_flags, + kdc_cert_flags, kcontext, request, &content_info->content, - &oid->oid, + oid, &rkeydata); if (ret != 0) { return ret; @@ -3550,6 +3706,9 @@ key.enctype = l; key.length = reply_key_pack->reply_key.keyvalue.len; key.contents = reply_key_pack->reply_key.keyvalue.data; + pkinit_debug(mcontext, 2, + "Recovered %d-bit temporary key for use with cipher %d.\n", + key.length * 8, key.enctype); /* Verify the reply_key_pack. */ status = SEC_ASN1DecodeInteger(&reply_key_pack->nonce, &l); @@ -3576,7 +3735,7 @@ PK11SlotInfo *preferred_slot, void *pwcb_args, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request, krb5_data *request_pkt, @@ -3585,7 +3744,7 @@ { SECStatus status; SECOidData *oid; - const SECItem *roid_data; + const SECOidData *roid; SECItem *rkeydata; krb5_checksum checksum; krb5_boolean valid; @@ -3614,18 +3773,18 @@ } /* Extract the reply key data, and validate the KDC. */ - roid_data = cert_get_oid_pkinit_rkey_data(); + roid = cert_get_oid_pkinit_rkey_data_oid(); rkeydata = NULL; ret = pkinit_verify_enc_key_pack_common(mcontext, pool, certdb, preferred_slot, pwcb_args, additional_authzdn_list, - cert_flags, + kdc_cert_flags, kcontext, request, &content_info->content, - roid_data, + roid, &rkeydata); if (ret != 0) { return ret; @@ -3648,6 +3807,9 @@ key->enctype = l; key->length = reply_key_pack->reply_key.keyvalue.len; key->contents = reply_key_pack->reply_key.keyvalue.data; + pkinit_debug(mcontext, 2, + "Recovered %d-bit temporary key for use with cipher %d.\n", + key->length * 8, key->enctype); /* Verify the reply_key_pack. */ status = SEC_ASN1DecodeInteger(&reply_key_pack->as_checksum.cksumtype, @@ -3679,7 +3841,7 @@ pkinit_process_dh_rep_info(struct module_context *mcontext, PLArenaPool *pool, CERTCertDBHandle *certdb, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request, struct pa_pk_as_rep *rep, @@ -3749,7 +3911,7 @@ ret = pkinit_validate_kdc_certificate(mcontext, pool, certdb, kdc_cert, signed_dh_data->certificates, additional_authzdn_list, - cert_flags, + kdc_cert_flags, kcontext, request); if (ret != 0) { pkinit_debug(mcontext, 1, @@ -3794,6 +3956,12 @@ kdc_nonce = rep->pa_pk_as_rep_value.dh_info.server_dh_nonce; server_dh_pubkey = *client_dh_pubkey; server_dh_pubkey.u.dh.publicValue = *kdc_pubkey; +#if DEBUG + dump("KDC pubkey", kdc_pubkey); + dump("Parameter Q", &client_dh_pubkey->u.dh.prime); + dump("Parameter G", &client_dh_pubkey->u.dh.base); + dump("Client pubkey", &client_dh_pubkey->u.dh.publicValue); +#endif sym = PK11_PubDerive(client_dh_privkey, &server_dh_pubkey, PR_TRUE, @@ -3830,7 +3998,7 @@ /* Convert the secret to a key. */ if (pkinit_octet_string_to_aeskey(mcontext, kcontext, SEC_OID_SHA1, shared_secret, - kdc_nonce ? client_dh_nonce : NULL, + kdc_nonce ? client_dh_nonce : NULL, kdc_nonce, ENCTYPE_AES256_CTS_HMAC_SHA1_96, key) != SECSuccess) { @@ -3855,7 +4023,7 @@ PK11SlotInfo *preferred_slot, struct pkinit_pwcb_args *pwcb_args, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request, krb5_data *request_pkt, @@ -3888,7 +4056,7 @@ preferred_slot, pwcb_args, additional_authzdn_list, - cert_flags, + kdc_cert_flags, kcontext, request, request_pkt, @@ -3902,7 +4070,7 @@ ret = pkinit_process_dh_rep_info(mcontext, pool, certdb, additional_authzdn_list, - cert_flags, + kdc_cert_flags, kcontext, request, rep, Index: src/pkinitt.h =================================================================== RCS file: /usr/local/CVS/pkinit-nss/src/pkinitt.h,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- src/pkinitt.h 5 Feb 2007 16:32:24 -0000 1.17 +++ src/pkinitt.h 23 Apr 2007 21:50:26 -0000 1.18 @@ -22,7 +22,7 @@ #ifndef pkinitt_h #define pkinitt_h -#ident "$Id: pkinitt.h,v 1.17 2007/02/05 16:32:24 nalin Exp $" +#ident "$Id: pkinitt.h,v 1.18 2007/04/23 21:50:26 nalin Exp $" #include <nspr.h> @@ -182,6 +182,10 @@ SECItem ad_type; SECItem ad_data; }; +struct typed_datum { + SECItem td_type; + SECItem td_data; +}; extern const SEC_ASN1Template pkinit_kerberos_principal_name_template[]; @@ -221,6 +225,12 @@ SECItem *pkinit_encode_authorization_datum(struct module_context *mcontext, PLArenaPool *pool, struct authorization_datum *adata); +SECItem *pkinit_encode_typed_data(struct module_context *mcontext, + PLArenaPool *pool, + struct typed_datum **adata); +SECItem *pkinit_encode_typed_datum(struct module_context *mcontext, + PLArenaPool *pool, + struct typed_datum *adata); struct auth_pack *pkinit_decode_auth_pack(struct module_context *mcontext, PLArenaPool *pool, SECItem *data); struct draft_auth_pack *pkinit_decode_draft_auth_pack(struct module_context *mcontext, @@ -256,6 +266,12 @@ struct authorization_datum *pkinit_decode_authorization_datum(struct module_context *mcontext, PLArenaPool *pool, SECItem *item); +struct typed_datum **pkinit_decode_typed_data(struct module_context *mcontext, + PLArenaPool *pool, + SECItem *item); +struct typed_datum *pkinit_decode_typed_datum(struct module_context *mcontext, + PLArenaPool *pool, + SECItem *item); krb5_error_code pkinit_build_tgs_name(struct module_context *mcontext, krb5_context kcontext, @@ -303,7 +319,7 @@ SECItem **authorized_certs, int n_authorized_certs, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 client_cert_flags, CERTCertificate **signing_cert, struct subject_public_key_info **client_dh_key, SECItem **nonce, @@ -329,7 +345,7 @@ PK11SlotInfo *preferred_slot, struct pkinit_pwcb_args *pwcb_args, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request, krb5_data *padata, @@ -340,7 +356,7 @@ PK11SlotInfo *preferred_slot, struct pkinit_pwcb_args *pwcb_args, const char **additional_authzdn_list, - PRInt32 cert_flags, + PRInt32 kdc_cert_flags, krb5_context kcontext, krb5_kdc_req *request, krb5_data *request_pkt,
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor